Metadata-Version: 2.4
Name: zoklens
Version: 2026.8.1
Summary: ZokLens Python SDK — LLM & Agent Observability via OpenTelemetry
Author-email: ZokLens Team <dev@zoklens.com>
License-Expression: Apache-2.0
Project-URL: Homepage, https://zoklens.com
Project-URL: Documentation, https://docs.zoklens.com/sdk/python
Project-URL: Repository, https://github.com/zokforce/zoklens-sdk-python
Keywords: llm,observability,tracing,agent,opentelemetry,zoklens
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: Programming Language :: Python :: 3
Classifier: Programming Language :: Python :: 3.9
Classifier: Programming Language :: Python :: 3.10
Classifier: Programming Language :: Python :: 3.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.9
Description-Content-Type: text/markdown
Requires-Dist: opentelemetry-api>=1.20.0
Requires-Dist: opentelemetry-sdk>=1.20.0
Requires-Dist: opentelemetry-exporter-otlp-proto-http>=1.20.0
Provides-Extra: openai
Requires-Dist: openai>=1.0.0; extra == "openai"
Provides-Extra: anthropic
Requires-Dist: anthropic>=0.20.0; extra == "anthropic"
Provides-Extra: all
Requires-Dist: openai>=1.0.0; extra == "all"
Requires-Dist: anthropic>=0.20.0; extra == "all"
Provides-Extra: dev
Requires-Dist: pytest>=7.0; extra == "dev"
Requires-Dist: pytest-asyncio>=0.21.0; extra == "dev"

# ZokLens Python SDK

LLM & Agent Observability via OpenTelemetry. Instrument supported LLM calls and
the application spans you explicitly create.

## Quick Start

```bash
pip install "zoklens>=2026.8.1,<2027"
```

For production, freeze or lock the resolved version before deploy.

```python
import zoklens

# 1. Initialize
zoklens.init(
    api_key="zok_xxx",                    # from ZokLens Console → API Keys
    endpoint="https://api.zoklens.com",   # or your self-hosted URL
    project="my-agent",
)

# 2. Auto-instrument LLM SDKs (one line)
zoklens.instrument(providers=["openai-compatible", "anthropic"])

# 3. Your existing code — zero changes needed
import openai
client = openai.OpenAI()
response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}],
)
# ✅ Trace automatically captured: model, tokens, cost, latency
```

## Features

## Typed Agent lifecycle and attempts

Use the typed API when ZokLens should measure application-owned Agent outcomes.
It only emits content-free OTel evidence; it never changes routing, retries,
models, tools, return values, exceptions, cancellation, or timeout behavior.

```python
run = zoklens.start_agent_run(
    agent_id="support-agent",
    agent_version="release-7",
    session_id="opaque-session-1",
    heartbeat_mode="instrumented",
    heartbeat_interval_seconds=30,
    progress_instrumented=True,
)

with run:
    run.progress("step_completed")
    answer = run.model_attempt(
        "deepseek-chat",
    ).execute(lambda: application_generate())
    docs = run.tool_attempt(
        "knowledge-search",
        tool_version="schema-2",
        tool_operation="search",
    ).execute(lambda: application_search())
```

Run IDs are `run_<32 lowercase hex>` from 128 bits of cryptographic randomness;
event and attempt IDs are UUIDv4. `retry_of_attempt_id` is explicit. Prompt,
completion, tool arguments/results, PII, secrets, and chain-of-thought have no
typed constructor fields and content capture is declared `prohibited`.

## Telemetry Data Safety

The legacy generic API still exports scalar `metadata` and caller-provided
`user_id`/`session_id` values as OTel attributes. It does not yet apply the V2
typed governance/content-policy contract to those legacy APIs.

- use opaque or tenant-approved pseudonymous correlation IDs, never email,
  patient/customer identity, secrets, or credentials;
- do not put raw Prompt/query/context/memory/tool bodies or hidden reasoning in
  `metadata`;
