WorkWeek Switch SDK — implement the switch contract for external agent teams
Project description
workweek-switch
Implement the WorkWeek switch contract for external agent teams.
The switch SDK lets your app expose capabilities that the WorkWeek platform can discover and invoke. Register handlers with decorators, self-register on startup, and the platform router handles the rest.
Install
pip install workweek-switch
Quick Start
Provider App (exposes capabilities)
from workweek_switch import WorkWeekSwitch
switch = WorkWeekSwitch(
team_id="your-team-uuid",
team_name="Best Apples Daily",
gateway_url="https://gw.askvai.com",
api_key="wk_rpt_your_key_here",
)
@switch.capability(
"apple_recommendations",
description="Daily recommendations for the best apples to buy",
agent_name="Produce Analyst",
agent_role="Apple Quality Specialist",
)
async def recommend_apples(message: str, context: dict) -> str:
return "Honeycrisp from Washington state — peak season, $2.49/lb."
@switch.capability(
"seasonal_analysis",
description="Seasonal apple availability and pricing trends",
agent_name="Market Analyst",
agent_role="Seasonal Pricing Specialist",
)
async def seasonal(message: str, context: dict) -> str:
return "Fuji apples peak Oct-Dec, prices drop 15% from summer highs."
app = switch.as_fastapi_app()
Consumer App (no capabilities, uses platform agents)
from workweek_switch import WorkWeekSwitch
switch = WorkWeekSwitch(
team_id="your-team-uuid",
team_name="My Consumer App",
gateway_url="https://gw.askvai.com",
api_key="wk_rpt_your_key_here",
)
# No @switch.capability() decorators — pure consumer
# Mount in your existing FastAPI app:
app.include_router(switch.as_fastapi_router())
Self-Registration (v0.2.0+)
Register your app with the platform on startup. Idempotent — safe to call every time your app starts.
from contextlib import asynccontextmanager
from fastapi import FastAPI
from workweek_switch import WorkWeekSwitch
switch = WorkWeekSwitch(
team_id="", # assigned by platform on first registration
team_name="My App",
gateway_url="https://gw.askvai.com",
api_key="wk_rpt_your_key_here",
)
@switch.capability("my_feature", description="Does something useful")
async def my_feature(message: str, context: dict) -> str:
return "result"
@asynccontextmanager
async def lifespan(app: FastAPI):
# Register on startup (non-fatal if platform unreachable)
try:
result = await switch.register(switch_url="https://myapp.com")
print(f"Registered: app_id={result.app_id} team_id={result.team_id}")
if result.api_key:
print(f"API Key (save this): {result.api_key}")
except Exception as e:
print(f"Registration failed (non-fatal): {e}")
yield
app = FastAPI(lifespan=lifespan)
app.include_router(switch.as_fastapi_router())
How It Works
Switch Contract
The SDK exposes three endpoints that the WorkWeek platform expects:
| Endpoint | Method | Purpose |
|---|---|---|
/switch/manifest |
GET | Returns registered capabilities, version hash, health status |
/dispatch |
POST | Routes a request to the matching capability handler |
/health |
GET | Liveness check |
Platform Integration
Your App (SDK) WorkWeek Platform
| |
|-- startup: register() ----------->| Creates App + Team + capabilities
| |
|<--- hourly poll: GET /manifest ---| Router syncs routing tables
| |
|<--- invoke: POST /dispatch -------| User invokes your capability
|--- response --------------------- >|
- Register: On startup, your app calls
register()to create its App + Team in the platform - Manifest polling: The platform router polls
/switch/manifesthourly to detect capability changes - Dispatch: When a user invokes one of your capabilities, the platform sends a POST to
/dispatch
Capability Lifecycle
- Add a capability: Add a
@switch.capability()decorator, deploy. Router picks it up within 1 hour (or immediately onregister()). - Remove a capability: Remove the decorator, deploy. Router drains the capability on next poll — existing requests complete, new ones route elsewhere.
- Update a capability: Change the handler function. No SDK/platform changes needed — dispatch always calls the current handler.
Version-Based Polling
The manifest includes a SHA-256 hash of registered capability names. The router skips processing when the version hasn't changed, keeping sync lightweight even at hundreds of tenants.
API Reference
WorkWeekSwitch
WorkWeekSwitch(
team_id: str, # Team UUID (assigned by platform on first register)
team_name: str, # Human-readable name shown in marketplace
webhook_secret: str = None,# HMAC secret for dispatch signature verification
gateway_url: str = None, # Platform gateway URL (required for register())
api_key: str = None, # API key (required for register())
)
@switch.capability()
@switch.capability(
name: str, # Capability name (must be unique within team)
description: str = "", # Shown in marketplace and manifest
is_primary: bool = True, # Primary handler for this capability
agent_name: str = None, # Display name (defaults to function name)
agent_role: str = None, # Role description (e.g., "Weather Analyst")
)
async def handler(message: str, context: dict) -> str:
...
Handlers can be async or sync. Sync handlers run in a thread pool automatically.
switch.register()
result = await switch.register(
switch_url: str, # Externally-reachable URL where manifest/dispatch live
) -> RegisterResult
Returns:
app_id: int— Platform app IDteam_id: str— Platform team UUIDteam_name: str— Team display nameapi_key: str | None— Full API key (first registration only, save it)api_key_prefix: str | None— Key prefix for identificationcapabilities_registered: list[str]— Capabilities the platform registeredalready_existed: bool— True if app was already registered
switch.build_manifest()
Returns the manifest dict that /switch/manifest serves. Useful for testing.
switch.as_fastapi_app()
Returns a standalone FastAPI application with the switch contract endpoints.
switch.as_fastapi_router()
Returns a FastAPI APIRouter for embedding switch endpoints in an existing app.
HMAC Signature Verification
For production deployments, enable webhook signature verification:
switch = WorkWeekSwitch(
team_id="...",
team_name="...",
webhook_secret="wk_secret_your_secret_here",
)
The platform signs dispatch request bodies with HMAC-SHA256. The SDK verifies the X-Webhook-Signature header automatically and returns 401 if invalid.
Changelog
v0.2.0
- Added
gateway_urlandapi_keyconstructor parameters - Added
register(switch_url)method for platform self-registration - Added
RegisterResultmodel - Added
httpxas a dependency (used for registration HTTP calls)
v0.1.0
- Initial release
- Capability registration via
@switch.capability()decorator - Manifest serving, dispatch routing, health check
- HMAC signature verification
- FastAPI app and router builders
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file workweek_switch-0.2.0.tar.gz.
File metadata
- Download URL: workweek_switch-0.2.0.tar.gz
- Upload date:
- Size: 10.6 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
9ac94e3d780160b4702eab3dd7e89fd1b6783a969b473474e2d92256fa8135f8
|
|
| MD5 |
378f538b07ffafc012c9727848fff18f
|
|
| BLAKE2b-256 |
9d2de28ef77783ecc177449b95f7967cd7f3bf836832ddd1ce0f96a944f9f350
|
File details
Details for the file workweek_switch-0.2.0-py3-none-any.whl.
File metadata
- Download URL: workweek_switch-0.2.0-py3-none-any.whl
- Upload date:
- Size: 9.7 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.10.11
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
c6d208132a329469b17073e434e5d04957dec395be683c7d3fef9325e5186fdc
|
|
| MD5 |
588d2dd8f6a71ddfd5704f5299d6319b
|
|
| BLAKE2b-256 |
19855b8d488c029d04673747c5d648b2c17215415204f28106766f5ea721133d
|