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.
Metadata
Release files for harbor-atif2otel 0.1.1
For a detailed explanation of source distributions (sdists) and built distributions (wheels), please see the package formats documentation.
Source distribution (sdist)
| File | Size | Uploaded | Provenance | Actions |
|---|---|---|---|---|
| harbor_atif2otel-0.1.1.tar.gz | 41.6 kB | Without provenance | Details for harbor_atif2otel-0.1.1.tar.gz |
Built distribution (wheel)
| File | Interpreter | ABI | Platform | Actions Reset |
|---|---|---|---|---|
| harbor_atif2otel-0.1.1-py3-none-any.whl | Python 3 | none | any | Details for harbor_atif2otel-0.1.1-py3-none-any.whl |
Total release size: 64.2 kB
Release files / harbor_atif2otel-0.1.1.tar.gz
| Download URL | harbor_atif2otel-0.1.1.tar.gz |
|---|---|
| Size | 41.6 kB |
| Tags | Source |
|
SHA-256 checksum How to use checksums |
439769f9dcd0787ecb1a25fe3ebe18e6be4a204f0f903dc157cc7fd166be51ce
|
|
BLAKE2b-256 checksum How to use checksums |
394e434f0f1f249716f6e70c6c719ad91c14514b500412b11021b59fd168c1d4
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Release files / harbor_atif2otel-0.1.1-py3-none-any.whl
| Download URL | harbor_atif2otel-0.1.1-py3-none-any.whl |
|---|---|
| Size | 22.6 kB |
| Tags | Python 3 |
|
SHA-256 checksum How to use checksums |
bd04effeab5e31c12ef86b5dbd4481b97e509ee2dfb41e557d7d1cc4e139d4a3
|
|
BLAKE2b-256 checksum How to use checksums |
9ffd1ed9dc788d3c00d07e9d0b457ba19e3d577d8896ac0cb73a0dcce2365383
|
| Upload date | |
|
Uploaded using Trusted Publishing? What is 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}
|
Security for harbor-atif2otel 0.1.1
Provenance
Provenance describes where a file came from. On PyPI, provenance is shared via attestations, which provide a verifiable record of the build or publishing details. View details, limitations and caveats.
No provenance found
None of the files in this release were uploaded with provenance metadata.
A note on package security
PyPI is a repository for distributing packages, not a service for auditing them. While third-party security researchers and our staff do their best to detect and remove malicious packages, PyPI cannot guarantee the quality, security or suitability of any given package.
To learn more about protecting yourself from malicious actors, visit our package security documentation.