Metadata-Version: 2.4
Name: tessen
Version: 0.1.0
Summary: Drop-in runtime capture for AI agents. Two lines to install. Logs every LLM call (Anthropic, OpenAI, Google Gemini, Cohere, Mistral) to local JSONL. MIT.
Author-email: Tessen <hi@tessen.dev>
License-Expression: MIT
Project-URL: Homepage, https://tessen.dev
Keywords: agent,observability,llm,tracing,anthropic,openai,gemini,cohere,mistral,ai-agent,debugging
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Topic :: Software Development :: Debuggers
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Logging
Classifier: Operating System :: OS Independent
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Typing :: Typed
Requires-Python: >=3.10
Description-Content-Type: text/markdown
License-File: LICENSE
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.40.0; extra == "anthropic"
Provides-Extra: openai
Requires-Dist: openai>=2.36.0; extra == "openai"
Provides-Extra: google
Requires-Dist: google-genai>=1.2.0; extra == "google"
Requires-Dist: google-generativeai>=0.8.6; extra == "google"
Provides-Extra: cohere
Requires-Dist: cohere>=6.1.0; extra == "cohere"
Provides-Extra: mistral
Requires-Dist: mistralai>=1.0.0; extra == "mistral"
Provides-Extra: viewer
Requires-Dist: rich>=13.0; extra == "viewer"
Provides-Extra: all
Requires-Dist: anthropic>=0.40.0; extra == "all"
Requires-Dist: openai>=2.36.0; extra == "all"
Requires-Dist: google-genai>=1.2.0; extra == "all"
Requires-Dist: google-generativeai>=0.8.6; extra == "all"
Requires-Dist: cohere>=6.1.0; extra == "all"
Requires-Dist: mistralai>=1.0.0; extra == "all"
Requires-Dist: rich>=13.0; extra == "all"
Dynamic: license-file

# tessen

**Drop-in runtime capture for AI agents.** Two lines to install. Every LLM call your agent makes — full request, full response with thinking decoded, cache-token usage, call site, timing — written as JSONL to local disk.

```python
import tessen
tessen.init(agent_name="my_agent")
```

That's the entire integration. Drop it before you construct your LLM client. Every call from every supported SDK is intercepted.

## Install

```bash
pip install tessen
# or, if you want the optional log viewer
pip install "tessen[viewer]"
```

The core install has **zero hard dependencies**. Tessen only patches vendor SDKs that are already importable in your process — install whichever you actually use.

## What gets captured

| SDK | what's intercepted |
|---|---|
| Anthropic (`anthropic`) | `messages.create`, `messages.stream`, async equivalents, batches API, and `stream=True` iterator returns (tee'd transparently) |
| OpenAI (`openai`) | `chat.completions.create`, `responses.create`, legacy `completions.create`, sync + async |
| Google Gemini | both `google.generativeai` (legacy) and `google.genai` (current), sync + async, streaming |
| Cohere (`cohere`) | `chat`, `chat_stream`, async |
| Mistral (`mistralai`) | `chat.complete`, `chat.stream`, async |

Streaming responses (both `messages.stream(...)` context managers and `messages.create(..., stream=True)` iterators) are tee'd transparently — your code does not change. The captured event records the assembled final message and chunk count.

## What an event looks like

```jsonc
{
  "event_id": "...",
  "agent_name": "my_agent",
  "ts": 1715630400.123,
  "provider": "anthropic",
  "surface": "create",
  "duration_ms": 842.1,
  "request": { "model": "claude-...", "system": "...", "messages": [...], "tools": [...] },
  "response": { "content": [ {"type": "thinking", ...}, {"type": "tool_use", ...} ],
                "stop_reason": "tool_use",
                "usage": { "input_tokens": 512, "output_tokens": 128,
                           "cache_read_input_tokens": 256,
                           "cache_creation_input_tokens": 64 } },
  "call_site": { "file": "/path/to/your/agent.py", "line": 84, "func": "step" },
  "status": "ok"
}
```

## Why

You cannot debug an agent you cannot see. Most observability tools capture the model call as a span on a metrics pipeline. Tessen captures the call as a **forensic record** — thinking deltas, tool blocks, stop reasons, cache hits, retries — at the depth needed to actually investigate why an agent failed.

- **request**: model, system, full `messages`, full `tools` schema, `max_tokens`, `thinking` config, sampling params
- **response**: every content block (`text`, `thinking`, `tool_use`, `tool_result`), `stop_reason`, `stop_sequence`, message `id`, model returned
- **usage**: `input_tokens`, `output_tokens`, `cache_read_input_tokens`, `cache_creation_input_tokens` — so you can see what the model actually paid for
- **call_site**: file path + line number + function name of the caller, with `/tessen/` and vendor-SDK frames skipped so you land on **your** code
- **timing**: `duration_ms` wall-clock
- **errors**: exception type, message, and full traceback if the call raised
- **streaming**: `streamed: true` + `chunks_captured` + assembled final message

## Where logs live

`~/.tessen/logs/{agent_name}/{YYYY-MM-DD}.jsonl`. Override with `tessen.init(log_dir=...)`. Files rotate at 100 MB.

```bash
python -m tessen.viewer ~/.tessen/logs/my_agent/2026-05-13.jsonl
```

## Overhead

The wrapper is a thin function call around the original SDK method. The write goes through a thread-locked append + `fsync()`. Real-world overhead is well under 1 ms per call.

## Frameworks

Tessen patches at the resource-class level (`anthropic.resources.messages.Messages.create`, etc.), so framework wrappers — LangChain `ChatAnthropic`, LangGraph nodes, deepagents, OpenAI Agents SDK with an Anthropic adapter — all flow through Tessen automatically.

## Privacy

The SDK runs in your process. There is no network call from Tessen. You bring your own API keys; we never see them. **Your code, your data, your machine.**

## License

MIT. Vendor it, ship it, modify it.

---

[tessen.dev](https://tessen.dev) · [hi@tessen.dev](mailto:hi@tessen.dev)
