Metadata-Version: 2.5
Name: pytracer-sdk
Version: 0.1.3
Summary: pyTracer instrumentation SDK - capture agent runs as OpenTelemetry-compliant spans.
Project-URL: Homepage, https://pytracer.com
Project-URL: Documentation, https://pytracer.com/docs.html
Project-URL: Repository, https://github.com/vi6120/pyTracer
Project-URL: Issues, https://github.com/vi6120/pyTracer/issues
Author: Vikas Ramaswamy, Nagesha Sheshadri
License-Expression: Apache-2.0
Requires-Python: >=3.10
Requires-Dist: pydantic>=2.6
Provides-Extra: dev
Requires-Dist: hypothesis>=6.100; extra == 'dev'
Requires-Dist: mypy>=1.10; extra == 'dev'
Requires-Dist: pytest-asyncio>=0.23; extra == 'dev'
Requires-Dist: pytest>=8.0; extra == 'dev'
Requires-Dist: ruff>=0.5; extra == 'dev'
Description-Content-Type: text/markdown

# pytracer-sdk

Layer 1 of pyTracer: captures agent actions (LLM calls, tool calls, reasoning steps, handoffs)
as structured, OpenTelemetry-compliant spans, with local redaction and non-blocking flush.

```python
import pytracer_sdk as pytracer

@pytracer.op(kind="llm")
def call_model(prompt: str) -> str:
    ...
```

## Capturing to a gateway

`HTTPTransport` ships spans to the ingestion gateway. Capture is fire-and-forget:
a gateway outage never blocks or crashes the agent. The gateway can accept a batch
(HTTP 202) yet still reject individual spans (for example a span missing
`branch`/`commit`). Those rejections are surfaced rather than lost:

- `HTTPTransport.rejected_spans` is a running count of spans the gateway rejected.
- Each rejection is logged as a warning (on the `pytracer_sdk` logger) with the
  gateway's reason.
- `on_rejection`, an optional `Callable[[int, list], None]`, is called with the
  count and the error list when a batch had rejections, so a sender can alert or
  fail its CI on silently dropped spans.

```python
from pytracer_sdk.transport import HTTPTransport

def alert(count, errors):
    raise SystemExit(f"gateway rejected {count} span(s): {errors[0]}")

transport = HTTPTransport("https://gateway.example", api_key="...", on_rejection=alert)
pytracer.configure(transport, service_name="my-agent")
# ... run the agent ...
print(transport.rejected_spans)  # 0 when every span was accepted
```

A bad `on_rejection` callback can never kill the flush worker: it is called
defensively and a raise from it is logged, not propagated.
