# AgentLens Python SDK [![PyPI version](https://img.shields.io/pypi/v/vectry-agentlens)](https://pypi.org/project/vectry-agentlens/) [![Python 3.9+](https://img.shields.io/badge/python-3.9%2B-blue)](https://www.python.org/downloads/) [![License: MIT](https://img.shields.io/badge/License-MIT-green.svg)](https://opensource.org/licenses/MIT) Agent observability that traces **decisions**, not just API calls. ## What is AgentLens? AgentLens is an observability SDK for AI agents. Unlike generic LLM tracing tools that only capture request/response pairs, AgentLens captures the **decision points** your agent makes: which tool it chose, what alternatives it considered, and why. This gives you a decision tree view of agent behavior, not just a flat log of API calls. ## Quick Start ```bash pip install vectry-agentlens ``` ```python import agentlens # Initialize once at startup agentlens.init(api_key="your-api-key") # Trace any function with a decorator @agentlens.trace(name="research-agent", tags=["research"]) def research(topic: str) -> str: # Your agent logic here return f"Results for: {topic}" research("quantum computing") ``` The `@trace` decorator works with both sync and async functions. Traces are batched and sent to the AgentLens API automatically. You can also use `trace` as a context manager: ```python with agentlens.trace(name="my-operation", session_id="user-123"): # Everything inside is traced result = do_work() ``` ## Integrations ### OpenAI Auto-capture every `chat.completions.create` call with token counts, cost estimation, and tool-call decisions. ```bash pip install vectry-agentlens[openai] ``` ```python import openai import agentlens from agentlens.integrations.openai import wrap_openai agentlens.init(api_key="your-api-key") client = openai.OpenAI() client = wrap_openai(client) # All calls are now traced automatically @agentlens.trace(name="assistant") def ask(question: str) -> str: response = client.chat.completions.create( model="gpt-4o", messages=[{"role": "user", "content": question}], ) return response.choices[0].message.content ask("What is the capital of France?") ``` `wrap_openai` instruments the client in-place. For each call it creates an LLM span with: - Model name, temperature, and max_tokens - Token usage (prompt, completion, total) - Estimated cost in USD (built-in pricing for GPT-4, GPT-4o, GPT-3.5-turbo variants) - Automatic `TOOL_SELECTION` decision points when the model invokes function/tool calls Streaming is also supported transparently. ### LangChain Drop in a callback handler to trace chains, agents, LLM calls, and tool invocations. ```bash pip install vectry-agentlens[langchain] ``` ```python import agentlens from agentlens.integrations.langchain import AgentLensCallbackHandler agentlens.init(api_key="your-api-key") handler = AgentLensCallbackHandler( trace_name="langchain-agent", tags=["production"], session_id="user-456", ) # Pass the handler to any LangChain chain or agent result = chain.invoke( {"input": "Summarize this document"}, config={"callbacks": [handler]}, ) ``` The handler automatically creates spans for LLM calls, tool calls, and chain execution. Agent tool selections are logged as `TOOL_SELECTION` decision points. It also works inside an existing trace context: ```python @agentlens.trace(name="my-pipeline") def run_pipeline(query: str): handler = AgentLensCallbackHandler() return chain.invoke({"input": query}, config={"callbacks": [handler]}) ``` ### Custom Agents For any agent framework (or your own), use `log_decision()` to record decision points manually. ```python import agentlens from agentlens import log_decision agentlens.init(api_key="your-api-key") @agentlens.trace(name="routing-agent") def route_request(query: str) -> str: # Your routing logic chosen_agent = "research-agent" log_decision( type="ROUTING", chosen={"name": chosen_agent, "confidence": 0.92}, alternatives=[ {"name": "support-agent", "confidence": 0.45, "reason_rejected": "not a support query"}, {"name": "sales-agent", "confidence": 0.12, "reason_rejected": "no purchase intent"}, ], reasoning="Query contains research keywords", context_snapshot={"tokens_used": 1200, "tokens_available": 6800}, cost_usd=0.003, duration_ms=45, ) return dispatch(chosen_agent, query) ``` Decision types include: `TOOL_SELECTION`, `ROUTING`, `RETRY`, `ESCALATION`, `MEMORY_RETRIEVAL`, `PLANNING`, and `CUSTOM`. ## API Reference ### `agentlens.init()` Initialize the SDK. Call once at application startup. ```python agentlens.init( api_key="your-api-key", # Required. Your AgentLens API key. endpoint="https://...", # API endpoint (default: https://agentlens.vectry.tech) flush_interval=5.0, # Seconds between batch flushes (default: 5.0) max_batch_size=10, # Traces per batch before auto-flush (default: 10) enabled=True, # Set False to disable sending (e.g., in tests) ) ``` ### `agentlens.trace()` Decorator or context manager that creates a trace (or a nested span if already inside a trace). ```python # As decorator @agentlens.trace(name="my-agent", tags=["v2"], session_id="user-789", metadata={"env": "prod"}) def my_agent(): ... # As context manager with agentlens.trace(name="sub-task") as ctx: ... ``` | Parameter | Type | Description | |-------------|------------------|--------------------------------------| | `name` | `str` | Name for the trace or span | | `tags` | `list[str]` | Tags for filtering in the dashboard | | `session_id`| `str` | Group traces by user session | | `metadata` | `dict` | Arbitrary key-value metadata | ### `agentlens.log_decision()` Record a decision point inside an active trace. ```python agentlens.log_decision( type="TOOL_SELECTION", chosen={"name": "search", "confidence": 0.95}, alternatives=[{"name": "calculator", "confidence": 0.3}], reasoning="User asked a factual question", context_snapshot={"tokens_used": 500}, cost_usd=0.001, duration_ms=23, ) ``` | Parameter | Type | Description | |-------------------|--------------|------------------------------------------------| | `type` | `str` | Decision type (see types above) | | `chosen` | `dict` | The selected option (`name`, `confidence`, etc.) | | `alternatives` | `list[dict]` | Rejected options with reasons | | `reasoning` | `str` | Why this option was chosen | | `context_snapshot`| `dict` | State at decision time (tokens, memory, etc.) | | `cost_usd` | `float` | Cost of this decision in USD | | `duration_ms` | `int` | Time taken to make the decision | ### `wrap_openai()` Instrument an OpenAI client for automatic tracing. ```python from agentlens.integrations.openai import wrap_openai client = wrap_openai(openai.OpenAI()) ``` Returns the same client instance with `chat.completions.create` wrapped. All calls automatically generate LLM spans and tool-call decision points. ### `AgentLensCallbackHandler` LangChain callback handler for automatic tracing. ```python from agentlens.integrations.langchain import AgentLensCallbackHandler handler = AgentLensCallbackHandler( trace_name="my-chain", # Trace name (if no active trace exists) tags=["prod"], # Optional tags session_id="user-123", # Optional session ID ) ``` ## Architecture ``` Your Agent Code │ ▼ AgentLens SDK ── @trace, log_decision(), wrap_openai() │ ▼ Batched HTTP Transport ── Collects traces, flushes every 5s or 10 traces │ ▼ AgentLens API ── https://agentlens.vectry.tech/api/traces │ ▼ Dashboard ── Decision trees, analytics, real-time streaming ``` The SDK is lightweight and non-blocking. Traces are serialized and batched in a background thread, so your agent code is never slowed down by observability. ## Dashboard View your traces at [agentlens.vectry.tech](https://agentlens.vectry.tech): - **Decision Trees** - Visualize the full decision path of every agent run - **Analytics** - Token usage, cost breakdowns, latency percentiles - **Real-time Streaming** - Watch agent decisions as they happen - **Session Grouping** - Track multi-turn conversations by session ID ## Development ```bash cd packages/sdk-python pip install -e ".[all]" # installs openai + langchain extras python -m pytest tests/ -v ``` ## License MIT - Built by [Vectry](https://vectry.tech)