# Todo 5 Tool-Call History Plan **Status:** done ## Result Implemented by subagent and locally verified. Commands: ```bash 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: ```python { "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: ```bash 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.