Tests / Test passed: 626
- Host Agent now defaults AI_PLANNER_ENABLED=true (opt-out via env), scoped to apps/device-host-agent/host_agent/execution.py only; the shared runtime.planner_config default (disabled) is unchanged. - Add openspec proposal for cloud-planner-proxy: centralize LLM provider config/credentials on the Cloud Control Plane and let the Host Agent proxy AI Planner decisions through it instead of holding provider API keys locally. Proposal only, no implementation yet.
93 lines
4.8 KiB
Markdown
93 lines
4.8 KiB
Markdown
## 1. Cloud API: planner configuration
|
|
|
|
- [ ] 1.1 Add Cloud API-side planner configuration (provider, model, timeout,
|
|
provider API keys) analogous to `runtime/planner_config.py`, loaded
|
|
from the Cloud API's own process environment (e.g.
|
|
`AI_PLANNER_PROVIDER`/`AI_PLANNER_MODEL`/`AI_PLANNER_TIMEOUT_SECONDS`,
|
|
`ANTHROPIC_API_KEY`/`OPENAI_API_KEY`), living in
|
|
`packages/cloud-platform/cloud` or `apps/cloud-api`.
|
|
- [ ] 1.2 Add `anthropic`/`openai` as explicit dependencies of the package
|
|
that hosts this configuration (confirm whether `packages/cloud-platform`
|
|
or `apps/cloud-api` is the right home, matching where the new endpoint
|
|
handler will live).
|
|
|
|
## 2. Cloud API: planner-decision internal endpoint
|
|
|
|
- [ ] 2.1 Add request/response Pydantic models to `cloud.internal_api.models`
|
|
for a planner-decision call: request carries `system_prompt`,
|
|
`user_prompt`, optional base64 screenshot, tool specs, timeout;
|
|
response carries resolved `tool_name`/`arguments` or a structured
|
|
error.
|
|
- [ ] 2.2 Add the internal route (e.g. `POST /internal/v1/planner/decide`)
|
|
to the existing internal router, reusing the current host-scoped
|
|
bearer auth dependency used by heartbeat/claim/renew/result -- no new
|
|
scope.
|
|
- [ ] 2.3 Implement the route handler by constructing
|
|
`runtime.tool_calling_client.AnthropicToolCallingClient` or
|
|
`OpenAIToolCallingClient` (per D1) from the Cloud API's own planner
|
|
configuration, calling `.decide(...)`, and translating
|
|
`ToolCallDecision`/`ToolCallUnavailable` into the response model.
|
|
- [ ] 2.4 Ensure the handler does not log or persist raw prompt text or
|
|
screenshot bytes; only metadata (host id, resolved tool name,
|
|
latency, error class) may be logged.
|
|
|
|
## 3. Host Agent: cloud-proxy transport
|
|
|
|
- [ ] 3.1 Add `AI_PLANNER_TRANSPORT` (`direct` default | `cloud`) to
|
|
`apps/device-host-agent/host_agent/config.py`.
|
|
- [ ] 3.2 Add a `request_planner_decision(...)` method to
|
|
`host_agent/client.py::HostAgentClient` that calls the new Cloud API
|
|
endpoint using the existing authenticated `httpx` session, importing
|
|
the new request/response models from `cloud.internal_api.models`.
|
|
- [ ] 3.3 Add `host_agent/cloud_planner_client.py::CloudProxyToolCallingClient`
|
|
implementing the `runtime.tool_calling_client.ToolCallingClient`
|
|
Protocol structurally (no import of `host_agent`/`cloud` from
|
|
`runtime`), wrapping `HostAgentClient.request_planner_decision(...)`
|
|
and raising `ToolCallUnavailable` on any failure (network error, auth
|
|
rejection, non-2xx, provider error, timeout) -- matching D7.
|
|
|
|
## 4. Host Agent: wiring
|
|
|
|
- [ ] 4.1 Update `apps/device-host-agent/host_agent/execution.py`'s
|
|
`_host_agent_planner_config()`/`create_task_runner()` so that when
|
|
`AI_PLANNER_TRANSPORT=cloud`, the constructed `TaskRunner`'s
|
|
`AIPlanner` is built with a `CloudProxyToolCallingClient` instead of
|
|
the default local provider client, while leaving the existing
|
|
default-enabled/direct-transport behavior unchanged when
|
|
`AI_PLANNER_TRANSPORT` is unset or `direct`.
|
|
- [ ] 4.2 Confirm `apps/device-host-agent/tests/test_execution.py::test_runtime_owned_packages_do_not_import_host_or_cloud_concerns`-style
|
|
boundary checks still pass with the new module in place.
|
|
|
|
## 5. Tests
|
|
|
|
- [ ] 5.1 Unit tests for Cloud API planner configuration loading
|
|
(defaults, provider/model/timeout parsing) mirroring
|
|
`tests/test_planner_config.py`.
|
|
- [ ] 5.2 Unit tests for the planner-decision endpoint: authorized request
|
|
resolves a decision, unauthenticated/foreign-host request is
|
|
rejected, provider failure returns a structured error without
|
|
crashing.
|
|
- [ ] 5.3 Unit tests for `CloudProxyToolCallingClient`: successful decision
|
|
round-trip, and each failure mode raises `ToolCallUnavailable`.
|
|
- [ ] 5.4 Unit tests for Host Agent wiring: `AI_PLANNER_TRANSPORT=cloud`
|
|
constructs an `AIPlanner` using `CloudProxyToolCallingClient`;
|
|
`AI_PLANNER_TRANSPORT` unset or `direct` preserves existing
|
|
direct-to-provider construction (no regression to the
|
|
already-implemented default-enabled behavior).
|
|
- [ ] 5.5 Full non-integration suite (`uv run --all-packages pytest -m
|
|
"not integration"`) passes with no regressions.
|
|
|
|
## 6. Documentation
|
|
|
|
- [ ] 6.1 Update `docs/CLOUD_DEPLOYMENT.md`'s Runtime AI Planner section
|
|
with the `cloud` transport configuration path, its trade-offs
|
|
(latency, cloud-availability coupling, expanded data path for
|
|
screenshots/prompts), and the credential split (Cloud API holds
|
|
provider keys for `cloud` transport; Host Agent holds them for
|
|
`direct` transport).
|
|
|
|
## 7. Validation
|
|
|
|
- [ ] 7.1 `openspec validate --strict` passes for this change.
|
|
- [ ] 7.2 Ruff check/format and `compileall` pass for all touched packages.
|