Metadata-Version: 2.4
Name: tracklyng
Version: 0.1.0
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: 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 — prototype

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.

## How it works

```
client app                      tracklyng lib                     document
-----------                     --------------                    ----------
tracklyng.init()  ───────────▶  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  ─────────────────────────────────────────▶  tracklyng_log.jsonl
   (+ 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 the document, and flips the instrumentation
on. They emit the standard `gen_ai.*` OpenTelemetry semantic conventions, so one
span shape covers every provider. In production the exporter would be OTLP →
Tracklyng's collector instead of a local file.

## 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()  # 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
uv sync
export GEMINI_API_KEY=your-real-key
python your_app.py
cat tracklyng_log.jsonl | python -m json.tool   # one captured span
```

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.

## 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 prototypes 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, span exporter that writes the document |
| `tests/` | unit tests for the span-to-record transform and host attributes |
