Metadata-Version: 2.4
Name: logicgaze
Version: 1.6.0
Summary: LogicGaze SDK — AI-native observability for tracing, evaluating, and monitoring production LLM applications. Instrument OpenAI, Anthropic, and any LLM with one line of code.
Project-URL: Homepage, https://github.com/VikneeshVG/logicgaze
Project-URL: Repository, https://github.com/VikneeshVG/logicgaze
Project-URL: Issues, https://github.com/VikneeshVG/logicgaze/issues
Author-email: Vikneesh VG <vikneeshwaran.k@ventragate.com>
License: MIT
License-File: LICENSE
Keywords: ai,anthropic,cohere,evaluation,google,langchain,litellm,llm,logicgaze,observability,openai,tracing
Classifier: Development Status :: 4 - Beta
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: MIT License
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: Programming Language :: Python :: 3.13
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Requires-Python: >=3.9
Requires-Dist: httpx>=0.26.0
Provides-Extra: agents
Requires-Dist: openai-agents>=0.0.7; extra == 'agents'
Provides-Extra: all
Requires-Dist: click>=8.0.0; extra == 'all'
Requires-Dist: cohere>=5.0.0; extra == 'all'
Requires-Dist: docker>=6.0.0; extra == 'all'
Requires-Dist: google-genai>=1.0.0; extra == 'all'
Requires-Dist: groq>=0.9.0; extra == 'all'
Requires-Dist: langchain-core>=0.1.0; extra == 'all'
Requires-Dist: langchain>=0.1.0; extra == 'all'
Requires-Dist: litellm>=1.0.0; extra == 'all'
Requires-Dist: mistralai>=1.0.0; extra == 'all'
Requires-Dist: psutil>=5.9.0; extra == 'all'
Requires-Dist: pyyaml>=6.0; extra == 'all'
Requires-Dist: schedule>=1.2.0; extra == 'all'
Requires-Dist: tomli>=2.0.0; extra == 'all'
Provides-Extra: cohere
Requires-Dist: cohere>=5.0.0; extra == 'cohere'
Provides-Extra: dev
Requires-Dist: build>=1.0.0; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.21.0; extra == 'dev'
Requires-Dist: pytest>=7.0.0; extra == 'dev'
Requires-Dist: respx>=0.20.0; extra == 'dev'
Requires-Dist: twine>=4.0.0; extra == 'dev'
Provides-Extra: google
Requires-Dist: google-genai>=1.0.0; extra == 'google'
Provides-Extra: groq
Requires-Dist: groq>=0.9.0; extra == 'groq'
Provides-Extra: langchain
Requires-Dist: langchain-core>=0.1.0; extra == 'langchain'
Requires-Dist: langchain>=0.1.0; extra == 'langchain'
Provides-Extra: litellm
Requires-Dist: litellm>=1.0.0; extra == 'litellm'
Provides-Extra: mistral
Requires-Dist: mistralai>=1.0.0; extra == 'mistral'
Provides-Extra: security
Requires-Dist: click>=8.0.0; extra == 'security'
Requires-Dist: psutil>=5.9.0; extra == 'security'
Requires-Dist: pyyaml>=6.0; extra == 'security'
Requires-Dist: schedule>=1.2.0; extra == 'security'
Requires-Dist: tomli>=2.0.0; extra == 'security'
Provides-Extra: security-containers
Requires-Dist: click>=8.0.0; extra == 'security-containers'
Requires-Dist: docker>=6.0.0; extra == 'security-containers'
Requires-Dist: psutil>=5.9.0; extra == 'security-containers'
Requires-Dist: pyyaml>=6.0; extra == 'security-containers'
Requires-Dist: schedule>=1.2.0; extra == 'security-containers'
Requires-Dist: tomli>=2.0.0; extra == 'security-containers'
Description-Content-Type: text/markdown

# logicgaze

