V3 Issue #229 — Native Provider Tools Implementation Plan
Issue-specific plan for #229, covering R003 and R005. Start from verified
mainbe3d035; finish and review before #230.
Goal: Add schema-backed native tool requests and lossless tool-result history across every shipped provider adapter, without textual control parsing or adapter-side execution.
Architecture: Keep shared call/reply/history validation in openkyrozen/providers/models.py; adapter serialization in each provider module; registry selection in the existing runtime provider boundary in openkyrozen/providers/calls.py. Preserve fallback, deadlines, retry budgets, usage accounting, and existing text APIs. Continuation state stays SDK-independent and bound to the provider/model that produced it.
Tech Stack: Python 3.12–3.13, existing provider SDKs, standard library, unittest, local HTTP/SDK-shape fixtures.
Constraints and compatibility
- No new dependencies, paid calls, executable transport of provider SDK objects, or automatic tool execution inside adapters.
- Keep legacy calls unchanged when
tools=None; reject unsupported schemas or malformed history before transport. - Provider-visible tools are canonical registry names filtered by the runtime's effective capability grant.
- Once a native call has been returned, follow-up requests remain pinned to its responding provider and request model.
- Keep opaque provider continuation separate from text, normal metadata, receipts, logs, and exception diagnostics.
- Preserve existing fallback model mapping and exactly-once usage accounting for the initial request.
Review focus
- Every adapter preserves the provider's native assistant/tool result shape and call order, including reasoning/signature blocks needed for replay.
- History validation rejects duplicate, orphaned, missing, mismatched, and non-contiguous replies before making a request.
- Tool schemas are not weakened by adapter conversion; strict-mode defaults do not change optional-field semantics.
- Fallback cannot move a continuation to a different provider or execute/replay a tool call.
- Runtime authority filters declarations; providers receive no executor references.
Task 1: Shared conversation and continuation contracts
Files: openkyrozen/providers/models.py, openkyrozen/providers/__init__.py, tests/test_provider_contracts.py
- [x] Add immutable
ToolReplyandProviderContinuationvalues with defensive JSON snapshots and validated identity fields. - [x] Extend
ModelResponsewith an optional continuation while excluding it from ordinary metadata and display representations. - [x] Validate message/response/reply history for unique IDs, exact one-to-one matching, complete adjacent reply groups, and correct call order.
- [x] Add regressions for valid multi-call history and every malformed grouping; run them red before implementation.
Task 2: Provider API, fallback pinning, and runtime catalog selection
Files: openkyrozen/providers/base.py, openkyrozen/providers/fallback.py, openkyrozen/providers/calls.py, runtime provider tests
- [x] Add keyword-only
tools=Noneto structured request helpers and the base contract; keep legacy-only providers usable with no tools. - [x] Pass only registry-derived schemas for requested canonical names allowed by effective runtime capabilities.
- [x] Bind continuation to the responding adapter instance and requested model; reject cross-adapter/model continuation and do not fall back after native execution starts.
- [x] Test default fallback mapping, continuation pinning, usage accounting once, and no execution inside providers.
Task 3: Native serialization for provider adapter families
Files: openkyrozen/providers/openai.py, azure.py, anthropic.py, google.py, bedrock.py, ollama.py, perplexity.py, adapter fixtures
- [x] Implement declarations, native assistant continuation, and correlated result serialization for OpenAI-compatible/Azure, Responses, Anthropic, Google/Vertex, Bedrock, Ollama, and Perplexity.
- [x] Preserve raw provider continuation payloads as JSON snapshots; reconstruct provider-native request forms without flattening tool-only history into prose.
- [x] Keep schema semantics; explicitly use non-strict behavior where strict conversion would make optional fields required; fail closed on unrepresentable schemas.
- [x] Test text-only, tool-only, mixed, multiple calls, failed results, IDs, ordering, usage, optional schema fields, unsupported Google schema keywords, and Gemini thought-signature replay.
Task 4: Documentation, regression review, and delivery
Files: docs/providers.md, provider contract tests, related issue #225 regression
- [x] Document truthful adapter capability behavior, continuation pinning, and the boundary with later AgentEngine/history work.
- [x] Independently review the completed diff; no verified correctness/security defect was found. Added regressions for the noted schema/signature coverage gaps.
- [ ] Run focused provider/runtime tests on Python 3.12 and 3.13, then full Python tests,
make check,make lint,make docs-check, Go TUI tests, and offline agent/subagent/TUI acceptance. Python 3.12 focused tests and repository checks passed; Python 3.13 dependencies are unavailable. The full suite has sandbox filesystem and network failures; agent, subagent, and TUI acceptance passed. - [x] Verify the #225 request-shape regression independently without claiming the broader issue is resolved.
- [ ] Sign commits with Evan's GPG key, verify locally and on GitHub, publish one PR against current
main, attach it to this chat, and wait for final-head CI/reviews. - [x] Leave the PR unmerged.