Metadata-Version: 2.4
Name: tracklyng
Version: 0.1.1
Summary: Tracklyng — LLM cost tracking & observability. Capture LLM requests via OpenTelemetry GenAI instrumentation with a one-line install and zero call-site changes.
Author-email: Slyng <tech@loroapp.com.br>
License-Expression: Apache-2.0
License-File: LICENSE
Keywords: anthropic,bedrock,cost-tracking,gemini,llm,observability,openai,opentelemetry,tracing,vertexai
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.11
Classifier: Programming Language :: Python :: 3.12
Classifier: Programming Language :: Python :: 3.13
Classifier: Topic :: Software Development :: Libraries :: Python Modules
Classifier: Topic :: System :: Monitoring
Classifier: Typing :: Typed
Requires-Python: >=3.11
Requires-Dist: certifi>=2024.2.2
Requires-Dist: google-genai>=1.0
Requires-Dist: openai>=1.40
Requires-Dist: opentelemetry-api>=1.27
Requires-Dist: opentelemetry-instrumentation-anthropic>=0.40
Requires-Dist: opentelemetry-instrumentation-bedrock>=0.40
Requires-Dist: opentelemetry-instrumentation-google-generativeai>=0.40
Requires-Dist: opentelemetry-instrumentation-openai>=0.40
Requires-Dist: opentelemetry-instrumentation-vertexai>=0.40
Requires-Dist: opentelemetry-sdk>=1.27
Requires-Dist: python-dotenv>=1.0
Requires-Dist: structlog>=24.1
Description-Content-Type: text/markdown

# Tracklyng

Capture what can be logged from a real LLM request, with a one-line install in
the client's app and **zero** changes to their call site.

```bash
pip install tracklyng
```

## How it works

```
client app                       tracklyng lib                      Tracklyng
-----------                      --------------                     ----------
tracklyng.init(api_key=...) ──▶  sets up OpenTelemetry tracer
                                 turns on the GenAI instrumentation
                                   (every provider SDK)
client.models.generate_content(...)
        │  (real request still goes straight to the provider)
        ▼
   OpenTelemetry instrumentation captures a span
        │
        ▼
   our SpanExporter  ──────────────────────────────────▶  hosted ingestion endpoint
   (+ token counts and host/location, lifted from the span & resource)
```

We do **not** maintain the monkey-patches ourselves — the
`opentelemetry-instrumentation-*` packages do. `tracklyng.init()` just stands up
the tracer, points the exporter at Tracklyng's managed ingestion endpoint, and
flips the instrumentation on. They emit the standard `gen_ai.*` OpenTelemetry
semantic conventions, so one span shape covers every provider. The exporter POSTs
each captured span as JSON to the hosted endpoint, where it is stored in
`telemetry_spans`.

## Run it

Call `tracklyng.init()` once, before the first LLM request — the rest is the
provider SDK code you already have:

```python
import os

import tracklyng

tracklyng.init(api_key="tlk_live_...")  # the entire integration

# ...your existing, unmodified provider call:
from google import genai

client = genai.Client(api_key=os.environ["GEMINI_API_KEY"])
client.models.generate_content(
    model="gemini-3.1-flash-lite",
    contents="In one sentence, what is observability for LLM apps?",
)
```

```bash
pip install tracklyng
export GEMINI_API_KEY=your-real-key
python your_app.py   # the captured span is sent to Tracklyng
```

The same `tracklyng.init()` covers every supported provider: an Azure OpenAI,
Anthropic, Bedrock, or Vertex AI call produces a span with the *same* `gen_ai.*`
keys.

Following Sentry's model for the credential: called with **no** `api_key`,
Tracklyng runs as a no-op (nothing is captured or sent) so it is safe to leave in
place across environments; a **malformed** key raises `TracklyngConfigError` at
`init()`; a well-formed key that turns out to be revoked is reported and the batch
dropped at export time, never crashing or blocking the host application.

## Providers

`tracklyng.init()` switches on one OpenTelemetry instrumentation per LLM SDK we
ship. A client only produces spans for the SDKs they actually use; others are
skipped silently. Adding a provider = adding its instrumentor to
`_enable_instrumentors`.

| Provider | Instrumentation | Notes |
|----------|-----------------|-------|
| OpenAI / **Azure OpenAI** | `opentelemetry-instrumentation-openai` | one instrumentor covers both — `AzureOpenAI` is part of the `openai` SDK |
| **Anthropic / Claude** | `opentelemetry-instrumentation-anthropic` | also covers Claude via the `anthropic` SDK's Bedrock client |
| **AWS Bedrock** | `opentelemetry-instrumentation-bedrock` | patches the `boto3` `bedrock-runtime` client |
| **Vertex AI** | `opentelemetry-instrumentation-vertexai` | patches `google-cloud-aiplatform` |
| **Google Gemini** | `opentelemetry-instrumentation-google-generativeai` | patches the unified `google-genai` SDK |

> All five emit the same `gen_ai.*` attributes, so the captured span shape is
> identical across providers — our exporter reads one set of keys regardless of
> which provider made the call.

## Data captured

> **Prompt and response content is captured verbatim.** Each span carries the
> full `gen_ai.*` payload the instrumentation emits — including **user messages,
> system instructions, and model completions** (both the current semantic-convention
> attributes and event bodies). Nothing is redacted or stripped; the complete
> record is stored server-side in `telemetry_spans.payload`.

Because the library runs inside your application, anything your app sends to or
receives from an LLM — including PII, PHI, or other regulated data — is part of
the captured span. If you operate under GDPR/HIPAA or similar obligations,
account for this prompt/response content in your data-processing assessment
before enabling Tracklyng. (Earlier builds stripped content to metadata only;
that behavior was removed — content is now persisted in full.)

## Layout

| Path | What |
|------|------|
| `tracklyng/__init__.py` | exposes `tracklyng.init()` |
| `tracklyng/_capture.py` | OTel setup, instrumentor list, and the span exporter that POSTs each span to the ingestion endpoint |
| `tests/` | unit tests for the span-to-record transform, the exporter, and init's failure modes |
