Metadata-Version: 2.4
Name: opexia-trace
Version: 0.1.0a2
Summary: Pluggable reasoning-trace observability for LLM applications
Author: OpexIA
License: Apache-2.0
Project-URL: Homepage, https://github.com/star-56/deep_reasoning/tree/main/opexia_trace
Project-URL: Repository, https://github.com/star-56/deep_reasoning
Project-URL: Issues, https://github.com/star-56/deep_reasoning/issues
Keywords: observability,opentelemetry,llm,reasoning,tracing,otel
Classifier: Development Status :: 3 - Alpha
Classifier: Intended Audience :: Developers
Classifier: License :: OSI Approved :: Apache Software License
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: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Requires-Python: >=3.10
Description-Content-Type: text/markdown
Requires-Dist: opentelemetry-api<2.0,>=1.24
Requires-Dist: opentelemetry-sdk<2.0,>=1.24
Requires-Dist: opentelemetry-exporter-otlp<2.0,>=1.24
Requires-Dist: pydantic<3.0,>=2.6
Requires-Dist: tiktoken<1.0,>=0.6
Requires-Dist: httpx<1.0,>=0.27
Provides-Extra: openllmetry
Requires-Dist: traceloop-sdk<1.0,>=0.18; extra == "openllmetry"
Provides-Extra: litellm
Requires-Dist: litellm<2.0,>=1.40; extra == "litellm"
Provides-Extra: adapters-microsoft
Provides-Extra: adapters-langchain
Requires-Dist: langchain-core<1.0,>=0.2; extra == "adapters-langchain"

# opexia-trace

Pluggable reasoning-trace observability for LLM applications.

`opexia-trace` is the client SDK for the **OpexIA Observability Layer**. It
captures *reasoning-altitude* spans — the decisions an agent made, the sources
it used, what each step cost, and the inputs to a reliability score — and ships
them to an OpexIA backend over OTLP. Unlike raw LLM logging, the unit of
observation is the *reasoning step*, not the HTTP call.

## Install

```bash
pip install opexia-trace
```

## Quickstart

Call `init()` once at process startup:

```python
from opexia.trace import init

init(
    org_id="pwc",
    workspace_id="chatpwc",
    project_id="due-diligence",
    backend_url="https://opexia.internal.example.com",
    api_key="opx_live_...",          # your workspace API key
)
```

With `auto_instrument=True` (the default), every LLM call made through
litellm / anthropic / openai is now traced. No other code changes required.

## The three integration patterns

### Pattern A — Auto-instrument (zero code changes)

`init(auto_instrument=True)` monkey-patches the litellm / anthropic / openai
client methods at startup. Every completion call emits a `gen_ai.*` span
carrying the model, token usage, and computed USD cost. This is the default;
you get it just by calling `init()`.

### Pattern B — the `@observe` decorator

Wrap any function — sync or async — to emit a reasoning span for it:

```python
from opexia.trace import observe

@observe(reasoning_role="decomposer", node_type="decomposer", name="plan.decompose")
async def decompose(query: str) -> list[str]:
    ...
```

Nested `@observe` calls auto-parent via OpenTelemetry context — a decorated
function called inside another decorated function becomes its child span, so
the reasoning tree falls out of normal call structure.

### Pattern C — the `ReasoningTrace` context manager

For explicit, structured traces — when you want to record decisions, sources,
and costs by hand:

```python
from opexia.trace import ReasoningTrace

with ReasoningTrace(name="answer.query", reasoning_role="synthesis") as trace:
    trace.record_cost(model="claude-opus-4-6", input_tokens=1200, output_tokens=400)
    trace.record_sources(used=[...], consulted=[...], dropped=[...])
    trace.record_decision(selected="opt_a", rules_fired=[...], scores={...})
    with trace.subnode(name="retrieve", node_type="retriever") as node:
        node.record_sources(...)
```

### Framework adapters

For agent frameworks, one line instruments a whole crew:

```python
from opexia.trace.adapters.microsoft import instrument_microsoft_agents
instrument_microsoft_agents(crew)        # Microsoft Agent Framework

from opexia.trace.adapters.reasonix import instrument_reasonix
instrument_reasonix(orchestrator)        # Reasonix orchestrator
```

Adapters are duck-typed — they import nothing from the framework, so they
never pin you to a version.

## Configuration — `init()` parameters

| Parameter | Default | Meaning |
|-----------|---------|---------|
| `org_id` / `workspace_id` / `project_id` | required | Tenancy identity stamped on every span. |
| `backend_url` | required | The OpexIA backend base URL. |
| `api_key` | required | Workspace API key (`opx_live_…` / `opx_test_…`). |
| `collector_endpoint` | `http://localhost:4317` | OTLP collector endpoint. |
| `wal_path` | `.opexia-wal/spans.jsonl` | Write-ahead-log path (see Durability). |
| `auto_instrument` | `True` | Patch litellm/anthropic/openai at startup. |
| `fail_open` | `False` | See "fail_open scope" in the FAQ. |
| `sampler_rate` | `1.0` | Fraction of traces sampled. |

## Durability — the write-ahead log

Spans are written to a local WAL (`wal_path`) before they are exported, so a
process crash or an unreachable collector does not lose spans — the next
`init()` replays any WAL left by a previous run. The WAL is why a dropped span
is treated as a *bug*, not an expected failure mode.

## Troubleshooting

- **No spans appear in the backend.** Check `backend_url` / `collector_endpoint`
  reachability and that `api_key` is a live (not revoked) workspace key.
- **`init() has not been called`.** A `@observe` / `ReasoningTrace` ran before
  `init()`. Call `init()` once at process startup, before any traced code.
- **Auto-instrument patched 0 clients.** None of litellm/anthropic/openai are
  importable in the process — Pattern A has nothing to patch. Use Pattern B/C.

## FAQ

**Does `opexia-trace` import my agent framework?** No. The adapters are
duck-typed and import nothing from the framework or from the OpexIA backend.

**What does `fail_open` actually cover?** `fail_open=True` only suppresses a
failure of *auto-instrument registration* during `init()` — it does NOT wrap
the whole `init()` body. A bad `collector_endpoint` or a WAL-path I/O error
still raises. Treat `fail_open` as "don't let auto-instrument break my
startup," not "make `init()` never raise." (Tracked as deferred note B2-N5;
a future release may widen the scope.)

**Can I emit spans during process shutdown?** No. Do not start new spans after
your shutdown hook runs — a span started concurrently with the exporter
shutting down can be dropped (this race exists in upstream OpenTelemetry's
`BatchSpanProcessor` too). Finish traced work before tearing the process down.
(Tracked as deferred note B2-N7.)

**Is the SDK safe in production?** Yes — span export is buffered and
WAL-backed, and a traced function never fails because tracing failed:
attribute-extraction errors are caught and recorded on the span, and the
original return value passes through untouched.
