todo-44-provider-tool-transcript.md 4.6 KB

Todo 44 Provider Tool Transcript Plan

Status: done

Result

  • Added structured assistant tool calls and provider-compatible tool replies.
  • Added local transcript-state validation and explicit tool-choice behavior.
  • Removed implicit forcing for a single ChatAgent tool while EventAgent explicitly forces its selected function.
  • Todo 44.1 hardened identity, payload precedence, history projection, and public request boundaries.
  • Verified with focused tests (97 passed) and the full suite (108 passed, one existing warning).

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

  1. Define the desired internal transcript and tool-choice API through failing tests.
  2. Verify failures occur because ChatMessage lacks assistant tool calls, the client rejects all tool messages, and a single tool is implicitly forced.
  3. Add the minimum typed message fields and transcript validation/serialization.
  4. Add explicit optional tool_choice to the chat-client protocols and implementation.
  5. Make EventAgent pass a forced function choice; keep Runtime ChatAgent calls unchanged with tools=[] and no tool choice.
  6. Run focused and full regressions.

Act

Follow TDD in small RED/GREEN slices:

  1. 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.
  2. 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.
  3. 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.
  4. 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.
  5. Run focused tests and confirm expected failures.
  6. Implement minimal domain validation, transcript serialization, explicit choice plumbing, and EventAgent forcing.
  7. 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