todo-43-typed-provider-tool-calls.md 3.5 KB

Todo 43 Typed Provider Tool Calls Plan

Status: pending

Goal

Preserve native provider tool calls as a distinct stream item so direct ChatAgent tools can retain provider-generated IDs and arguments without changing the existing hidden text-event protocol.

Scope

  • Keep text protocol output as name-only events for dual-agent mode.
  • Represent provider-native tool calls with a separate stream kind.
  • Preserve id, name, parsed arguments, and raw arguments from the provider.
  • Flush buffered provider tool calls when a compatible provider ends with [DONE] but omits the expected tool-call finish reason.
  • Do not change Runtime tool routing or provider transcript serialization in this todo.

Files

  • Modify: src/agent_lab/domain/messages.py
  • Modify: src/agent_lab/infrastructure/openai_compatible.py
  • Modify: tests/test_openai_stream_parser.py
  • Modify: tests/test_debug_runtime.py
  • Modify: docs/plans/todos.md

Plan

Plan

  1. Add failing parser tests for distinct event sources.
  2. Verify the tests fail because provider calls and text events currently share kind="event".
  3. Add explicit StreamItem.text_event(...) and StreamItem.provider_tool_call(...) constructors while keeping the event payload as ToolCallEvent.
  4. Update the text-event parser to emit text_event and the provider tool-call accumulator to emit provider_tool_call.
  5. Make parser flush drain buffered provider calls exactly once when no finish reason was received.
  6. Update existing Runtime regression fixtures to accept only text_event in the current dual-agent path; direct routing remains out of scope.

Act

Follow TDD in this order:

  • Add test_parser_distinguishes_text_events_from_provider_tool_calls asserting:
    • visible text remains message_delta;
    • <agent_events>mock_search</agent_events> produces kind="text_event";
    • provider delta.tool_calls produces kind="provider_tool_call" with intact arguments.
  • Add test_parser_flushes_provider_tool_calls_without_finish_reason using fragmented name/argument chunks followed by flush() and asserting one call is emitted.
  • Add test_parser_does_not_emit_provider_tool_call_twice_after_finish_and_flush to protect normal providers.
  • Run uv run pytest tests/test_openai_stream_parser.py -q and confirm the new tests fail for the missing typed kinds/flush behavior.
  • Implement the minimum domain/parser changes.
  • Run the focused parser tests until green.
  • Run uv run pytest tests/test_debug_runtime.py tests/test_event_agent.py tests/test_openai_stream_parser.py -q to verify the existing dual-agent path still passes.

Observe

Confirm from test output and inspection that:

  • text events remain name-only;
  • provider calls retain raw and parsed arguments;
  • multiple provider calls preserve provider order/index;
  • flush emits only buffered calls that were not already drained;
  • no Runtime direct-tools behavior was added early.

Update

  • Mark only todo 43 as done after focused and regression tests pass.
  • If review finds a defect after the commit, add a new 43.1 row directly below todo 43 instead of modifying any completed historical todo row.
  • Commit with a message explaining that the previous shared event kind discarded the distinction required for direct tools; mention compatibility risk if any provider-specific flush behavior remains.

Verification

  • uv run pytest tests/test_openai_stream_parser.py -q
  • uv run pytest tests/test_debug_runtime.py tests/test_event_agent.py tests/test_openai_stream_parser.py -q
  • git diff --check