Convert ATIF agent trajectories to OpenTelemetry protobuf spans with pluggable uploaders.
Project description
harbor-atif2otel
Convert ATIF agent trajectories to OpenTelemetry spans for visualization in any OTel-compatible backend.
Install
pip install harbor-atif2otel
Quick Start
import json
from harbor_atif2otel import convert_trajectory
from harbor_atif2otel.uploaders.mlflow_protobuf import MlflowProtobufUploader
# Load an ATIF trajectory
with open("trajectory.json") as f:
trajectory = json.load(f)
# Convert to OTel ResourceSpans
resource_spans = convert_trajectory(trajectory)
# Upload to MLflow
uploader = MlflowProtobufUploader(
endpoint="https://mlflow.example.com",
experiment_name="my-eval",
token="my-auth-token",
workspace="default",
)
uploader.upload(resource_spans)
API
convert_trajectory(trajectory, trace_seed=None, service_name="harbor", max_attribute_bytes=10240)
Convert a single ATIF trajectory dict to an OTel ResourceSpans protobuf.
- trajectory: Parsed ATIF JSON (dict)
- trace_seed: Optional seed for deterministic trace/span IDs. Defaults to
session_idortrajectory_id. - service_name: OTel resource
service.nameattribute - max_attribute_bytes: Truncation limit for large string attributes
Returns an opentelemetry.proto.trace.v1.trace_pb2.ResourceSpans.
convert_trajectories(trajectories, **kwargs)
Batch convert. Returns list[ResourceSpans].
validate_trajectory(trajectory)
Validate an ATIF trajectory dict. Returns list[str] of issues (empty = valid).
ATIF → OTel Mapping
| ATIF Concept | OTel Span |
|---|---|
| Trajectory | Root AGENT span |
| Conversational turn | Nested AGENT span (multi-turn only) |
Agent step (source: "agent") |
LLM span |
Tool call (tool_calls[]) |
TOOL span (sibling of LLM) |
| Subagent delegation | Nested AGENT span tree |
Span Hierarchy
Single-turn:
AGENT (root)
├── LLM (agent step 1)
├── TOOL (Read)
├── TOOL (Edit)
├── LLM (agent step 2)
└── TOOL (Bash)
Multi-turn:
AGENT (root)
├── AGENT (turn 1)
│ ├── LLM
│ └── TOOL
└── AGENT (turn 2)
├── LLM
└── TOOL
Span Attributes
Spans carry OpenInference semantic attributes for LLM observability:
| Attribute | Set On | Source |
|---|---|---|
openinference.span.kind |
All | AGENT / LLM / TOOL |
session.id |
All | trajectory.session_id |
llm.model_name |
AGENT, LLM | agent.model_name or step.model_name |
llm.token_count.prompt |
AGENT, LLM | final_metrics or step.metrics |
llm.token_count.completion |
AGENT, LLM | final_metrics or step.metrics |
llm.token_count.prompt_details.cache_read |
LLM | step.metrics.cached_tokens |
llm.cost.total |
AGENT, LLM | final_metrics.total_cost_usd or step.metrics.cost_usd |
tool.name |
TOOL | tool_call.function_name |
input.value |
All | Message or arguments |
output.value |
All | Response or observation result |
ATIF v1.7 Feature Support
| Feature | Status |
|---|---|
| Core steps (user/agent/system) | Supported |
| Tool calls + observation matching | Supported |
| Multi-turn splitting | Supported |
final_metrics / metrics token counts |
Supported |
reasoning_content |
Supported |
tool_definitions |
Supported |
llm_call_count: 0 (deterministic dispatch) |
Supported |
llm_call_count > 1 (aggregated) |
Supported |
is_copied_context filtering |
Supported |
subagent_trajectories embedding |
Supported |
Multimodal ContentPart (v1.6+) |
Supported (text extracted, images as metadata) |
context_management system steps |
Supported |
Harbor Job Plugin
The package includes a Harbor job plugin (OtelPlugin) that automatically exports OTel traces during harbor run. It supports two modes:
Streaming — upload each trial as it completes
harbor run --dataset terminal-bench@2.0 --agent claude-code \
--plugin atif2otel \
--plugin-kwarg endpoint=https://mlflow.example.com \
--plugin-kwarg experiment_name=my-eval
Batch — write flat files after the job ends
harbor run --dataset terminal-bench@2.0 --agent claude-code \
--plugin atif2otel \
--plugin-kwarg output_dir=./otel-traces \
--plugin-kwarg encoding=json
Both modes can be combined (stream to endpoint + write files). Mode is auto-detected from which outputs are configured, or set explicitly with --plugin-kwarg mode=stream|batch.
Plugin kwargs
| Kwarg | Env var fallback | Description |
|---|---|---|
endpoint |
OTEL_EXPORTER_OTLP_ENDPOINT |
OTLP endpoint URL |
output_dir |
HARBOR_OTEL_OUTPUT_DIR |
Directory for flat file output |
experiment_name |
MLFLOW_EXPERIMENT_NAME |
MLflow experiment name (defaults to job name) |
token |
MLFLOW_TRACKING_TOKEN |
Auth token for the endpoint |
workspace |
— | MLflow workspace (default: "default") |
encoding |
— | "json" (JSONL) or "pb" (protobuf) |
mode |
— | "auto", "stream", or "batch" |
Shared Export API
The export functions are available for programmatic use:
from harbor_atif2otel.export import export_trial, export_trials
# Single trial
rs = export_trial(Path("jobs/my-job/trial-001"))
# Batch with file output
result = export_trials(trial_dirs, output=Path("out.jsonl"), encoding="json")
print(f"{result.converted} converted, {result.errors} errors")
Writing a Custom Uploader
Implement harbor_atif2otel.uploaders.base.Uploader:
from harbor_atif2otel.uploaders.base import Uploader
from opentelemetry.proto.trace.v1.trace_pb2 import ResourceSpans
class MyUploader(Uploader):
def upload(self, resource_spans: ResourceSpans) -> None:
# Serialize and send to your backend
...
The upload_batch() method is provided by the base class and calls upload() in a loop with error counting.
License
Apache 2.0 — see LICENSE.
Project details
Release history Release notifications | RSS feed
Download files
Download the file for your platform. If you're not sure which to choose, learn more about installing packages.
Source Distribution
Built Distribution
Filter files by name, interpreter, ABI, and platform.
If you're not sure about the file name format, learn more about wheel file names.
Copy a direct link to the current filters
File details
Details for the file harbor_atif2otel-0.1.0.tar.gz.
File metadata
- Download URL: harbor_atif2otel-0.1.0.tar.gz
- Upload date:
- Size: 37.7 kB
- Tags: Source
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
7d359676ce328f01354beb0a9817fff8d52cc0a29d569d19582146e292d61065
|
|
| MD5 |
d3b45966657cd2f9b188471de8b4d3d6
|
|
| BLAKE2b-256 |
012e3e23f912e6f939e48488e4bf6065ff7af9dc48ae5b56cf1bf6de4f28d79a
|
File details
Details for the file harbor_atif2otel-0.1.0-py3-none-any.whl.
File metadata
- Download URL: harbor_atif2otel-0.1.0-py3-none-any.whl
- Upload date:
- Size: 18.4 kB
- Tags: Python 3
- Uploaded using Trusted Publishing? No
- Uploaded via: uv/0.11.26 {"installer":{"name":"uv","version":"0.11.26","subcommand":["publish"]},"python":null,"implementation":{"name":null,"version":null},"distro":{"name":"macOS","version":null,"id":null,"libc":null},"system":{"name":null,"release":null},"cpu":null,"openssl_version":null,"setuptools_version":null,"rustc_version":null,"ci":null}
File hashes
| Algorithm | Hash digest | |
|---|---|---|
| SHA256 |
0f3afd8e8da4650ab7dcb3ee86a1462575878fd3533dc392e37492698289e693
|
|
| MD5 |
537af626fa2b7d28c72963601447b584
|
|
| BLAKE2b-256 |
95d2d527047943e9c5a64d8b98f84be5827da16678e63b252cbf0e730714201a
|