AI context observability. See what your LLM saw.
Project description
SeeCtx
AI context observability. See what your LLM saw.
SeeCtx captures a ContextPack for every LLM call your agent makes — what context went in, what got excluded and why, which model was used, what came out, and what it cost. One line of code. Zero config.
pip install seectx[openai,server]
from seectx import wrap, serve
from openai import OpenAI
client = wrap(OpenAI())
# Use the client exactly as before — every call is now captured
response = client.chat.completions.create(
model="gpt-4o",
messages=[
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "What is context engineering?"},
]
)
serve() # Open http://localhost:4100
For short scripts, call flush() before the process exits, or use an explicit
run session so SeeCtx can close the run cleanly:
from seectx import flush, start_run, wrap
from openai import OpenAI
with start_run(app_name="support-agent"):
client = wrap(OpenAI())
client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": "Summarize this ticket"}],
)
# Optional outside a context manager; useful in notebooks and scripts.
flush()
Why
You're building AI agents. They call LLMs dozens of times per run. Each call gets a different context window — different system prompts, retrieved documents, tool results, conversation history. When something goes wrong, you're staring at logs trying to reconstruct what the model actually saw.
SeeCtx records the full context assembly for every step. Not just the input/output — the decisions: what sections were included, what was rejected (and why), how the token budget was spent, and how context drifted step over step.
Integrations
OpenAI
from seectx import wrap
from openai import OpenAI
client = wrap(OpenAI())
# All chat.completions.create() calls are now captured
Anthropic
from seectx import wrap
import anthropic
client = wrap(anthropic.Anthropic())
# All messages.create() calls are now captured
LangGraph
from seectx import observe
graph = observe(build_my_agent())
result = graph.invoke({"input": "resolve this case"})
# Every LLM call in every node — captured with delegation links
Transparent Proxy (Zero Code Changes)
Intercept any LLM traffic without modifying your application. The proxy auto-detects Anthropic, OpenAI, and Ollama request formats on the same port.
# Start proxy + viewer in one command
seectx proxy --provider ollama --port 4050 --viewer-port 4100
Or from Python:
from seectx import proxy
proxy(provider="ollama", proxy_port=4050, viewer_port=4100, working_dir=".")
Point your client at the proxy instead of the real API:
from openai import OpenAI
# Instead of connecting to Ollama directly, go through SeeCtx
client = OpenAI(base_url="http://localhost:4050/v1", api_key="unused")
response = client.chat.completions.create(
model="llama3.2",
messages=[{"role": "user", "content": "Hello"}]
)
Works with any provider:
| Provider | Proxy command | Client base URL |
|---|---|---|
| Ollama | seectx proxy --provider ollama |
http://localhost:4050/v1 |
| OpenAI | seectx proxy --provider openai |
http://localhost:4050/v1 |
| Anthropic | seectx proxy --provider anthropic |
http://localhost:4050 |
Every API call appears in the viewer at http://localhost:4100 in real time.
What Gets Captured
Each ContextPack records:
| Layer | What you see |
|---|---|
| Sections | Every piece of context — system prompt, user message, tool results, retrieval chunks, memory, instructions. 8 section types. |
| Token Budget | How many tokens each section used. What got rejected and why (budget exceeded, relevance filtered, TTL expired, duplicate, policy blocked, dependency missing, manual exclusion). |
| Model | Provider, model name, temperature, max_tokens, top_p, stop sequences. |
| Output | Generated text, tool calls with arguments, stop reason. |
| Metrics | Latency, input/output tokens, cache hits, estimated cost (40+ models priced). |
| Multi-Agent | Parent run, parent step, delegation scope, inherited sections — full lineage across agent handoffs. |
Features
Context Assembler
Budget-aware context builder with automatic rejection tracking:
from seectx import ContextAssembler
assembler = ContextAssembler(budget=4000)
assembler.add(system_prompt_section)
assembler.add(retrieval_section) # auto-rejected if budget exceeded
budget = assembler.build_budget() # includes rejection ledger
Redaction Policies
Ship ContextPacks without leaking sensitive data:
from seectx import set_redaction_policy, RedactionPolicy
set_redaction_policy(RedactionPolicy.HASH) # SHA-256 all content
set_redaction_policy(RedactionPolicy.TRUNCATE) # First 100 chars only
set_redaction_policy(RedactionPolicy.DROP_CONTENT) # Structure without content
Stored ContextPacks include a redaction_policy field so the viewer and audit exports can tell whether raw content, hashes, truncated content, dropped content, or a custom redactor was used.
Local Deletion
SeeCtx stores captures locally by default. Delete one run or purge a storage directory from the CLI:
seectx rm <run_id> --dir .seectx
seectx purge --dir .seectx
Context Drift Detection
Track how context evolves across steps:
from seectx import diff_context_packs
diff = diff_context_packs(step_1_pack, step_2_pack)
# Shows added, removed, and modified sections between steps
Source Discovery
SeeCtx scans your working directory to build a Source Inventory — everything that was available to the context assembly process, not just what made it into the LLM call.
from seectx import discover_sources
inventory = discover_sources(working_dir=".")
print(f"{inventory.total_sources} sources, {inventory.total_tokens_available:,} tokens available")
Automatically detects 15+ context source patterns:
| Pattern | Type |
|---|---|
SKILL.md |
Skill (Claude) |
CLAUDE.md, .claude/settings.json |
Agent config |
.cursorrules, .cursor/rules/ |
Agent config (Cursor) |
.clinerules, .windsurfrules |
Agent config |
.github/copilot-instructions.md |
Agent config (Copilot) |
.mcp.json, mcp.json |
Tool definition (MCP) |
system_prompt.txt, system_prompt.md |
Agent config |
.env, .env.local |
Environment |
.py, .ts, .go, etc. |
Code files |
.md, .txt, .rst |
Documentation |
Respects .seectxignore for excluding paths. Hashes content (SHA-256) for change detection across steps.
Each source tracks a status: included (sent to LLM), considered (evaluated but not sent), available (on disk), excluded (filtered out), or stale (outdated version used).
This is the pre-assembly layer — SeeCtx's two-layer context model:
- Layer 1: Source Inventory — What was available to the context builder
- Layer 2: Wire Context — What was actually sent to the LLM
The gap between these two layers is where hallucinations, stale context, and assembly blind spots live.
Cost Estimation
Built-in pricing for 40+ models:
from seectx import estimate_cost
cost = estimate_cost("gpt-4o", input_tokens=1500, output_tokens=500)
Conformance Levels
Three levels of observability depth:
- L1 Capture — Basic fields: sections, model, output, metrics
- L2 Budget — Rejection ledger populated with exclusion reasons
- L3 Multi-Agent — Delegation links across agent handoffs
ContextPack Format
ContextPack is an open JSON format. Here's what one looks like:
{
"format_version": "0.1.0",
"run_id": "run_abc123",
"step_id": "step_0",
"step_index": 0,
"sections": [
{
"type": "system_prompt",
"section_id": "sec_001",
"source": "system",
"token_count": 150,
"content": "You are a customer support agent..."
},
{
"type": "retrieval",
"section_id": "sec_002",
"source": "knowledge_base",
"token_count": 800,
"content": "Order #12345 was shipped on..."
}
],
"token_budget": {
"total_budget": 4096,
"total_used": 950,
"rejected": [
{
"section_id": "sec_003",
"section_type": "retrieval",
"token_count": 3500,
"reason": "budget_exceeded"
}
]
},
"model": {
"provider": "openai",
"model": "gpt-4o",
"temperature": 0.7
},
"output": {
"type": "text",
"text": "I can see your order #12345 was shipped...",
"stop_reason": "stop"
},
"metrics": {
"latency_ms": 1250,
"input_tokens": 950,
"output_tokens": 85,
"cost_estimate_usd": 0.0034
}
}
The full spec is in spec/CONTEXTPACK_SPEC.md with a JSON Schema at spec/context-pack.schema.json.
Installation
# Just OpenAI
pip install seectx[openai]
# Just Anthropic
pip install seectx[anthropic]
# LangGraph agents
pip install seectx[langgraph]
# With the viewer UI
pip install seectx[openai,server]
# Everything
pip install seectx[all]
Requires Python 3.10+.
Viewer
seectx serve
# or
python -c "from seectx import serve; serve()"
Opens a local UI at http://localhost:4100 showing all captured runs and steps. Three-column layout: run timeline, step detail with tabbed views (Context, Budget, Drift, Sources, Output, Model, Raw JSON), and a metadata sidebar.
Viewer Tabs
| Tab | What it shows |
|---|---|
| Context | Every section in the context window, color-coded by type (system prompt, user message, tool result, retrieval, memory, instruction) |
| Budget | Token allocation per section, rejected candidates with rejection reasons |
| Drift | Visual diff between consecutive steps — what was added, removed, or modified |
| Sources | Pre-assembly inventory: all upstream sources, inclusion status, compression ratio, source-level drift with insight metrics |
| Output | Model response including tool calls with structured display |
| Model | Provider, model name, temperature, parameters |
| Raw | Full ContextPack JSON |
Real-Time Testing
With Ollama
Terminal 1 — Start proxy + viewer:
cd your-project/
pip install seectx[all] openai
python -c "from seectx import proxy; proxy(provider='ollama', proxy_port=4050, viewer_port=4100, working_dir='.')"
Terminal 2 — Chat through the proxy:
python -c "from openai import OpenAI;client=OpenAI(base_url='http://localhost:4050/v1',api_key='unused');msgs=[];exec(\"while True:\n q=input('You: ')\n if q.lower()=='quit':break\n msgs.append({'role':'user','content':q})\n r=client.chat.completions.create(model='llama3.2',messages=msgs)\n print(f'\\n{r.choices[0].message.content}\\n')\n msgs.append({'role':'assistant','content':r.choices[0].message.content})\")"
Open http://localhost:4100 — every message appears live.
The proxy and viewer bind to 127.0.0.1 by default so captured context stays local. Use --host 0.0.0.0 only when you intentionally want to expose them to a container or trusted LAN.
With Anthropic (Claude)
# Terminal 1
python -c "from seectx import proxy; proxy(provider='anthropic', proxy_port=4050, viewer_port=4100, working_dir='.')"
# Terminal 2
export ANTHROPIC_API_KEY=your-key
python -c "from anthropic import Anthropic;c=Anthropic(base_url='http://localhost:4050');msgs=[];exec(\"while True:\n q=input('You: ')\n if q=='quit':break\n msgs.append({'role':'user','content':q})\n r=c.messages.create(model='claude-sonnet-4-20250514',max_tokens=1024,messages=msgs)\n print(f'\\n{r.content[0].text}\\n')\n msgs.append({'role':'assistant','content':r.content[0].text})\")"
With OpenAI
# Terminal 1
python -c "from seectx import proxy; proxy(provider='openai', proxy_port=4050, viewer_port=4100, working_dir='.')"
# Terminal 2
export OPENAI_API_KEY=your-key
python -c "from openai import OpenAI;client=OpenAI(base_url='http://localhost:4050/v1');msgs=[];exec(\"while True:\n q=input('You: ')\n if q=='quit':break\n msgs.append({'role':'user','content':q})\n r=client.chat.completions.create(model='gpt-4o',messages=msgs)\n print(f'\\n{r.choices[0].message.content}\\n')\n msgs.append({'role':'assistant','content':r.choices[0].message.content})\")"
CLI Reference
# Start proxy (captures LLM traffic) + viewer
seectx proxy --provider <ollama|openai|anthropic> [--port 4050] [--viewer-port 4100] [--working-dir .] [--host 127.0.0.1]
# Start viewer only (browse previously captured runs)
seectx serve [--port 4100] [--storage-dir ~/.seectx]
# List captured runs
seectx ls
# Inspect a specific run
seectx inspect <run-id>
Development
git clone https://github.com/arq-ai-labs/seectx.git
cd seectx
pip install -e ".[dev]"
pytest tests/ -v
License
Project details
Release history Release notifications | RSS feed
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 seectx-0.1.0.tar.gz.
File metadata
- Download URL: seectx-0.1.0.tar.gz
- Upload date:
- Size: 134.2 kB
- Tags: Source
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
fc351f6558d0ad11c83245b7d3f2a70ba45f9e3dcf8150f5154f151a1ce3eff9
|
|
| MD5 |
9dde0920e45736ec02a302bc46071a1b
|
|
| BLAKE2b-256 |
f3b58ae05107ba8e0e542ead8d2c51e1200fa1b6f550789a28511415158ccdb2
|
Provenance
The following attestation bundles were made for seectx-0.1.0.tar.gz:
Publisher:
publish.yml on arq-ai-labs/seectx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
seectx-0.1.0.tar.gz -
Subject digest:
fc351f6558d0ad11c83245b7d3f2a70ba45f9e3dcf8150f5154f151a1ce3eff9 - Sigstore transparency entry: 1449498216
- Sigstore integration time:
-
Permalink:
arq-ai-labs/seectx@2a512c3d8f9d25b6afed5b92731fc1e9e56338c1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arq-ai-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2a512c3d8f9d25b6afed5b92731fc1e9e56338c1 -
Trigger Event:
push
-
Statement type:
File details
Details for the file seectx-0.1.0-py3-none-any.whl.
File metadata
- Download URL: seectx-0.1.0-py3-none-any.whl
- Upload date:
- Size: 113.5 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? Yes
- Uploaded via: twine/6.1.0 CPython/3.13.12
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
1edea14b77cdd89c065a2953578a6d5c8fc1d4467043d6abd9393ce0f31097b7
|
|
| MD5 |
28035d5dc18a352019c9c88ae4034a77
|
|
| BLAKE2b-256 |
84168370b144968af36efdc60adc358849d94e91c761c94428bf6b88ce29e646
|
Provenance
The following attestation bundles were made for seectx-0.1.0-py3-none-any.whl:
Publisher:
publish.yml on arq-ai-labs/seectx
-
Statement:
-
Statement type:
https://in-toto.io/Statement/v1 -
Predicate type:
https://docs.pypi.org/attestations/publish/v1 -
Subject name:
seectx-0.1.0-py3-none-any.whl -
Subject digest:
1edea14b77cdd89c065a2953578a6d5c8fc1d4467043d6abd9393ce0f31097b7 - Sigstore transparency entry: 1449498236
- Sigstore integration time:
-
Permalink:
arq-ai-labs/seectx@2a512c3d8f9d25b6afed5b92731fc1e9e56338c1 -
Branch / Tag:
refs/tags/v0.1.0 - Owner: https://github.com/arq-ai-labs
-
Access:
public
-
Token Issuer:
https://token.actions.githubusercontent.com -
Runner Environment:
github-hosted -
Publication workflow:
publish.yml@2a512c3d8f9d25b6afed5b92731fc1e9e56338c1 -
Trigger Event:
push
-
Statement type: