Skip to content
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 5 additions & 0 deletions .changeset/thick-boats-win.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
---
"trackio": minor
---

feat:Add OTLP trace ingestion MVP
2 changes: 2 additions & 0 deletions docs/source/_toctree.yml
Original file line number Diff line number Diff line change
Expand Up @@ -29,6 +29,8 @@
title: API and MCP Server
- local: alerts
title: Alerts
- local: otel_tracing
title: OpenTelemetry Traces
- local: ml_agents
title: ML Agents
- local: environment_variables
Expand Down
93 changes: 93 additions & 0 deletions docs/source/otel_tracing.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,93 @@
# OpenTelemetry Traces

Trackio can receive OpenTelemetry trace data over OTLP/HTTP and show it in the
dashboard's **Traces** tab. This is useful for agent and LLM frameworks that
already emit OpenTelemetry or OpenInference spans, such as smolagents through
`openinference-instrumentation-smolagents`.

This is a minimal v1 receiver:

- supported endpoint: `POST /otel/v1/traces`
- supported encoding: OTLP protobuf (`application/x-protobuf`)
- supported signal: traces
- storage: each incoming span is converted to a `trackio.Trace` log entry

## Start Trackio

Launch a writable Trackio dashboard:

```bash
trackio show
```

Use the write-access URL or token printed by the server. For a local server,
OTLP requests must include the same write token as normal remote logging.

## Configure an OTLP Exporter

Point your OpenTelemetry exporter at Trackio:

```bash
export OTEL_EXPORTER_OTLP_TRACES_ENDPOINT="http://localhost:7860/otel/v1/traces"
export OTEL_EXPORTER_OTLP_TRACES_PROTOCOL="http/protobuf"
export OTEL_EXPORTER_OTLP_TRACES_HEADERS="x-trackio-write-token=<write-token>"
```

Set the Trackio project and run using OpenTelemetry resource attributes:

```python
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider

provider = TracerProvider(
resource=Resource(
{
"trackio.project": "my-project",
"trackio.run": "agent-run",
"service.name": "smolagents",
}
)
)
```

If `trackio.project` is not set, Trackio uses the `project` query parameter if
present, then falls back to `otel-traces`. If `trackio.run` is not set, Trackio
uses `service.name`, then falls back to `otel`.

## Example: smolagents with OpenInference

```bash
pip install smolagents opentelemetry-sdk opentelemetry-exporter-otlp-proto-http openinference-instrumentation-smolagents
```

```python
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from openinference.instrumentation.smolagents import SmolagentsInstrumentor

provider = TracerProvider(
resource=Resource(
{
"trackio.project": "agent-debugging",
"trackio.run": "smolagents",
"service.name": "smolagents",
}
)
)
provider.add_span_processor(BatchSpanProcessor(OTLPSpanExporter()))
SmolagentsInstrumentor().instrument(tracer_provider=provider)

# Run your smolagents application normally. Spans will appear in Trackio.
```

## Notes

Trackio preserves the raw span attributes in trace metadata and maps common
OpenInference fields such as `input.value`, `output.value`, and indexed
`llm.input_messages.*` / `llm.output_messages.*` attributes into conversational
messages for the Traces tab.

Runnable examples are available in `examples/otel-basic-mvp.py` and
`examples/otel-smolagents-integration.py`.
61 changes: 61 additions & 0 deletions examples/otel-basic-mvp.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
"""Minimal OpenTelemetry -> Trackio example.

Run a writable Trackio dashboard first:

trackio show

Then set the write token printed by Trackio and run this example:

export TRACKIO_WRITE_TOKEN=<write-token>
python examples/otel-basic-mvp.py

The span appears in the Trackio dashboard's Traces tab.
"""

import os

from opentelemetry import trace
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor


def main() -> None:
endpoint = os.getenv(
"TRACKIO_OTEL_ENDPOINT",
"http://localhost:7860/otel/v1/traces",
)
write_token = os.getenv("TRACKIO_WRITE_TOKEN")
headers = {"x-trackio-write-token": write_token} if write_token else {}

