Skip to main content

Open-source trajectory logging for LLM agents. Two lines of code to structured, replayable records of every agent run.

Project description

trajectory-collector

Open-source trajectory logging for LLM agents. Two lines of code to get structured, replayable records of every agent run — ready for evaluation and fine-tuning.

No backend. No API key. No vendor lock-in. Just local JSON files.

Quickstart

pip install trajectory-collector

OpenAI

from collector import TrajectoryCollector, trace

collector = TrajectoryCollector(
    task_id="fix-auth-bug",
    domain="code",
    model={"name": "gpt-4o", "version": "2024-08-06", "provider": "openai"},
)

@trace(collector)
def call_llm(messages):
    return openai.chat.completions.create(model="gpt-4o", messages=messages)

# Use call_llm as normal — every call is automatically logged
response = call_llm(messages=[
    {"role": "system", "content": "You are a software engineer."},
    {"role": "user", "content": "Fix the authentication bug in auth.py"},
])

# When the agent finishes, save the trajectory
collector.save_episode()
# -> data/trajectories/f47ac10b-58cc-4372-a567-0e02b2c3d479.json

That's it. The @trace decorator auto-detects the OpenAI response, extracts the assistant message, tool calls, and token counts, and logs everything in a structured format.

Anthropic

@trace(collector)
def call_llm(messages):
    return anthropic.messages.create(model="claude-sonnet-4-20250514", messages=messages, max_tokens=4096)

Same decorator. Auto-detected. Handles text blocks and tool use blocks.

Any other LLM

def my_normalizer(response):
    return [{"role": "assistant", "content": response.text}], response.token_count

@trace(collector, normalizer=my_normalizer)
def call_llm(messages):
    return my_custom_client.generate(messages)

Write a 5-line normalizer function that tells us how to extract fields from your response type. Or skip it entirely — unknown types fall back to stringifying the response.

What gets logged

Each trajectory is a JSON file containing the full conversation:

{
  "episode_id": "f47ac10b-58cc-4372-a567-0e02b2c3d479",
  "task_id": "fix-auth-bug",
  "domain": "code",
  "model": {
    "name": "gpt-4o",
    "version": "2024-08-06",
    "provider": "openai"
  },
  "messages": [
    {"role": "system", "content": "You are a software engineer."},
    {"role": "user", "content": "Fix the authentication bug in auth.py"},
    {
      "role": "assistant",
      "content": "Let me look at the auth module.",
      "tool_calls": [
        {"id": "call_1", "name": "bash", "arguments": "{\"command\": \"cat auth.py\"}"}
      ]
    },
    {"role": "tool", "tool_call_id": "call_1", "content": "class AuthHandler:\n    ..."}
  ],
  "reward": null,
  "success": null,
  "created_at": "2026-04-13T10:30:00Z",
  "duration_ms": 45200,
  "total_tokens": 8430,
  "metadata": {}
}

Reward fields start as null — they get filled in later by a verifier that scores whether the agent succeeded.

Manual logging

If you need more control, use the collector directly:

from collector import TrajectoryCollector

collector = TrajectoryCollector(
    task_id="extract-invoice",
    domain="extraction",
    model={"name": "gpt-4o-mini", "version": "2024-07-18", "provider": "openai"},
)

collector.add_message(role="system", content="Extract fields from this invoice.")
collector.add_message(role="user", content="Invoice #1234...")
collector.add_message(role="assistant", content='{"vendor": "Acme", "amount": 150.00}')
collector.add_tokens(500)
collector.save_episode()

Multiple episodes

Use reset() to reuse a collector across tasks:

for task in tasks:
    collector.reset(task_id=task.id)
    run_agent(task, collector)
    collector.save_episode()

Schema

See collector/SCHEMA.md for the full schema reference, field descriptions, and design decisions.

License

MIT

Project details


Download files

Download the file for your platform. If you're not sure which to choose, learn more about installing packages.

Source Distribution

trajectory_collector-0.1.1.tar.gz (9.0 kB view details)

Uploaded Source

Built Distribution

If you're not sure about the file name format, learn more about wheel file names.

trajectory_collector-0.1.1-py3-none-any.whl (8.8 kB view details)

Uploaded Python 3

File details

Details for the file trajectory_collector-0.1.1.tar.gz.

File metadata

  • Download URL: trajectory_collector-0.1.1.tar.gz
  • Upload date:
  • Size: 9.0 kB
  • Tags: Source
  • Uploaded using Trusted Publishing? No
  • Uploaded via: twine/6.2.0 CPython/3.14.2

File hashes

Hashes for trajectory_collector-0.1.1.tar.gz
Algorithm Hash digest
SHA256 bbdd70d2afe0921b398325eb88136758d564a0858df6c621b3c742955e17e3fb
MD5 7f4525295c1775d2ac9b05e8f3b6f5b1
BLAKE2b-256 d09b604298ce2bbab2676910cb8d77cbfeb9e3309cc486d91f2ad79b81308596

See more details on using hashes here.

File details

Details for the file trajectory_collector-0.1.1-py3-none-any.whl.

File metadata

File hashes

Hashes for trajectory_collector-0.1.1-py3-none-any.whl
Algorithm Hash digest
SHA256 80b60df9bc13ab7490728efff5c351ccc3d9078303a7ebecb02d0f9637965dd3
MD5 bfa32310039d3c1f8e1b26467b1f8228
BLAKE2b-256 34f63f00e74b3b72f9f5a97f626f66febf0f976321a57ec354acb455c47d912e

See more details on using hashes here.

Supported by

AWS Cloud computing and Security Sponsor Datadog Monitoring Depot Continuous Integration Fastly CDN Google Download Analytics Pingdom Monitoring Sentry Error logging StatusPage Status page