todo-2-domain-runtime.md 3.2 KB

Todo 2 Domain Runtime Plan

Status: done

Result

Implemented by subagent and locally verified with:

uv run pytest tests/test_openai_stream_parser.py tests/test_event_agent.py tests/test_debug_runtime.py

Result: 3 passed in 0.07s.

Evaluation note: the runtime currently streams message deltas to the caller but does not yet persist the aggregated assistant message into conversation history. Todo 3 should cover transcript persistence when wiring the WebSocket/API surface.

Goal

Implement the core domain/runtime loop that passes the existing Todo 1 tests without adding WebSocket, FastAPI routes, or UI.

Scope

This todo covers:

  • Domain objects for chat messages, tool-call events, stream items, and usage.
  • Application contracts for debug run requests and agent parameters.
  • EventAgent execution for enabled tools.
  • OpenAI Chat Completions compatible stream parsing.
  • DebugRuntime loop that routes ChatAgent tool events through EventAgent and resumes ChatAgent with tool replies.

This todo does not cover:

  • Real HTTP calls to an LLM endpoint.
  • pydantic-settings config.
  • WebSocket protocol.
  • Static frontend.

Files

  • Create: src/agent_lab/domain/events.py
  • Create: src/agent_lab/domain/messages.py
  • Create: src/agent_lab/application/contracts.py
  • Create: src/agent_lab/application/event_agent.py
  • Create: src/agent_lab/application/runtime.py
  • Create: src/agent_lab/infrastructure/openai_compatible.py
  • Do not modify: docs/plans/todos.md
  • Avoid modifying tests unless a test is internally inconsistent.

Design

Use small Pydantic models for contracts that cross application boundaries and dataclasses for internal stream items. Keep tool handling intentionally simple:

  • ToolCallEvent.name is the ChatAgent tool name.
  • EventAgent treats ToolCallEvent.name as the tool identifier.
  • The MVP ships one built-in event tool, handoff_note, that returns a JSON tool reply.
  • Disabled tools return a JSON error tool reply instead of raising.

DebugRuntime.run() is an async generator. It builds initial messages from system prompts, pre-messages, and the user message. It calls chat_client.stream_chat(), yields message deltas and events, sends events to EventAgent, appends the returned tool message, and calls ChatAgent again until no event is produced or max_event_loops is reached.

Steps

  1. Implement domain models in events.py and messages.py.
  2. Implement request/parameter contracts in contracts.py.
  3. Implement EventAgent.handle().
  4. Implement ChatCompletionStreamParser.feed() for OpenAI-compatible streamed chunks:
    • content deltas become StreamItem.message_delta.
    • tool call chunks are accumulated by index.
    • on finish_reason == "tool_calls", emit StreamItem.event for each completed tool call.
    • usage becomes StreamItem.usage.
  5. Implement DebugRuntime.run() around the chat client protocol.
  6. Run uv run pytest tests/test_openai_stream_parser.py tests/test_event_agent.py tests/test_debug_runtime.py.
  7. Fix only Todo 2 scope failures.

Verification

Run:

uv run pytest tests/test_openai_stream_parser.py tests/test_event_agent.py tests/test_debug_runtime.py

Expected result:

  • 3 tests pass.
  • No FastAPI/WebSocket/UI code is required for this todo.