Session Management
tj.init()
Configure provider imports and legacy SDK helpers. The generated API client and the
trajectory.lib upload workflows do not require tj.init().
Harness telemetry
During a live rollout, the harness writes events, rewards, and completion through the generated client. These calls require an API key and an in-progresstrajectory_id.
event_id and reward_id dedupe retries. New events or rewards after the trajectory is terminal
are rejected. complete() is idempotent for an already-completed trajectory.
Benchmarks
Runtime-backed benchmark authoring
Author a generatedBenchmarkSpec, then use trajectory.lib.benchmarks.submit() to upload
artifacts and bounded task parts. It returns a BenchmarkOperation after the upload is accepted;
registration and optional runtime image builds continue on the server. This workflow requires
the asynchronous ingestion backend to be deployed and enabled.
ImageRef() accepts an immutable registry/image@sha256:... reference, a bpt_ blueprint ID,
or an im- Modal image ID. RuntimeRef() refers to an existing organization runtime.
DockerfileBuild() resolves the Dockerfile relative to root; its parent directory is the build
context, and root-level .dockerignore rules determine which files are uploaded. Unresolved
runtimes, mutable image references, and empty run commands are rejected before session creation.
The runtime owns environment interaction and reports rewards through log_reward.
trajectory.lib.benchmarks.submit()
To add or update tasks, submit another manifest with
bench_id set to the existing benchmark ID.
Each task name is its stable key. Async append uploads the supplied artifacts and skips unchanged
task writes on the server. Each submission has its own operation and idempotency key.
Operation status and results
get_operation(client, operation_id) returns a handle without making a request.
operation.refresh() retrieves a BenchmarkIngestionOperationStatus.
operation.result(timeout=1800, poll_interval=2) waits for a terminal status and returns a
BenchmarkOperationResult. A TimeoutError stops local waiting; server processing continues.
Use timeout=None to wait without a deadline.
result.status.ready is true only when builds were explicitly requested, the operation succeeded,
all submitted tasks registered, and every required runtime is ready. Registration-only success
leaves ready false. result.tasks(), runtimes(), failures(), and affected_tasks(runtime_id)
iterate paginated results. Failures are typed BenchmarkIngestionFailure objects. Pass
cursor=failure.affected_tasks_cursor to affected_tasks() to start at a supplied failure cursor.
Partial failure raises BenchmarkPartialFailureError; a failed operation raises
BenchmarkOperationError. Both are available from trajectory.lib.benchmark_operations and
expose partial_result and failures. Successful registrations and runtimes remain available.
See benchmark submission operations for failure handling and limits.
Blocking and prepared-upload helpers
benchmarks.push(client, manifest, root=Path(".")) and
benchmarks.append(client, bench_id, manifest, root=Path(".")) submit an operation and wait for
registration. They preserve their successful IngestBenchmarkResponse and
BenchmarkTaskAppendSummary return types, respectively, and raise on failed or partially failed
operations. Use submit() for image builds, custom waiting, and reconnection.
trajectory.lib.upload_benchmark() and trajectory.lib.append_benchmark_tasks() accept a prepared
BenchmarkSpec or mapping and UploadFile objects. Small requests retain the legacy registration
and append flow; large manifests or artifact maps automatically use ingestion operations and wait
for completion. The small append path diffs tasks before uploading and commits changed tasks in
batches. Its batch limits do not control task parts on the ingestion path.
Successful task writes remain if a later batch or ingestion step fails. Re-running the same input
skips unchanged tasks. Async ingestion does not perform the legacy unchanged-artifact upload
optimization.
Trace Import
tj.list_conversations()
List available conversations from the configured provider.
tj.import_conversations()
Import conversations and return one Trajectory per conversation.
tj.transform()
Transform raw provider data into a Trajectory without fetching. Useful for custom pipelines
that already have the source payload.
Building Trajectories
See Bring Your Own Data for the full walkthrough.tj.build_trajectory_from_messages()
Build a Trajectory from a flat list of Message objects. Cumulative steps, tool-call metrics,
and telemetry fields (content hash, idempotency key) are computed automatically.
task_metadata recognizes num_turns, total_tokens, total_cost, and completion_tokens;
other keys are ignored.
tj.build_trajectory_from_parsed()
Build a Trajectory from a ParsedConversation. Provider subclasses delegate to this so the
ParsedConversation → Trajectory logic has a single implementation.
tj.build_reward_from_scalar()
Build a single-component Reward from a numeric score. scaled_value is normalized to [0, 1]
using score_range.
Message Recipes
Adapters from common third-party message formats intoMessage objects. All recipes fail loud
on unexpected input — see Bring Your Own Data for examples.
Helpers
Trace Workflow
See Linking Events to Trajectories for the workflow.tj.start_trace()
Create a TraceContext for buffering correlated telemetry and trajectory data.
trace_id is omitted, the SDK generates a UUID4 hex string.
TraceContext
trace.event(event_type, properties=None, *, user_id=None, trajectory_id=None, source="sdk", metadata=None) -> TelemetryEvent
Append a TelemetryEvent to trace.events with trace_id and session_id set to trace.trace_id.
trace.build_trajectory(messages, *, data_source, conversation_id=None, ...) -> Trajectory
Convenience wrapper around build_trajectory_from_messages that injects trace_id and defaults
conversation_id to trace_id. Accepts the same kwargs as build_trajectory_from_messages (minus
trace_id).
upload_trace()
Upload trajectories, stamp matching events with returned trajectory_id values, then ingest
the events. Import this workflow from trajectory.lib.
trace_id. The result contains typed upload
and telemetry summaries plus the event mappings as sent. A trajectory failure raises
PartialUploadError; a missing correlation ID raises TraceCorrelationError. In either case,
no telemetry is ingested. TraceCorrelationError is also raised when the upload response omits
an item for any submitted trajectory, even if the call contained no events.
Events are serialized before trajectory upload so their trace_id values can be inspected. An
unsupported JSON value raises TypeError before any request is made. Events that serialize but
fail telemetry field validation are returned in result.telemetry.failures; valid events in the
same call are still ingested and the uploaded trajectories remain stored.
Upload & Ingest
upload_trajectories()
Filter empty trajectories, batch valid trajectories, and upload through the generated client.
The workflow uses up to eight workers, batches at 1,000 items or 128 MiB, and gives each upload
request a 120-second timeout.
ingest_events()
Validate telemetry locally, split valid events into bounded chunks, and ingest them through the
generated client.
ingested, total skipped, and structured failures. Local validation
requires a non-empty event_type, a non-empty session_id, mapping properties, and an ISO 8601
timestamp. Events that are not mappings or contain unsupported JSON values are also returned as
structured failures. Failure indices are mapped back to the original input list.
chunk_size must be an integer greater than zero; other types raise TypeError, while zero and
negative values raise ValueError.
tj.save()
Save trajectories to JSON files on disk. Each file is named {output_dir}/{conversation_id}.json.