Metadata-Version: 2.4
Name: datagol-lens
Version: 0.2.0
Summary: Python SDK for DataGOL Lens: tracing, cost and budgets for LLM applications
Author: DataGOL
License-Expression: MIT
Project-URL: Homepage, https://datagol.ai
Keywords: llm,observability,tracing,langchain,datagol
Classifier: Programming Language :: Python :: 3
Classifier: Operating System :: OS Independent
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.9
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.27.0
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.3; extra == "langchain"
Dynamic: license-file

# datagol-lens

Python SDK for [DataGOL Lens](https://datagol.ai): traces, token usage, cost and
budgets for LLM applications. Wrap your Anthropic or OpenAI client, or attach a
LangChain callback, and every call shows up in Lens as a trace with its spans.

```bash
pip install datagol-lens
pip install "datagol-lens[langchain]"   # for the LangChain / LangGraph handler
```

## Configure

| Variable | Description |
|---|---|
| `DATAGOL_LENS_API_URL` | Your Lens backend URL (default `http://localhost:8000`) |
| `DATAGOL_LENS_API_KEY` | A project API key (`dgl_...`), created in Lens for the project you trace into |

Both can also be passed to `DatagolLensClient(api_url=..., api_key=...)`.

## Trace one call per request

```python
import anthropic
from datagol_lens import DatagolLensClient

sdk = DatagolLensClient()
client = sdk.wrap(anthropic.Anthropic(), project_id="<project-id>", name="support-bot")

resp = client.messages.create(
    model="claude-haiku-4-5-20251001",
    messages=[{"role": "user", "content": "What is Python?"}],
    max_tokens=200,
)
```

The same works for `openai.OpenAI()`.

## Trace a multi-step run

```python
from datagol_lens import DatagolLensClient, TraceContext

sdk = DatagolLensClient()

with TraceContext(sdk, "<project-id>", name="research-agent",
                  session_id="sess-abc", user_id="user-123") as ctx:
    llm = ctx.wrap(anthropic.Anthropic())      # each call becomes a span
    resp = llm.messages.create(model="claude-haiku-4-5-20251001", max_tokens=300,
                               messages=[{"role": "user", "content": "Summarize recent AI news"}])
    ctx.add_span(name="vector_search", span_type="retriever",
                 input={"query": "AI news"}, output={"chunks": 2})
    ctx.set_output({"summary": resp.content[0].text})
# the trace is sent when the block exits
```

## LangChain and LangGraph

```python
from datagol_lens import DatagolLensClient, TraceContext
from datagol_lens.langchain import DatagolLensCallbackHandler

with TraceContext(DatagolLensClient(), "<project-id>", name="langchain-agent") as ctx:
    handler = DatagolLensCallbackHandler(ctx)
    answer = chain.invoke("What is Python?", config={"callbacks": [handler]})
```

Every chat-model call is recorded as an `llm` span with its token usage, and every
tool call as a `tool` span. Attach the same handler to a LangGraph agent's `config`.

## Budgets and attribution

Pass `company_id`, `agent_key` and `user_id` to `TraceContext` and Lens attributes
the trace's cost to that company, agent and user. Before a run, check the budgets:

```python
from datagol_lens import BudgetExceeded

result = sdk.check_budget("<project-id>", company_id="acme", user_id="u-1", agent_key="support-bot")
if not result["allowed"]:
    raise BudgetExceeded("over budget", result)
```

Lens prices each span from its model and token counts, so the SDK only reports usage.

## License

MIT
