Metadata-Version: 2.4
Name: trajectory-sdk
Version: 0.6.26
Summary: Generated Trajectory API client and high-level workflows
Requires-Python: >=3.11
Description-Content-Type: text/markdown
License-File: LICENSE
Requires-Dist: httpx>=0.28.1
Requires-Dist: pathspec>=0.12
Requires-Dist: pydantic>=2.0
Requires-Dist: tqdm>=4.67
Dynamic: license-file

# Trajectory SDK

Generated Python client for the Trajectory API, with high-level workflows for uploading
trajectories, telemetry, and runtime-backed benchmarks.

## Install

```bash
pip install trajectory-sdk
```

## Quick start

Set `TRAJECTORY_API_KEY`, then create a client:

```python
from trajectory._client import Client

client = Client()
benchmarks = client.benchmarks.list(limit=10)
```

For task execution, follow the [native benchmark harness](#native-benchmark-harness) and
[registration and image builds](#registration-and-image-builds) examples below.

SDK-owned HTTP clients use a 600-second read/write/pool timeout and a 5-second connection timeout.
Supplying `http_client=` inherits that client's timeouts, including a bare HTTPX client's 5-second
default. An explicit `Client(timeout=...)` overrides the supplied client; a resource method's
`timeout=` overrides that request. `timeout=None` disables timeouts. Retry counts are unchanged.

The generated resource methods map directly to the public HTTP API. Benchmark submission
and file-upload workflows are available from `trajectory.lib.benchmarks` and `trajectory.lib.files`.
Detailed authored guides are not currently available without documentation access; the core
workflow is included here.

## Native benchmark harness

A task's `run_command` starts your program inside its runtime. Supply task input through
`TaskSpec.env_vars`, such as `QUESTION` and `EXPECTED_ANSWER` below. `input_messages` is stored
task metadata; the native runtime does not send it to the model or to your program's stdin.
Your harness reads the input, calls the model, grades its answer, records the reward, and
completes the trajectory before exiting. The SDK has no `trajectory` CLI executable.

Managed runs supply `TRAJECTORY_API_KEY` and `TRAJECTORY_BASE_URL`.
Use `Client()` without embedding credentials in the image. `trajectories.create()` returns the
precreated trajectory ID; pass that ID explicitly to model, reward, and completion calls. Model
requests use the same client and API key, while the Backend resolves the model endpoint.

```python harness.py
import os

from trajectory._client import Client

client = Client()
tid = client.trajectories.create().tid
try:
    response = client.chat.completions.create(
        model="policy",
        messages=[{"role": "user", "content": os.environ["QUESTION"]}],
        x_trajectory_id=tid,
    )
    answer = response.choices[0].message.content or ""
    reward = float(answer.strip() == os.environ["EXPECTED_ANSWER"])
    client.trajectories.log_reward(tid, reward_id="answer", name="accuracy", value=reward)
except Exception as error:
    try:
        client.trajectories.complete(tid, termination_reason="ERROR")
    except Exception as completion_error:
        error.add_note(f"Error completion failed: {type(completion_error).__name__}")
    raise
else:
    client.trajectories.complete(tid, termination_reason="ENV_DONE")
```

Package this file and its dependencies in your Dockerfile. For example, copying it to
`/app/harness.py` makes `python /app/harness.py` the task's `run_command`. A wrong answer gets
reward zero and `ENV_DONE`; a model or grader error remains an error, even if reporting its
completion also fails. Process exit alone does not complete a trajectory.

## Registration and image builds

Define a `BenchmarkSpec` named `benchmark` with your tasks, distinct `train`/`test` splits,
and a runtime such as `DockerfileBuild("Dockerfile")`. With the Dockerfile and harness under
the local `benchmark/` directory, register it and build its runtime:

```python registration.py
from pathlib import Path

from trajectory._client import Client
from trajectory.lib.benchmarks import push, wait_for_benchmark_images

client = Client()
result = push(client, benchmark, root=Path("benchmark"))
images = wait_for_benchmark_images(client, result.bench_id)
```

`push()` uploads files and waits for task registration; it does **not** start image builds.
`wait_for_benchmark_images()` first calls `client.benchmarks.images.build(bench_id)` and then
polls for completion. `client.benchmarks.images.list(bench_id)` only reads status. Once the
runtime is ready, use the benchmark ID to start evaluation or training; uploading does not
execute the tasks. Run this registration code with your organization's API key.

## Development

```bash
uv run pytest
uv run ruff check .
uv run ruff format --check .
```

The generated client lives in `src/trajectory/`. Handwritten workflows live in
`src/trajectory/lib/`, and tests live in `tests/`.

## License

[Apache 2.0](LICENSE)
