Implement cloud-planner-proxy: AI planner routes through Cloud API
Implements all 19 tasks of the cloud-planner-proxy OpenSpec change:
- Cloud API: cloud.planner_config (CloudPlannerConfig, load/build helpers)
reusing runtime.tool_calling_client provider clients (no new dependency
needed -- device-cloud-platform already depends on device-agent-runtime).
- Cloud API: new host-scoped POST /internal/v1/hosts/{host_id}/planner/decide
internal endpoint, reusing existing bearer auth; logs only metadata
(host id, tool name, latency, error class), never prompt/screenshot
content.
- Host Agent: new AI_PLANNER_TRANSPORT config (direct default | cloud) and
host_agent/cloud_planner_client.py::CloudProxyToolCallingClient, a
synchronous ToolCallingClient implementation (structural, not importing
runtime) that calls the new endpoint via its own httpx.Client -- avoids
bridging the async HostAgentClient across the worker-thread boundary
that AIPlanner.plan() runs in (asyncio.to_thread in lease.py).
- Host Agent wiring: create_execution_factories()/_host_agent_planner()
select the cloud-proxy client only when AI_PLANNER_TRANSPORT=cloud;
direct/unset transport is unchanged (still the default).
- Tests: 22 new tests across Cloud API config, the new endpoint, the new
client, and transport-selection wiring; full non-integration suite
(492 tests) passes with no regressions.
- Docs: docs/CLOUD_DEPLOYMENT.md documents the cloud transport, its
trade-offs, and the credential split between Host Agent and Cloud API.
proposal.md/design.md were corrected during implementation to reflect two
findings: no new anthropic/openai dependency is actually needed, and
CloudProxyToolCallingClient uses its own sync httpx.Client rather than a
new HostAgentClient method, per the thread-boundary reasoning above.
This commit is contained in:
@@ -365,8 +365,36 @@ fails on its first step (no silent fallback to the stub planner). Set
|
||||
(e.g. for offline/dev hosts with no provider credentials).
|
||||
|
||||
For OpenAI, set `AI_PLANNER_PROVIDER=openai`, choose the deployed model through
|
||||
`AI_PLANNER_MODEL`, and provide `OPENAI_API_KEY`. Provider credentials belong
|
||||
only on the Host Agent; the Cloud API does not need them.
|
||||
`AI_PLANNER_MODEL`, and provide `OPENAI_API_KEY`. This is the **`direct`
|
||||
transport** (the default): the Host Agent holds provider credentials and
|
||||
calls Anthropic/OpenAI itself.
|
||||
|
||||
### Cloud-proxy transport (`AI_PLANNER_TRANSPORT=cloud`)
|
||||
|
||||
Set `AI_PLANNER_TRANSPORT=cloud` on the Host Agent to instead route every
|
||||
planning decision through the Cloud API's
|
||||
`POST /internal/v1/hosts/{host_id}/planner/decide` endpoint (the same
|
||||
host-scoped bearer credential used for heartbeat/claim/renew/result). In this
|
||||
mode:
|
||||
|
||||
- **Credentials move to the Cloud API.** Configure `AI_PLANNER_PROVIDER`,
|
||||
`AI_PLANNER_MODEL`, `AI_PLANNER_TIMEOUT_SECONDS`, and
|
||||
`ANTHROPIC_API_KEY`/`OPENAI_API_KEY` on the Cloud API process instead of the
|
||||
Host Agent -- edge hosts no longer need provider keys at all.
|
||||
- **Trade-offs to accept before enabling:**
|
||||
- *Latency*: every planning step now makes a round trip to the Cloud API in
|
||||
addition to the LLM provider call.
|
||||
- *Availability coupling*: unlike heartbeat/claim (which tolerate transient
|
||||
Cloud API outages via retry/backoff), a planning step fails immediately if
|
||||
the Cloud API or its configured provider is unreachable -- there is no
|
||||
fallback to the stub planner or to a local direct call.
|
||||
- *Expanded data path*: goal/scene prompts and screenshots now transit the
|
||||
Cloud API. The endpoint logs only metadata (host id, resolved tool name,
|
||||
latency, error class) and never prompt text or screenshot bytes, but the
|
||||
request bodies themselves do cross the network to the control plane.
|
||||
|
||||
`AI_PLANNER_TRANSPORT` unset or `direct` preserves the existing
|
||||
direct-to-provider behavior with no change.
|
||||
|
||||
## Operational Limitations
|
||||
|
||||
|
||||
Reference in New Issue
Block a user