Open-source trajectory logging for LLM agents. Two lines of code to structured, replayable records of every agent run.
Project description
sentric
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 sentric
OpenAI
from sentric 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 sentric 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()
Testing
pip install sentric[dev]
pytest tests/ -v
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
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file sentric-0.1.1.tar.gz.
File metadata
- Download URL: sentric-0.1.1.tar.gz
- Upload date:
- Size: 10.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
17a6c004e0f8eabeb4ac54ac4e27bebe4a58ea7d85ac036669de4e222a6770af
|
|
| MD5 |
19b45d9217426909b692ced53ee8fda3
|
|
| BLAKE2b-256 |
f3d5285b776337b281890f1715ece848db254a631c11d456980ea295122732d9
|
File details
Details for the file sentric-0.1.1-py3-none-any.whl.
File metadata
- Download URL: sentric-0.1.1-py3-none-any.whl
- Upload date:
- Size: 7.9 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: twine/6.2.0 CPython/3.14.2
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
4a47e7d16527044a643fbd35ccc7ec603c703c9778e43d06f3d043732df65cc3
|
|
| MD5 |
52c25afc749d7b5797ccd51880ac2420
|
|
| BLAKE2b-256 |
77f70b08db8d61611a07cb5094c80e495693ea63b5e2ed7b6667715f4c0f46dd
|