> This page is also available as Markdown: append .md to its URL. The full index of Sikaru docs is at https://docs.sikaru.ai/llms.txt. Code examples use only operations from the public API reference at https://docs.sikaru.ai/api-reference.

# Trace ingestion

Operations on this page (Python SDK `sikaru_api`):

0. Trajectory format: Harbor ATIF (Agent Trajectory Interchange Format), spec [https://github.com/harbor-framework/harbor/blob/main/rfcs/0001-trajectory-format.md](https://github.com/harbor-framework/harbor/blob/main/rfcs/0001-trajectory-format.md) and [https://docs.harborframework.com/agents/atif](https://docs.harborframework.com/agents/atif). Accepted `schema_version`: `ATIF-v1.7` or `ATIF-v1.8`; managed run trajectories are exported as `ATIF-v1.8`. Validation rules: `agent.name` and `agent.version` required; `step_id` starts at 1 and increments by 1; `source` is `user`, `agent`, or `system`; every step has `message`; `tool_calls`, `model_name`, `reasoning_content`, `metrics` only on agent steps; each tool call has `tool_call_id`, `function_name`, `arguments`; observation results reference calls with `source_call_id`; `timestamp`, when present, is ISO 8601.
   Historical traces from LangSmith, Braintrust, Raindrop, Langfuse, or other tools: export them, convert each trace to one ATIF document (the converter on this page maps OpenAI-style chat messages), and submit with `format="harbor.atif.v1"`. OpenInference or OTLP spans can be submitted directly but are stored as spans, not trajectories.
1. Submit traces: `client.trace_streams.stream_openinference_spans(format=..., trajectory=..., dataset=..., metadata=TraceMetadata(account_id=..., project_id=..., source=...), idempotency_key=...)`. Declared formats: `harbor.atif.v1` (uses `trajectory`), `openinference.v1` (uses `spans` of generated `OpenInferenceSpan` values), `opentelemetry.otlp.v1` (uses `resource_spans`). Reuse an idempotency key only for an identical batch. An ingest acknowledgement does not prove evaluation or improvement completed.
2. Read a managed run's trajectory: `client.runs.get_trajectory(project, run_id)`. Requires a project API key with `runs:read`. Response fields: `runId`, `harnessVersionId`, `status`, `trajectory` (ATIF 1.8). Omitted private content is marked in `trajectory.extra`.
3. 404 on an authorized, existing run means no retained trajectory; a missing or inaccessible run can also return 404. A service error means the read failed: retry the read, do not submit the run again.
4. Missing metrics mean unknown usage, not zero. Unavailable trajectory or failed read is not zero steps or zero usage.
5. Dashboard: project > **Conversations** > managed run history; **Show older managed runs** pages back. Managed history and submitted traces are separate views; do not add their counts together.

Sikaru has two ways to get execution evidence. **Submitted traces** come from an agent you run elsewhere. **Managed run trajectories** come from runs Sikaru executes. They are separate entry points and separate views.

```mermaid
flowchart LR
    External["Agent you run elsewhere"] -->|"submit traces"| Submitted["Submitted traces view"]
    Managed["Managed run in Sikaru"] -->|"retained automatically"| History["Managed run history"]
    History --> Trajectory["Trajectory and measured usage"]
```

In short: submit traces from your own agent; managed runs need no upload and appear in their own history.

These examples use the authenticated `client` and `project` from the [Quickstart](/get-started/quickstart).

## Trajectory format: Harbor ATIF

Sikaru records every trajectory in the **Agent Trajectory Interchange Format (ATIF)** from [Harbor](https://docs.harborframework.com/agents/atif). An ATIF document is one JSON object holding an agent's full interaction: user and system messages, agent replies, reasoning, tool calls, tool results, and metrics. One open format means the same trajectory works for inspection, evaluation, and improvement, wherever it came from.

| Field            | Purpose                                                                           |
| ---------------- | --------------------------------------------------------------------------------- |
| `schema_version` | `ATIF-v1.7` or `ATIF-v1.8`. Managed run trajectories are exported as `ATIF-v1.8`. |
| `agent`          | Agent `name` and `version` (both required), plus optional `model_name`            |
| `steps`          | Ordered `user`, `agent`, and `system` steps, numbered from 1                      |
| `session_id`     | Optional ID shared by trajectories from one conversation                          |
| `final_metrics`  | Optional token, cost, and step totals                                             |

Read the [ATIF specification](https://github.com/harbor-framework/harbor/blob/main/rfcs/0001-trajectory-format.md) for every field.

## Bring traces from LangSmith, Braintrust, Raindrop, and others

Historical traces become trajectories once they are in ATIF. Export them from your current tool, convert each trace to one ATIF document, and submit it.

```mermaid
flowchart LR
    Tool["LangSmith, Braintrust, Raindrop, Langfuse, ..."] -->|"export"| Raw["Exported traces"]
    Raw -->|"convert"| Atif["ATIF documents"]
    Atif -->|"harbor.atif.v1"| Sikaru["Sikaru trajectories"]
```

| What you have                                                        | What to do                                                                                                                          |
| -------------------------------------------------------------------- | ----------------------------------------------------------------------------------------------------------------------------------- |
| ATIF files, such as Harbor's `trajectory.json`                       | Submit them as they are.                                                                                                            |
| Traces in LangSmith, Braintrust, Raindrop, Langfuse, or another tool | Export them, convert to ATIF, then submit.                                                                                          |
| OpenInference or OpenTelemetry spans                                 | Submit them directly. They are stored as spans, not trajectories, so convert to ATIF if you want to evaluate them as conversations. |

#### Export your traces

Use your tool's export feature or API. For each trace, build one list of OpenAI-style chat messages (`role`, `content`, `tool_calls`, `tool_call_id`). In most exports, the last model call's input messages plus its output message hold the whole conversation.

#### Convert each trace to ATIF

This function turns one message list into an ATIF document that passes Sikaru's validation:

```python
import json


def to_atif(messages, *, session_id, agent_name, agent_version, model_name=None):
    """Convert OpenAI-style chat messages from an exported trace into an ATIF document."""
    steps = []
    for msg in messages:
        role = msg["role"]
        if role == "tool":
            # A tool result belongs to the agent step that made the call.
            observation = steps[-1].setdefault("observation", {"results": []})
            observation["results"].append(
                {"source_call_id": msg["tool_call_id"], "content": msg.get("content") or ""}
            )
            continue
        step = {
            "step_id": len(steps) + 1,
            "source": {"user": "user", "system": "system"}.get(role, "agent"),
            "message": msg.get("content") or "",
        }
        if role == "assistant":
            if model_name:
                step["model_name"] = model_name
            calls = msg.get("tool_calls") or []
            if calls:
                step["tool_calls"] = [
                    {
                        "tool_call_id": call["id"],
                        "function_name": call["function"]["name"],
                        "arguments": json.loads(call["function"]["arguments"] or "{}"),
                    }
                    for call in calls
                ]
        steps.append(step)
    return {
        "schema_version": "ATIF-v1.7",
        "session_id": session_id,
        "agent": {"name": agent_name, "version": agent_version},
        "steps": steps,
    }
```

Adapt the field names if your export uses a different message shape. Harbor also publishes Pydantic models for building ATIF in `harbor.models.trajectories`.

#### Submit the trajectories

Send each document with the `harbor.atif.v1` format. Use the source trace ID in the idempotency key so a retry never creates a duplicate:

```python
from sikaru_api import TraceMetadata

for trace in exported_traces:
    receipt = client.trace_streams.stream_openinference_spans(
        format="harbor.atif.v1",
        trajectory=to_atif(
            trace["messages"],
            session_id=trace["id"],
            agent_name="support-agent",
            agent_version="2026-10-01",
        ),
        dataset="historical",
        metadata=TraceMetadata(account_id="customer-42", project_id=project, source="langsmith-export"),
        idempotency_key=f"historical-{trace['id']}",
    )
```

> **Note**
>
> The service validates each document and rejects invalid ones. Fix the converter, then resubmit with the same idempotency key.

## Submit traces

The generated trace method accepts declared wire formats directly. It does not provide a local Experience builder, vendor converter, trajectory class, or agent facade. Normalize source data in your application before you send it. Server-side normalization stays part of the private service.

```python
import json
from pathlib import Path
from sikaru_api import TraceMetadata

trajectory = json.loads(Path("trajectory.json").read_text())
receipt = client.trace_streams.stream_openinference_spans(
    format="harbor.atif.v1", trajectory=trajectory, dataset="baseline",
    metadata=TraceMetadata(account_id="customer-42", project_id=project, source="my-agent"),
    idempotency_key="baseline-trace-001",
)
```

| Format                  | Payload field                                           |
| ----------------------- | ------------------------------------------------------- |
| `harbor.atif.v1`        | `trajectory` (ATIF documents, validated by the service) |
| `openinference.v1`      | `spans`, using generated `OpenInferenceSpan` values     |
| `opentelemetry.otlp.v1` | `resource_spans`                                        |

Reuse trace idempotency keys only for identical batches.

> **Note**
>
> A trace ingest acknowledgement does not prove that downstream evaluation or improvement completed.

## Read a managed run's trajectory

Use a project API key with `runs:read` to read retained execution structure and measured token usage:

```python
evidence = client.runs.get_trajectory(project, run_id)
trajectory = evidence["trajectory"]
```

The response includes `runId`, `harnessVersionId`, `status`, and an ATIF 1.8 `trajectory`.

* Private messages, reasoning, tool payloads, and provider metadata are omitted and marked in `trajectory.extra`.
* Embedded subagent trajectories keep their own token totals. Root totals exclude those children.
* Missing metrics mean unknown usage, not zero.
* This export does not include model pricing.

The endpoint reads a committed snapshot. Active or interrupted runs may have partial evidence.

| Response                            | Meaning                                                   | What to do                                   |
| ----------------------------------- | --------------------------------------------------------- | -------------------------------------------- |
| 404 for an authorized, existing run | No retained trajectory is available                       | Treat evidence as unavailable, not empty.    |
| 404 otherwise                       | The run may be missing or inaccessible                    | Check the run ID and project.                |
| Service error                       | The read failed; the trajectory is not necessarily absent | Retry the read. Do not submit the run again. |

The Python method requires a client generated from the updated API contract and a service that supports this endpoint.

## Find runs in the platform

Open your project and go to **Conversations**. Managed runs appear in their own history. Open a run to inspect its retained trajectory and measured usage. Use **Show older managed runs** to continue through earlier runs. Agent pages also scope managed history to that agent.

Managed history and submitted traces are separate views:

* A managed run does not need to be uploaded again to appear in managed history.
* Submitted-trace counts can be zero while managed runs exist.
* Do not add their totals together as a unique-run count.

> **Warning**
>
> A visible run confirms that work was admitted, not that a complete trajectory was retained. Run status, trajectory availability, and measurement completeness are separate. Refresh active runs to read newer retained evidence. Never read an unavailable trajectory or failed read as zero steps or zero usage.

## Next steps

#### [Evaluations](/improve/evaluations)

Apply a rubric to retained traces and review results.

#### [Quickstart](/get-started/quickstart)

Set up the authenticated client used in these examples.

#### [Troubleshooting](/help/troubleshooting)

Diagnose a run that has no trajectory.