Todo 44 Provider Tool Transcript Plan
Status: in_progress
Goal
Represent and serialize a valid OpenAI-compatible assistant tool-call transcript so a later Runtime todo can continue ChatAgent after native tool execution without weakening the existing dual-agent path.
Scope
- Add structured assistant
tool_calls to the internal ChatMessage model.
- Serialize assistant tool calls and matched
tool replies into provider-compatible request messages.
- Reject orphaned, mismatched, duplicate, or incomplete tool transcripts before network I/O.
- Replace implicit “one tool means forced invocation” behavior with an explicit
tool_choice argument.
- EventAgent explicitly forces its one selected tool; ordinary ChatAgent requests keep automatic/no forced choice behavior.
- Do not enable ChatAgent tools in Runtime in this todo.
Files
- Modify:
src/agent_lab/domain/messages.py
- Modify:
src/agent_lab/infrastructure/chat_client.py
- Modify:
src/agent_lab/application/runtime.py
- Modify:
src/agent_lab/application/event_agent.py
- Modify:
src/agent_lab/application/contracts.py
- Modify:
tests/test_websocket_api.py
- Modify:
tests/test_event_agent.py
- Modify:
tests/test_debug_runtime.py only for protocol signature compatibility if required
- Modify:
docs/plans/todos.md
Plan
Plan
- Define the desired internal transcript and tool-choice API through failing tests.
- Verify failures occur because
ChatMessage lacks assistant tool calls, the client rejects all tool messages, and a single tool is implicitly forced.
- Add the minimum typed message fields and transcript validation/serialization.
- Add explicit optional
tool_choice to the chat-client protocols and implementation.
- Make EventAgent pass a forced function choice; keep Runtime ChatAgent calls unchanged with
tools=[] and no tool choice.
- Run focused and full regressions.
Act
Follow TDD in small RED/GREEN slices:
- Message shape RED
- Add a test constructing an assistant
ChatMessage with two ToolCallEvent entries and asserting model_dump() preserves IDs, names, parsed arguments, and raw arguments.
- Add validation tests rejecting
tool_calls on non-assistant roles and rejecting a tool role without tool_call_id.
- Provider serialization RED
- Add a MockTransport request test containing:
- assistant content plus two tool calls;
- two matching tool replies;
- a final user or assistant-visible message after replies.
- Assert the provider payload uses standard
assistant.tool_calls[].function.arguments raw JSON and tool.tool_call_id.
- Transcript guard RED
- Add separate tests rejecting before network I/O:
- orphan tool reply;
- mismatched call ID;
- duplicate tool reply;
- a non-tool message while assistant calls remain unresolved.
- Tool choice RED
- Add a test showing one ChatAgent tool with no explicit choice does not add forced
tool_choice.
- Add a test showing an explicit forced function choice is serialized exactly.
- Update EventAgent tests to assert its argument-generation call supplies the selected function choice.
- Run focused tests and confirm expected failures.
- Implement minimal domain validation, transcript serialization, explicit choice plumbing, and EventAgent forcing.
- Re-run focused tests and then the full suite.
Observe
Confirm that:
- the legal transcript matches OpenAI-compatible message ordering;
- visible assistant content can coexist with tool calls;
- invalid histories fail locally before HTTP calls;
- single-tool ChatAgent requests are no longer accidentally forced;
- EventAgent retains deterministic selected-tool invocation;
- Runtime still sends
tools=[] in the existing dual-agent mode.
Update
- Mark only todo 44
done after spec and quality reviews pass.
- If review finds post-commit defects, add
44.1 directly below todo 44 and do not edit the completed row.
- Commit code with the problem and any compatibility risk in the body.
Verification
uv run pytest tests/test_websocket_api.py tests/test_event_agent.py tests/test_debug_runtime.py -q
uv run pytest
git diff --check