[![PyPI version](https://img.shields.io/pypi/v/logicgaze.svg)](https://pypi.org/project/logicgaze/)
[![Python 3.9+](https://img.shields.io/pypi/pyversions/logicgaze.svg)](https://pypi.org/project/logicgaze/)
[![License: MIT](https://img.shields.io/badge/License-MIT-yellow.svg)](https://opensource.org/licenses/MIT)

**LogicGaze SDK** — AI-native observability for production LLM applications.

Instrument OpenAI, Anthropic, and any LLM with one line of code. Every call is automatically traced, cost-attributed, and visible in the [LogicGaze](https://logicgaze.com) dashboard.

## Features

- **Zero-config tracing** — wrap your existing client (or use `init(auto_instrument=True)`), get full observability instantly
- **Streaming instrumentation** — token usage and time-to-first-token captured even for `stream=True` calls
- **Cost tracking** — token-level cost attribution per model, provider, and session
- **Distributed traces** — full span trees across LLM calls, tool calls, agents, and retrievals
- **Scores & feedback** — `log_score()` / `log_feedback()` attach evals and user signals to traces
- **Datasets** — build eval datasets in code, or copy production traces into test cases
- **Prompt management** — versioned `{{variable}}` templates with a client-side TTL cache
- **Masking** — redact or transform what LogicGaze stores (`mask`, `hide_inputs`, `hide_outputs`)
- **OpenAI Agents SDK** — `instrument_openai_agents()` traces agent runs automatically
- **Guardrails** — PII detection, prompt injection detection, harmful content filtering
- **LLM-as-Judge** — async evaluation scoring (faithfulness, relevance, coherence, hallucination)
- **OpenTelemetry ingest** — the LogicGaze backend accepts OTLP/HTTP JSON at `/api/v1/otel/v1/traces` (server-side ingest; this SDK does not emit OTel itself)

## Installation

```bash
pip install logicgaze
```

## Quick Start

### 1. Initialize once at startup

```python
import logicgaze

logicgaze.init(
    api_key="lgz_...",               # LogicGaze API key
    base_url="https://your-logicgaze-backend.com",
)
```

Set `LOGICGAZE_API_KEY` in your environment to avoid passing `api_key` explicitly.

### 2. Wrap your AI client

**OpenAI**
```python
from openai import OpenAI
import logicgaze

logicgaze.init(api_key="lgz_...")
client = logicgaze.wrap_openai(OpenAI())

response = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Hello!"}],
)
print(response.choices[0].message.content)
```

**Anthropic**
```python
from anthropic import Anthropic
import logicgaze

logicgaze.init(api_key="lgz_...")
client = logicgaze.wrap_anthropic(Anthropic())

response = client.messages.create(
    model="claude-opus-4-6",
    max_tokens=1024,
    messages=[{"role": "user", "content": "Hello!"}],
)
print(response.content[0].text)
```

**Or patch constructors automatically (opt-in)**
```python
import logicgaze
logicgaze.init(api_key="lgz_...", auto_instrument=True)

import openai
client = openai.OpenAI()   # already wrapped — every new client is traced

logicgaze.uninstrument()   # restore the original constructors at any time
```

`auto_instrument=True` patches `openai.OpenAI` / `openai.AsyncOpenAI` and
`anthropic.Anthropic` / `anthropic.AsyncAnthropic`. It is idempotent, fully
try/except-guarded, and reversible with `logicgaze.uninstrument()`.

### 3. Group calls with `TraceContext`

```python
from logicgaze import TraceContext, wrap_openai
from openai import OpenAI

client = wrap_openai(OpenAI())

with TraceContext(session_id="sess-abc", user_id="user-42", service_name="chat-api"):
    # All calls inside share the same trace in the dashboard
    client.chat.completions.create(model="gpt-4o", messages=[...])
    client.chat.completions.create(model="gpt-4o", messages=[...])
```

### 4. Use `@traceable` on functions

```python
from logicgaze import traceable, wrap_openai
from openai import OpenAI

client = wrap_openai(OpenAI())

@traceable(session_id="sess-1", service_name="recommendation-engine")
def recommend(user_query: str):
    return client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": user_query}],
    )

# Async functions are fully supported
@traceable(service_name="summarizer")
async def summarize(text: str):
    return await client.chat.completions.create(
        model="gpt-4o",
        messages=[{"role": "user", "content": f"Summarize: {text}"}],
    )
```

### 5. Query traces programmatically

```python
from logicgaze import get_client

lg = get_client()

# List recent traces
traces = lg.list_traces(service_name="chat-api", page_size=20)

# Get a specific trace with all spans
trace = lg.get_trace("trace-uuid-here", include_spans=True)

# Dashboard overview (last 24 h)
overview = lg.get_dashboard(window_hours=24)
print(overview["total_requests"], overview["total_cost_usd"])

# Estimate cost before a call
estimate = lg.estimate_cost("openai", "gpt-4o", prompt_tokens=500, completion_tokens=200)
```

## Streaming

Streamed calls are traced too — no code changes:

```python
stream = client.chat.completions.create(
    model="gpt-4o",
    messages=[{"role": "user", "content": "Write a haiku"}],
    stream=True,
)
for chunk in stream:
    print(chunk.choices[0].delta.content or "", end="", flush=True)
```

- For OpenAI-protocol providers the SDK injects `stream_options={"include_usage": true}`
  automatically so the final chunk carries token usage (retried once without it if
  a provider rejects the parameter)
- For Anthropic, usage is aggregated from `message_start` / `message_delta` stream events
- The span records the full aggregated output, token usage, latency, and
  `time_to_first_token_ms` in metadata — submitted via the non-blocking background queue

## Scores & Feedback

```python
lg = logicgaze.get_client()

# data_type inferred: NUMERIC / CATEGORICAL / BOOLEAN
lg.log_score(trace_id, "relevance", 0.92)
lg.log_score(trace_id, "tone", string_value="formal")
lg.log_score(trace_id, "is_correct", True)
lg.log_score(trace_id, "faithfulness", 0.8, span_id=span_id, comment="one unsupported claim")

# End-user feedback
lg.log_feedback(trace_id, feedback_type="thumbs", score=1, label="up")
lg.log_feedback(trace_id, comment="Answer was outdated")

scores = lg.list_scores(trace_id=trace_id)
```

Async variants: `alog_score`, `alog_feedback`, `alist_scores`.

## Datasets

Turn production traces into test cases:

```python
dataset = lg.create_dataset("qa-regression", description="Golden QA test cases")

lg.add_dataset_item(dataset["id"], input={"question": "What is RAG?"},
                    expected_output={"answer": "..."})

lg.add_dataset_items(dataset["id"], [
    {"input": {"question": "q1"}, "expected_output": {"answer": "a1"}},
    {"input": {"question": "q2"}},
])  # per-item errors collected — one failure never aborts the rest

# Copy production traces into the dataset (input/output from each trace's first LLM span)
lg.add_traces_to_dataset(dataset["id"], trace_ids=["trace-uuid-1", "trace-uuid-2"])

items = lg.get_dataset_items(dataset["id"], page=1, page_size=50)
datasets = lg.list_datasets()
```

## Prompt Management

```python
# Create (or version-bump — the server auto-increments versions)
lg.create_prompt("welcome-email", "Hi {{name}}, welcome to {{product}}!",
                 tags=["onboarding"], config={"model": "gpt-4o"})

# Fetch with a client-side TTL cache (default 60s, stale-while-revalidate)
prompt = lg.get_prompt("welcome-email")                 # latest
prompt = lg.get_prompt("welcome-email", version=2)      # pinned
text = prompt.compile(name="Ada", product="LogicGaze")  # {{var}} substitution

versions = lg.list_prompt_versions("welcome-email")
```

Cache semantics: fresh entries are served with no network call; stale entries are
served immediately and refreshed in a background thread; if the refresh fails, the
stale prompt keeps being served — network errors never reach your code once a
prompt has been fetched at least once. Async: `aget_prompt`, `acreate_prompt`.

## Masking

Control what LogicGaze **stores** (gateway-proxied calls still send the real
messages to the LLM provider — masking only affects persisted observability data):

```python
logicgaze.init(
    api_key="lgz_...",
    hide_inputs=True,          # stored input_messages become [REDACTED]
    hide_outputs=True,         # stored output content becomes [REDACTED]
    mask=my_scrubber,          # or a custom callable applied before storage
)
```

Also available as env vars: `LOGICGAZE_HIDE_INPUTS`, `LOGICGAZE_HIDE_OUTPUTS`.
If `mask` raises, `[MASKING_ERROR]` is stored instead of raw data.

## OpenAI Agents SDK

```bash
pip install logicgaze[agents]
```

```python
import logicgaze
from logicgaze import instrument_openai_agents

logicgaze.init(api_key="lgz_...")
instrument_openai_agents()
# every Agent run is now traced — agent, generation (llm), tool,
# handoff, and guardrail spans land in LogicGaze automatically
```

Equivalent to `agents.add_trace_processor(LogicGazeTracingProcessor())`. All
callbacks are non-blocking and error-guarded — tracing can never crash the agent.

## Configuration

| Parameter | Environment variable | Default |
|-----------|---------------------|---------|
| `api_key` | `LOGICGAZE_API_KEY` | — |
| `base_url` | `LOGICGAZE_BASE_URL` | `http://localhost:8000` |
| `timeout` | `LOGICGAZE_TIMEOUT` | `30.0` s |
| `sample_rate` | `LOGICGAZE_SAMPLE_RATE` | `1.0` |
| `max_retries` | `LOGICGAZE_MAX_RETRIES` | `3` |
| `batch_size` | `LOGICGAZE_BATCH_SIZE` | `50` |
| `flush_interval` | `LOGICGAZE_FLUSH_INTERVAL` | `5.0` s |
| `debug` | `LOGICGAZE_DEBUG` | `False` |
| `hide_inputs` | `LOGICGAZE_HIDE_INPUTS` | `False` |
| `hide_outputs` | `LOGICGAZE_HIDE_OUTPUTS` | `False` |
| `mask` | — | `None` |
| `auto_instrument` | — | `False` |

## API Reference

### `logicgaze.init(api_key, base_url, timeout, *, sample_rate, max_retries, batch_size, flush_interval, debug, mask, hide_inputs, hide_outputs, auto_instrument)`
Initialize the global client. Call once at application startup.

### `logicgaze.uninstrument()`
Reverse `auto_instrument` — restores the patched OpenAI/Anthropic constructors.

### `wrap_openai(client, *, service_name=None)`
Patches `client.chat.completions.create` (sync & async, streaming included). Returns the same client object.

### `wrap_anthropic(client, *, service_name=None)`
Patches `client.messages.create` (sync & async, streaming included). Returns the same client object.

### `TraceContext(*, project, trace_id, session_id, user_id, service_name, tags)`
Context manager (sync & async). Injects trace metadata into every gateway call made within the block.

### `@traceable(*, name, project, session_id, user_id, service_name, tags, run_type)`
Decorator that wraps the function body in a `TraceContext`. Works with sync and async functions.

### `instrument_openai_agents(client=None)`
Registers a `LogicGazeTracingProcessor` with the OpenAI Agents SDK. Returns the processor.

### `LogicGazeClient` methods

| Method | Description |
|--------|-------------|
| `chat_completion(provider, model, messages, **kwargs)` | Proxied LLM call via gateway |
| `achat_completion(...)` | Async version |
| `log_score(trace_id, name, value, ...)` | Attach an evaluation score (NUMERIC/CATEGORICAL/BOOLEAN) |
| `log_feedback(trace_id, feedback_type=..., ...)` | Attach end-user feedback |
| `list_scores(...)` | List scores with trace/name filters |
| `create_dataset(name, ...)` / `list_datasets()` | Manage datasets |
| `add_dataset_item(s)(...)` / `get_dataset_items(...)` | Manage dataset items |
| `add_traces_to_dataset(dataset_id, trace_ids)` | Copy production traces into a dataset |
| `get_prompt(name, version=None, cache_ttl_seconds=60)` | Fetch a cached `Prompt` (`.compile(**vars)`) |
| `create_prompt(name, prompt, ...)` / `list_prompt_versions(name)` | Manage prompt versions |
| `list_traces(**filters)` | List traces with optional filters |
| `get_trace(trace_id, include_spans)` | Fetch a trace and its spans |
| `get_spans(trace_id)` | Fetch all spans for a trace |
| `get_agent_graph(trace_id)` | Fetch the visual agent execution graph |
| `get_dashboard(window_hours)` | Aggregated dashboard metrics |
| `get_model_distribution(window_hours)` | Token & cost breakdown by model |
| `get_provider_breakdown(window_hours)` | Breakdown by AI provider |
| `estimate_cost(provider, model, prompt_tokens, completion_tokens)` | Pre-call cost estimate |
| `list_model_costs(provider)` | Pricing table for a provider |
| `get_guardrails_summary(window_hours)` | Guardrail event summary |

Most read/write methods have `a`-prefixed async variants (e.g. `alog_score`, `aget_prompt`, `acreate_span`).

## Requirements

- Python 3.9+
- `httpx >= 0.26.0`

Optional extras: `logicgaze[agents]` (OpenAI Agents SDK), `logicgaze[litellm]`,
`logicgaze[langchain]`, `logicgaze[google]`, `logicgaze[cohere]`, `logicgaze[groq]`,
`logicgaze[mistral]`, `logicgaze[all]`.

## Links

- **Dashboard**: [logicgaze.com](https://logicgaze.com)
- **Documentation**: [docs.logicgaze.com](https://docs.logicgaze.com)
- **GitHub**: [github.com/VikneeshVG/logicgaze](https://github.com/VikneeshVG/logicgaze)
- **PyPI**: [pypi.org/project/logicgaze](https://pypi.org/project/logicgaze/)

## License

MIT
