Metadata-Version: 2.4
Name: trasee
Version: 0.1.0
Summary: Execution-trace canvas for pydantic-ai agents: zero-instrumentation tool tracing + full per-turn prompts + read-only DAG canvas + JSONL replay
Author: senseone
License: MIT
Project-URL: Homepage, https://github.com/nuass/trasee
Keywords: pydantic-ai,agent,observability,tracing,debugging,llm
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
Classifier: Programming Language :: Python :: 3.11
Classifier: Topic :: Software Development :: Debuggers
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: pydantic-ai>=2.57
Requires-Dist: fastapi>=0.110
Requires-Dist: uvicorn>=0.27
Dynamic: license-file

# trasee — execution-trace canvas for pydantic-ai agents

[中文文档](README.zh.md)

> Component ① (observability) of **SOH — senseone harness**, an agent framework by senseone.

**What exactly happened in this run?** Every full prompt sent to the model on every
turn, every tool call's arguments / return value / latency — all landed as events on a
read-only DAG canvas. Built for development-time debugging and demo acceptance;
one-way code → canvas mapping.

Split of duties with Logfire: Logfire is production monitoring (aggregation, alerting,
search); trasee is a development-time **single-run microscope** (full prompt text,
raw tool inputs/outputs, retry-after-rejection visible frame by frame).

## Install

```bash
pip install -e .          # dist name: trasee, import name: trasee
```

Requires `pydantic-ai>=2.57`, plus `fastapi` / `uvicorn` for serving the canvas.

## Three-line integration

```python
from pydantic_ai import Agent, FunctionToolset
from trasee import RunRegistry, TracingToolset, bind_trace, record_run

tools = FunctionToolset()

@tools.tool
def query_something(ctx, q: str) -> dict:
    """Your business tool — zero instrumentation code."""
    return {...}

agent = Agent(toolsets=[TracingToolset(tools)], output_type=str, ...)

registry = RunRegistry()
trace = registry.create("description of this task")
token = bind_trace(trace)
try:
    await record_run(agent, prompt, model=model, deps=deps, trace=trace)
finally:
    token.var.reset(token)
```

Open the canvas:

```python
from trasee.server import serve
serve(registry, port=8222)          # http://127.0.0.1:8222
```

On the canvas: purple model turns on the left lane (click to see the exact full prompt
sent this turn — LLMs are stateless, everything is re-sent each round), tool nodes on
the right (args / result / latency / errors), a black conclusion block, red retry loops.

## Four-layer API

| Layer | API | What it does |
|---|---|---|
| Core | `RunTrace` / `RunRegistry` / `bind_trace` | Event stream (monotonic seq), SSE-style `new_events(after)` |
| Instrumentation | `TracingToolset(toolset, classifier=, on_result=)` | WrapperToolset subclass; call_tool emits events automatically |
| Capture | `record_run(agent, prompt, trace=, dump_to=)` | Walks the `agent.iter()` node stream + serializes prompts |
| Config | `ToolClassifier({tool: (category, label)}, {category: color})` | Canvas grouping/colors; unregistered tools fall back to defaults |

`on_result(name, result, ctx)` is the business hook point (e.g. collect numbers from
tool returns into a whitelist for a conclusion gate).

**Note**: tools registered via `@agent.tool` live in the Agent's internal toolset and
bypass the wrapper — to make them visible on the canvas, register tools on a separate
`FunctionToolset` and wrap it with `TracingToolset`.
Trace resolution order: explicit `trace=` argument → `bind_trace` → `ctx.deps.trace`.

## JSONL dump & replay

```python
await record_run(..., dump_to="runs/")        # writes runs/{run_id}.jsonl after the run
serve(replay_dir="runs/", port=8222)          # pure replay mode: review runs after shutdown
```

JSONL format: a `_meta` header line + one line per event + a `_final` trailer
(`trasee-jsonl@1`). The run list groups runs by agent; replayed files carry a
"📁 replay" badge.

## Event model (free-form dict, conventional `type`)

`model_start` (auto-incremented round, `prompt` = full messages for the turn) /
`model_end` (tool_calls) / `tool_start` / `tool_end` (ms, result or error) /
`final_answer` / `run_failed`.

## Demo

```bash
python -m trasee.demo 8222        # three-tool refund agent, scripted replay, zero tokens
```

## Current limits (roadmap)

- Read-only canvas: runs cannot be triggered/replayed from the canvas (runs are started by host code)
- tool-return content over 1400 chars is collapsed in the prompt drawer (full content stays on the tool node)
- Single-process in-memory registry; for multi-process/distributed setups, dump JSONL and aggregate for replay

## License

MIT — see [LICENSE](LICENSE).