- treat generic spans/metadata as telemetry, not as validated Agent, Prompt,
  tool, policy, or authorization evidence.

Use the typed Agent API above for strict lifecycle evidence. Existing generic
APIs continue at their honest lower coverage.

## Version Compatibility

Do not auto-upgrade the SDK at runtime. Do not add startup-time package installs,
runtime self-updaters, or automatic `pip install -U zoklens` scripts to customer
applications. Add the SDK through your normal package manager, commit the
lockfile, and upgrade through an explicit dependency change with tests. The
`2026.x` SDK family is the current compatibility window; patch and minor
releases stay backward compatible for the telemetry protocol unless release
notes say otherwise.

Every trace includes SDK metadata so ZokLens can detect outdated clients:

- `zoklens.sdk.version`
- `zoklens.sdk.language`
- `zoklens.sdk.protocol_version`
- `zoklens.sdk.compatibility_family`

### Auto-Instrumentation

Automatically patches LLM SDKs to capture traces — zero code changes to your LLM calls.

```python
zoklens.instrument(providers=["openai-compatible"])  # OpenAI, DeepSeek, Gemini-compatible, custom gateways
zoklens.instrument(providers=["anthropic"])          # Anthropic SDK
zoklens.instrument(providers=["deepseek"])           # Alias for OpenAI-compatible DeepSeek setups
```

**Captured attributes**:

- Canonical GenAI: `gen_ai.request.model`, `gen_ai.response.model`, `gen_ai.provider.name`, `gen_ai.usage.input_tokens`, `gen_ai.usage.output_tokens`, `gen_ai.usage.total_tokens`
- Compatibility aliases: `llm.model`, `llm.request.model`, `llm.response.model`, `llm.provider`, `llm.tokens.input`, `llm.tokens.output`, `llm.tokens.total`, `llm.duration_ms`

When both request and response models are present, ZokLens uses the response
model for display because it reflects the actual provider result. The SDK
preserves exact provider model strings and does not write `"unknown"` as a model.

### Custom Spans

Track any operation in your agent pipeline:

```python
with zoklens.span("retrieval", metadata={"retrieval_system": "support-kb", "top_k": 5}):
    docs = retriever.search(query)

with zoklens.span("generation", metadata={"model": "gpt-4o"}):
    response = llm.generate(prompt)
```

### Session Tracking

Group related spans under a session with user context:

```python
with zoklens.session(user_id="u123", session_id="s456"):
    # All spans inside automatically get user_id and session_id
    with zoklens.span("step-1"):
        ...
    with zoklens.span("step-2"):
        ...
```

### Shutdown

Flush pending spans before exit:

```python
zoklens.shutdown()
```

## How It Works

```
Your App                          ZokLens
┌─────────────────────┐         ┌──────────────┐
│  import zoklens      │         │              │
│  zoklens.init(...)   │  OTLP  │  Trace Store │
│  zoklens.instrument()│────────→│  Cost Calc   │
│                      │  HTTP  │  AI Copilot  │
│  llm.chat(...)       │         │              │
└─────────────────────┘         └──────────────┘
```

The SDK uses OpenTelemetry under the hood:

- Configures a `TracerProvider` with a custom `ZokLensSpanExporter`
- Exports traces via **OTLP/HTTP** to `POST /api/v1/otel/v1/traces`
- Authenticates with `X-API-Key` header for tenant isolation

## SDK vs Proxy

| | Proxy (Sprint 10) | SDK (Sprint 12) |
|---|---|---|
| Setup | Change `BASE_URL` | `import zoklens` |
| Code changes | Zero | 3 lines |
| Trace depth | HTTP layer | Span/chain level |
| Custom attributes | ❌ | ✅ user_id, session, metadata |
| Agent internals | ❌ | Manual generic spans only; canonical Agent/tool governance is future V2 scope |
| Best for | Quick start | Agent developers |

Both can be used simultaneously.

## Requirements

- Python ≥ 3.9
- OpenTelemetry SDK (installed automatically)

## License

Apache-2.0