provider = TracerProvider(
resource=Resource(
{
"trackio.project": os.getenv("TRACKIO_PROJECT", "otel-basic-mvp"),
"trackio.run": os.getenv("TRACKIO_RUN", "manual-span"),
"service.name": "trackio-otel-basic-example",
}
)
)
provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint=endpoint, headers=headers))
)
trace.set_tracer_provider(provider)

tracer = trace.get_tracer("trackio.examples.otel-basic-mvp")
with tracer.start_as_current_span("manual llm call") as span:
span.set_attribute("openinference.span.kind", "llm")
span.set_attribute("input.value", "Say hello to Trackio.")
span.set_attribute("output.value", "Hello from OpenTelemetry.")
span.set_attribute("llm.model_name", "example-model")
span.set_attribute("llm.token_count.prompt", 5)
span.set_attribute("llm.token_count.completion", 4)

provider.force_flush()
provider.shutdown()
print(f"Sent one OTLP span to {endpoint}")


if __name__ == "__main__":
main()
80 changes: 80 additions & 0 deletions examples/otel-smolagents-integration.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,80 @@
"""smolagents + OpenInference + OpenTelemetry -> Trackio example.

Run a writable Trackio dashboard first:

trackio show

Then set the write token printed by Trackio and run this example:

pip install smolagents openinference-instrumentation-smolagents \
opentelemetry-sdk opentelemetry-exporter-otlp-proto-http
export TRACKIO_WRITE_TOKEN=<write-token>
python examples/otel-smolagents-integration.py

This example uses a local dummy smolagents model, so it does not require an LLM
API key. The agent run appears in the Trackio dashboard's Traces tab.

Note: smolagents currently declares a narrower huggingface-hub dependency than
Trackio. If installing smolagents downgrades huggingface-hub in a Trackio dev
environment, reinstall Trackio's supported range with:

pip install "huggingface-hub>=1.10.0,<2"
"""

import os

from openinference.instrumentation.smolagents import SmolagentsInstrumentor
from opentelemetry.exporter.otlp.proto.http.trace_exporter import OTLPSpanExporter
from opentelemetry.sdk.resources import Resource
from opentelemetry.sdk.trace import TracerProvider
from opentelemetry.sdk.trace.export import BatchSpanProcessor
from smolagents import CodeAgent, Model
from smolagents.models import ChatMessage, MessageRole


class DemoModel(Model):
def generate(self, messages, **kwargs):
return ChatMessage(
role=MessageRole.ASSISTANT,
content="```python\nfinal_answer('Trackio received this smolagents trace')\n```",
)


def main() -> None:
endpoint = os.getenv(
"TRACKIO_OTEL_ENDPOINT",
"http://localhost:7860/otel/v1/traces",
)
write_token = os.getenv("TRACKIO_WRITE_TOKEN")
headers = {"x-trackio-write-token": write_token} if write_token else {}

provider = TracerProvider(
resource=Resource(
{
"trackio.project": os.getenv("TRACKIO_PROJECT", "otel-smolagents"),
"trackio.run": os.getenv("TRACKIO_RUN", "dummy-agent"),
"service.name": "smolagents",
}
)
)
provider.add_span_processor(
BatchSpanProcessor(OTLPSpanExporter(endpoint=endpoint, headers=headers))
)

SmolagentsInstrumentor().instrument(tracer_provider=provider)

agent = CodeAgent(
tools=[],
model=DemoModel(model_id="trackio-demo-model"),
max_steps=1,
)
result = agent.run("Return a short confirmation that Trackio tracing works.")

provider.force_flush()
provider.shutdown()
print(f"Agent result: {result}")
print(f"Sent smolagents OpenInference spans to {endpoint}")


if __name__ == "__main__":
main()
1 change: 1 addition & 0 deletions pyproject.toml
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@ dependencies = [
"numpy<3.0.0",
"pillow<13.0.0",
"orjson>=3.0,<4.0.0",
"opentelemetry-proto>=1.25.0,<2.0.0",
"tomli>=2.0.0; python_version < '3.11'",
]
classifiers = [
Expand Down
Loading
Loading