# Architecture

## Modules and responsibilities

### registry.py

Source: `../src/openai_resptools/registry.py`

Responsibilities:
- Store a mapping `{tool_name -> ToolDef}`
- Provide a `@tool()` decorator to register functions
- Optionally auto-register public methods on an object (registry subclass pattern)
- Build OpenAI tool payloads via `as_openai_tools()`
- Build `tool_choice` payload via `allowed_tools_choice(...)`

### tool.py

Source: `../src/openai_resptools/tool.py`

Responsibilities:
- Define `ToolDef` (dataclass)
- Build tool definition from a callable (`ToolDef.from_callable`)
  - Build args model + parameter schema (via `schema.py`)
  - Extract description from docstring when not provided
- Validate tool args with Pydantic
- Execute tools and normalize failures into `ToolExecutionError`
- Serialize outputs to JSON string for `function_call_output`

### schema.py

Source: `../src/openai_resptools/schema.py`

Responsibilities:
- Build a Pydantic model from a callable signature/type hints
- Convert that model JSON schema into an OpenAI tool `parameters` schema
- Implement strict/non-strict schema behaviors

### runner.py

Source: `../src/openai_resptools/runner.py`

Responsibilities:
- Execute the tool-calling loop for the Responses API
- Convert SDK output items into plain dicts so they can be appended back into `input`
- Parse `function_call.arguments` JSON and validate it’s an object
- Lookup tool by name and execute it
- Append `function_call_output` items with matching `call_id`
- Stop when the model produces no tool calls, returning `output_text`

Event hooks:
- `on_event(event_name: str, payload: dict)` is called for:
  - `llm.request` (iteration)
  - `llm.response` (iteration, tool_calls count)
  - `tool.call` (call_id, name, arguments)
  - `tool.output` (call_id, name, output)

### errors.py

Source: `../src/openai_resptools/errors.py`

Responsibilities:
- Define package error hierarchy used by registry/tool/runner

### types.py

Source: `../src/openai_resptools/types.py`

Responsibilities:
- Shared typing helpers (InputItem/InputList)
- `RunnerResult` dataclass
- TypedDict helpers for function call items (best-effort)

## High-level flow

1. You build a `ToolRegistry` and register tools.\n
2. You create a `ToolRunner` with an OpenAI client, model name, and registry.\n
3. `ToolRunner.run(...)` calls the Responses API with `tools` and `input`.\n
4. If the model requests tools, the runner executes them and appends `function_call_output` items.\n
5. The loop continues until the model returns no `function_call` items.\n

