todo-5-tool-call-history.md 3.1 KB

Todo 5 Tool-Call History Plan

Status: done

Result

Implemented by subagent and locally verified.

Commands:

uv run pytest tests/test_debug_runtime.py tests/test_websocket_api.py
uv run pytest

Results:

  • focused runtime/API tests: 7 passed, 1 warning in 0.23s
  • full suite: 10 passed, 1 warning in 0.24s

Evaluation note: the remaining work is cleanup and closeout. The warning is from FastAPI/Starlette TestClient importing the currently installed httpx version and does not block the MVP.

Goal

Make multi-turn tool-call history compatible with strict OpenAI Chat Completions compatible providers.

Problem

After the model emits a tool call, the next Chat Completions request should include:

  1. the assistant message that contains tool_calls;
  2. the corresponding tool reply message.

Current runtime appends assistant content and tool replies, but it does not preserve assistant tool_calls. Some providers reject this because a tool message lacks a preceding assistant tool call.

Scope

This todo covers:

  • Representing assistant tool_calls in ChatMessage.
  • Preserving raw tool call id/name/arguments from ToolCallEvent.
  • Updating DebugRuntime to append assistant tool_calls before tool replies.
  • Updating OpenAI payload serialization to include tool_calls.
  • Tests proving strict provider-compatible history order.

This todo does not cover:

  • New tools.
  • UI changes.
  • External queues.
  • Real network smoke.

Files

  • Modify: src/agent_lab/domain/messages.py
  • Modify: src/agent_lab/application/runtime.py
  • Modify: src/agent_lab/infrastructure/chat_client.py only if serialization needs adjustment.
  • Modify or add tests, preferably in tests/test_debug_runtime.py and/or tests/test_websocket_api.py.
  • Do not modify: docs/plans/todos.md.

Design

Add optional tool_calls to ChatMessage as a list of OpenAI-compatible tool call dicts. Keep content as a string for simplicity; use the aggregated assistant text or "" when a model emits only tool calls.

For each ToolCallEvent, create this message shape:

{
    "id": event.id,
    "type": "function",
    "function": {
        "name": event.name,
        "arguments": event.raw_arguments,
    },
}

At the end of each model turn, append one assistant message with aggregated content and all tool calls from that turn, then append the generated tool replies.

Steps

  1. Write a failing test proving that the second ChatAgent call receives messages in this order:
    • system
    • user
    • assistant with tool_calls
    • tool with matching tool_call_id
  2. Add tool_calls to ChatMessage.
  3. Update DebugRuntime.run() to collect tool calls per model turn and append them to the assistant message before tool replies.
  4. Confirm OpenAICompatibleChatClient serializes tool_calls through model_dump(exclude_none=True); adjust only if needed.
  5. Run:

    uv run pytest tests/test_debug_runtime.py tests/test_websocket_api.py
    uv run pytest
    

Verification

Expected result:

  • Tests prove assistant tool_calls precede tool replies.
  • Full suite passes.