prime-traces 0.0.1__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- prime_traces-0.0.1/.gitignore +27 -0
- prime_traces-0.0.1/LICENSE +21 -0
- prime_traces-0.0.1/PKG-INFO +205 -0
- prime_traces-0.0.1/README.md +176 -0
- prime_traces-0.0.1/examples/basic_usage.py +108 -0
- prime_traces-0.0.1/pyproject.toml +60 -0
- prime_traces-0.0.1/src/prime_traces/__init__.py +95 -0
- prime_traces-0.0.1/src/prime_traces/batching.py +136 -0
- prime_traces-0.0.1/src/prime_traces/core/__init__.py +8 -0
- prime_traces-0.0.1/src/prime_traces/core/client.py +433 -0
- prime_traces-0.0.1/src/prime_traces/core/config.py +85 -0
- prime_traces-0.0.1/src/prime_traces/exceptions.py +144 -0
- prime_traces-0.0.1/src/prime_traces/models.py +234 -0
- prime_traces-0.0.1/src/prime_traces/py.typed +0 -0
- prime_traces-0.0.1/src/prime_traces/traces.py +522 -0
- prime_traces-0.0.1/tests/conftest.py +59 -0
- prime_traces-0.0.1/tests/test_batching.py +112 -0
- prime_traces-0.0.1/tests/test_client_config.py +65 -0
- prime_traces-0.0.1/tests/test_reads.py +725 -0
- prime_traces-0.0.1/tests/test_sdk_config.py +53 -0
- prime_traces-0.0.1/tests/test_upload_wire.py +392 -0
|
@@ -0,0 +1,27 @@
|
|
|
1
|
+
__pycache__/
|
|
2
|
+
*.py[cod]
|
|
3
|
+
*$py.class
|
|
4
|
+
*.so
|
|
5
|
+
.Python
|
|
6
|
+
build/
|
|
7
|
+
develop-eggs/
|
|
8
|
+
dist/
|
|
9
|
+
downloads/
|
|
10
|
+
eggs/
|
|
11
|
+
.eggs/
|
|
12
|
+
lib/
|
|
13
|
+
lib64/
|
|
14
|
+
parts/
|
|
15
|
+
sdist/
|
|
16
|
+
var/
|
|
17
|
+
wheels/
|
|
18
|
+
*.egg-info/
|
|
19
|
+
.installed.cfg
|
|
20
|
+
*.egg
|
|
21
|
+
.env
|
|
22
|
+
venv/
|
|
23
|
+
.venv/
|
|
24
|
+
ENV/
|
|
25
|
+
test_env/
|
|
26
|
+
.DS_Store
|
|
27
|
+
.claude/worktrees/
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2025 Prime Intellect
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,205 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: prime-traces
|
|
3
|
+
Version: 0.0.1
|
|
4
|
+
Summary: Prime Intellect Traces SDK - Upload, query and export traces
|
|
5
|
+
Project-URL: Homepage, https://github.com/PrimeIntellect-ai/prime
|
|
6
|
+
Project-URL: Documentation, https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-traces
|
|
7
|
+
Project-URL: Repository, https://github.com/PrimeIntellect-ai/prime.git
|
|
8
|
+
Author-email: Prime Intellect <contact@primeintellect.ai>
|
|
9
|
+
License: MIT
|
|
10
|
+
License-File: LICENSE
|
|
11
|
+
Keywords: evals,observability,rollouts,traces
|
|
12
|
+
Classifier: Development Status :: 3 - Alpha
|
|
13
|
+
Classifier: Intended Audience :: Developers
|
|
14
|
+
Classifier: License :: OSI Approved :: MIT License
|
|
15
|
+
Classifier: Operating System :: OS Independent
|
|
16
|
+
Classifier: Programming Language :: Python :: 3
|
|
17
|
+
Classifier: Programming Language :: Python :: 3.10
|
|
18
|
+
Classifier: Programming Language :: Python :: 3.11
|
|
19
|
+
Classifier: Programming Language :: Python :: 3.12
|
|
20
|
+
Classifier: Topic :: Scientific/Engineering :: Artificial Intelligence
|
|
21
|
+
Classifier: Topic :: Software Development :: Libraries :: Python Modules
|
|
22
|
+
Requires-Python: >=3.10
|
|
23
|
+
Requires-Dist: httpx>=0.25.0
|
|
24
|
+
Requires-Dist: pydantic>=2.0.0
|
|
25
|
+
Provides-Extra: dev
|
|
26
|
+
Requires-Dist: pytest>=7.0.0; extra == 'dev'
|
|
27
|
+
Requires-Dist: ruff>=0.13.1; extra == 'dev'
|
|
28
|
+
Description-Content-Type: text/markdown
|
|
29
|
+
|
|
30
|
+
# Prime Traces SDK
|
|
31
|
+
|
|
32
|
+
Upload and query training, evaluation and inference traces through the Prime
|
|
33
|
+
Traces service.
|
|
34
|
+
|
|
35
|
+
## Features
|
|
36
|
+
|
|
37
|
+
- **Content-addressed uploads** - Batches are identified by the SHA-256 of
|
|
38
|
+
their exact bytes, so interrupted uploads are safe to rerun and never store
|
|
39
|
+
twice
|
|
40
|
+
- **Deterministic batching** - JSONL files are split at byte thresholds without
|
|
41
|
+
rewriting a single line
|
|
42
|
+
- **Typed reads** - Cursor-paginated summaries over extracted columns and raw
|
|
43
|
+
document retrieval
|
|
44
|
+
- **Type-safe** - Full type hints and Pydantic models
|
|
45
|
+
- **No CLI dependencies** - Pure SDK, usable in producers and services
|
|
46
|
+
|
|
47
|
+
## Installation
|
|
48
|
+
|
|
49
|
+
```bash
|
|
50
|
+
uv add prime-traces
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
or with pip:
|
|
54
|
+
|
|
55
|
+
```bash
|
|
56
|
+
pip install prime-traces
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Quick Start
|
|
60
|
+
|
|
61
|
+
### Upload from memory
|
|
62
|
+
|
|
63
|
+
`upload_records` accepts JSON-compatible mappings as well as objects exposing
|
|
64
|
+
`to_record()`. Verifiers `Trace` / `Episode` and prime-rl `Rollout` objects
|
|
65
|
+
provide that method, so producers can upload completed records without writing
|
|
66
|
+
an intermediate JSONL file:
|
|
67
|
+
|
|
68
|
+
```python
|
|
69
|
+
from prime_traces import LineFormat, TracesClient
|
|
70
|
+
|
|
71
|
+
client = TracesClient() # PRIME_API_KEY / ~/.prime/config.json
|
|
72
|
+
|
|
73
|
+
# Iterable[vf.Trace] or Iterable[prime_rl.orchestrator.types.Rollout]
|
|
74
|
+
receipts = client.upload_records(
|
|
75
|
+
traces,
|
|
76
|
+
context={"source": "prime-rl", "run_id": "run_9f3k2m"},
|
|
77
|
+
)
|
|
78
|
+
|
|
79
|
+
# Iterable[vf.Episode] for multi-agent runs
|
|
80
|
+
receipts = client.upload_records(
|
|
81
|
+
episodes,
|
|
82
|
+
line_format=LineFormat.EPISODE,
|
|
83
|
+
context={"source": "verifiers"},
|
|
84
|
+
)
|
|
85
|
+
```
|
|
86
|
+
|
|
87
|
+
Records are serialized lazily and fed into bounded batches, so this neither
|
|
88
|
+
buffers the complete iterable nor round-trips through the filesystem. Callers
|
|
89
|
+
that already have encoded JSONL bytes can use `upload_lines` directly.
|
|
90
|
+
|
|
91
|
+
### Upload a completed JSONL file
|
|
92
|
+
|
|
93
|
+
```python
|
|
94
|
+
from prime_traces import TracesClient, LineFormat
|
|
95
|
+
|
|
96
|
+
client = TracesClient() # PRIME_API_KEY / ~/.prime/config.json
|
|
97
|
+
|
|
98
|
+
# One bare Verifiers trace per line:
|
|
99
|
+
receipts = client.upload_file("traces.jsonl", context={"source": "hosted_eval"})
|
|
100
|
+
|
|
101
|
+
# One complete episode per line (multi-agent runs):
|
|
102
|
+
receipts = client.upload_file(
|
|
103
|
+
"episodes.jsonl",
|
|
104
|
+
line_format=LineFormat.EPISODE,
|
|
105
|
+
context={"source": "hosted_eval", "suite_commit": "a1f39c2"},
|
|
106
|
+
)
|
|
107
|
+
```
|
|
108
|
+
|
|
109
|
+
Uploads are content-addressed: each request is identified by the SHA-256 of its
|
|
110
|
+
exact uncompressed JSONL bytes and sent with an `Idempotency-Key`. Rerunning an
|
|
111
|
+
interrupted upload re-reads the file, reproduces the same bytes and keys, and
|
|
112
|
+
the service replays committed receipts without storing anything twice. A 400
|
|
113
|
+
rejection stops the upload with a bounded error code (`ErrorCode`); 429/503 and
|
|
114
|
+
gateway 502/504 are retried with the same bytes, honoring `Retry-After`.
|
|
115
|
+
|
|
116
|
+
## Query
|
|
117
|
+
|
|
118
|
+
```python
|
|
119
|
+
page = client.list(run_id="run_9f3k2m", reward_min=0.9, has_error=False)
|
|
120
|
+
for summary in page.items:
|
|
121
|
+
print(summary.trace_id, summary.score)
|
|
122
|
+
|
|
123
|
+
for summary in client.iter(task_id="tb2-0187"): # paginates for you
|
|
124
|
+
...
|
|
125
|
+
|
|
126
|
+
summary = client.get("8d3f1a2b...")
|
|
127
|
+
raw = client.get_raw("8d3f1a2b...") # exact stored trace document
|
|
128
|
+
client.download_raw("8d3f1a2b...", "t.json") # streamed, for large traces
|
|
129
|
+
|
|
130
|
+
client.delete("8d3f1a2b...") # NotFoundError if the owner has no such trace
|
|
131
|
+
client.delete_run("run_9f3k2m") # one mutation, synchronous, no job handle
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
Deletion is not a no-op on absent rows: the service checks existence first and
|
|
135
|
+
answers 404, so repeating a delete that already succeeded raises
|
|
136
|
+
`NotFoundError`. (The design docs specify it as idempotent; this tracks the
|
|
137
|
+
service as built.) Failures known to occur before delivery, 429 responses, and
|
|
138
|
+
service-coded 503 refusals are retried. Ambiguous response-path failures and
|
|
139
|
+
gateway 502/503/504 responses are surfaced as `AmbiguousDeleteError` without
|
|
140
|
+
replaying the deletion, because a retry could delete a trace written after the
|
|
141
|
+
first request.
|
|
142
|
+
|
|
143
|
+
Trace point reads/deletes and episode point/member reads currently reject IDs
|
|
144
|
+
containing `/`. ASGI decodes an encoded slash before matching the service's
|
|
145
|
+
`/{resource_id}` routes, so those IDs cannot be addressed until the service
|
|
146
|
+
accepts path-valued route parameters.
|
|
147
|
+
|
|
148
|
+
Episodes are read-only resources:
|
|
149
|
+
|
|
150
|
+
```python
|
|
151
|
+
page = client.list_episodes(
|
|
152
|
+
run_id="run_9f3k2m",
|
|
153
|
+
environment_id="terminal-bench-2",
|
|
154
|
+
)
|
|
155
|
+
for episode in page.items:
|
|
156
|
+
print(episode.episode_id, episode.outcome)
|
|
157
|
+
|
|
158
|
+
if page.items:
|
|
159
|
+
episode_id = page.items[0].episode_id
|
|
160
|
+
detail = client.get_episode(episode_id) # + member aggregate under .traces
|
|
161
|
+
print(detail.error.type, detail.traces.trace_count)
|
|
162
|
+
|
|
163
|
+
# Member trace summaries use the trace filters (except sort) and pagination.
|
|
164
|
+
client.list_episode_traces(episode_id, has_error=True)
|
|
165
|
+
```
|
|
166
|
+
|
|
167
|
+
Response shapes mirror the service's pinned models: pages are
|
|
168
|
+
`{items, next_cursor}`, a trace summary nests `model` / `score` / `execution`,
|
|
169
|
+
an episode nests `error` and (on point lookup) the member-trace aggregate
|
|
170
|
+
under `traces`, and unrecorded fields come back as `null`.
|
|
171
|
+
|
|
172
|
+
## Configuration
|
|
173
|
+
|
|
174
|
+
| Source | Meaning |
|
|
175
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
176
|
+
| `PRIME_API_KEY` | Platform API token (needs `traces:read` / `traces:write` scopes) |
|
|
177
|
+
| `PRIME_TEAM_ID` | Optional team context, sent as `X-Prime-Team-ID` |
|
|
178
|
+
| `PRIME_TRACES_URL` | Base URL of the Prime Traces service; defaults to the platform API base URL. For the service's local compose stack: `http://localhost:8083` |
|
|
179
|
+
| `~/.prime/config.json` | Shared prime CLI config (`api_key`, `team_id`, `traces_url`) |
|
|
180
|
+
|
|
181
|
+
## Not implemented yet (open v0 contract decisions)
|
|
182
|
+
|
|
183
|
+
- Exports, in any form. The service publishes `GET /traces/export` and the two
|
|
184
|
+
job routes, but all three handlers raise `NotImplementedError` — answered as
|
|
185
|
+
500, not the 501 they document — and the streaming route declares no query
|
|
186
|
+
parameters, so there is no filter vocabulary to bind to. Wrapping it now
|
|
187
|
+
would ship a method that cannot succeed.
|
|
188
|
+
- `/search` and free-text queries — deferred with the `trace_components`
|
|
189
|
+
projection.
|
|
190
|
+
- Typed dot-path predicates (`traces.query`) — needs the server-side field
|
|
191
|
+
registry.
|
|
192
|
+
- An async client — the other prime SDKs ship sync/async pairs, and the main
|
|
193
|
+
producers (verifiers, prime-rl) are async; add once the sync surface
|
|
194
|
+
settles rather than freezing a duplicated API now.
|
|
195
|
+
|
|
196
|
+
## Documentation
|
|
197
|
+
|
|
198
|
+
For detailed documentation, visit the
|
|
199
|
+
[Prime Traces SDK documentation](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-traces).
|
|
200
|
+
|
|
201
|
+
## Related Packages
|
|
202
|
+
|
|
203
|
+
- [prime](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime) - Prime CLI (`prime traces ...` commands)
|
|
204
|
+
- [prime-sandboxes](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-sandboxes) - Sandboxes SDK
|
|
205
|
+
- [prime-evals](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-evals) - Evals SDK
|
|
@@ -0,0 +1,176 @@
|
|
|
1
|
+
# Prime Traces SDK
|
|
2
|
+
|
|
3
|
+
Upload and query training, evaluation and inference traces through the Prime
|
|
4
|
+
Traces service.
|
|
5
|
+
|
|
6
|
+
## Features
|
|
7
|
+
|
|
8
|
+
- **Content-addressed uploads** - Batches are identified by the SHA-256 of
|
|
9
|
+
their exact bytes, so interrupted uploads are safe to rerun and never store
|
|
10
|
+
twice
|
|
11
|
+
- **Deterministic batching** - JSONL files are split at byte thresholds without
|
|
12
|
+
rewriting a single line
|
|
13
|
+
- **Typed reads** - Cursor-paginated summaries over extracted columns and raw
|
|
14
|
+
document retrieval
|
|
15
|
+
- **Type-safe** - Full type hints and Pydantic models
|
|
16
|
+
- **No CLI dependencies** - Pure SDK, usable in producers and services
|
|
17
|
+
|
|
18
|
+
## Installation
|
|
19
|
+
|
|
20
|
+
```bash
|
|
21
|
+
uv add prime-traces
|
|
22
|
+
```
|
|
23
|
+
|
|
24
|
+
or with pip:
|
|
25
|
+
|
|
26
|
+
```bash
|
|
27
|
+
pip install prime-traces
|
|
28
|
+
```
|
|
29
|
+
|
|
30
|
+
## Quick Start
|
|
31
|
+
|
|
32
|
+
### Upload from memory
|
|
33
|
+
|
|
34
|
+
`upload_records` accepts JSON-compatible mappings as well as objects exposing
|
|
35
|
+
`to_record()`. Verifiers `Trace` / `Episode` and prime-rl `Rollout` objects
|
|
36
|
+
provide that method, so producers can upload completed records without writing
|
|
37
|
+
an intermediate JSONL file:
|
|
38
|
+
|
|
39
|
+
```python
|
|
40
|
+
from prime_traces import LineFormat, TracesClient
|
|
41
|
+
|
|
42
|
+
client = TracesClient() # PRIME_API_KEY / ~/.prime/config.json
|
|
43
|
+
|
|
44
|
+
# Iterable[vf.Trace] or Iterable[prime_rl.orchestrator.types.Rollout]
|
|
45
|
+
receipts = client.upload_records(
|
|
46
|
+
traces,
|
|
47
|
+
context={"source": "prime-rl", "run_id": "run_9f3k2m"},
|
|
48
|
+
)
|
|
49
|
+
|
|
50
|
+
# Iterable[vf.Episode] for multi-agent runs
|
|
51
|
+
receipts = client.upload_records(
|
|
52
|
+
episodes,
|
|
53
|
+
line_format=LineFormat.EPISODE,
|
|
54
|
+
context={"source": "verifiers"},
|
|
55
|
+
)
|
|
56
|
+
```
|
|
57
|
+
|
|
58
|
+
Records are serialized lazily and fed into bounded batches, so this neither
|
|
59
|
+
buffers the complete iterable nor round-trips through the filesystem. Callers
|
|
60
|
+
that already have encoded JSONL bytes can use `upload_lines` directly.
|
|
61
|
+
|
|
62
|
+
### Upload a completed JSONL file
|
|
63
|
+
|
|
64
|
+
```python
|
|
65
|
+
from prime_traces import TracesClient, LineFormat
|
|
66
|
+
|
|
67
|
+
client = TracesClient() # PRIME_API_KEY / ~/.prime/config.json
|
|
68
|
+
|
|
69
|
+
# One bare Verifiers trace per line:
|
|
70
|
+
receipts = client.upload_file("traces.jsonl", context={"source": "hosted_eval"})
|
|
71
|
+
|
|
72
|
+
# One complete episode per line (multi-agent runs):
|
|
73
|
+
receipts = client.upload_file(
|
|
74
|
+
"episodes.jsonl",
|
|
75
|
+
line_format=LineFormat.EPISODE,
|
|
76
|
+
context={"source": "hosted_eval", "suite_commit": "a1f39c2"},
|
|
77
|
+
)
|
|
78
|
+
```
|
|
79
|
+
|
|
80
|
+
Uploads are content-addressed: each request is identified by the SHA-256 of its
|
|
81
|
+
exact uncompressed JSONL bytes and sent with an `Idempotency-Key`. Rerunning an
|
|
82
|
+
interrupted upload re-reads the file, reproduces the same bytes and keys, and
|
|
83
|
+
the service replays committed receipts without storing anything twice. A 400
|
|
84
|
+
rejection stops the upload with a bounded error code (`ErrorCode`); 429/503 and
|
|
85
|
+
gateway 502/504 are retried with the same bytes, honoring `Retry-After`.
|
|
86
|
+
|
|
87
|
+
## Query
|
|
88
|
+
|
|
89
|
+
```python
|
|
90
|
+
page = client.list(run_id="run_9f3k2m", reward_min=0.9, has_error=False)
|
|
91
|
+
for summary in page.items:
|
|
92
|
+
print(summary.trace_id, summary.score)
|
|
93
|
+
|
|
94
|
+
for summary in client.iter(task_id="tb2-0187"): # paginates for you
|
|
95
|
+
...
|
|
96
|
+
|
|
97
|
+
summary = client.get("8d3f1a2b...")
|
|
98
|
+
raw = client.get_raw("8d3f1a2b...") # exact stored trace document
|
|
99
|
+
client.download_raw("8d3f1a2b...", "t.json") # streamed, for large traces
|
|
100
|
+
|
|
101
|
+
client.delete("8d3f1a2b...") # NotFoundError if the owner has no such trace
|
|
102
|
+
client.delete_run("run_9f3k2m") # one mutation, synchronous, no job handle
|
|
103
|
+
```
|
|
104
|
+
|
|
105
|
+
Deletion is not a no-op on absent rows: the service checks existence first and
|
|
106
|
+
answers 404, so repeating a delete that already succeeded raises
|
|
107
|
+
`NotFoundError`. (The design docs specify it as idempotent; this tracks the
|
|
108
|
+
service as built.) Failures known to occur before delivery, 429 responses, and
|
|
109
|
+
service-coded 503 refusals are retried. Ambiguous response-path failures and
|
|
110
|
+
gateway 502/503/504 responses are surfaced as `AmbiguousDeleteError` without
|
|
111
|
+
replaying the deletion, because a retry could delete a trace written after the
|
|
112
|
+
first request.
|
|
113
|
+
|
|
114
|
+
Trace point reads/deletes and episode point/member reads currently reject IDs
|
|
115
|
+
containing `/`. ASGI decodes an encoded slash before matching the service's
|
|
116
|
+
`/{resource_id}` routes, so those IDs cannot be addressed until the service
|
|
117
|
+
accepts path-valued route parameters.
|
|
118
|
+
|
|
119
|
+
Episodes are read-only resources:
|
|
120
|
+
|
|
121
|
+
```python
|
|
122
|
+
page = client.list_episodes(
|
|
123
|
+
run_id="run_9f3k2m",
|
|
124
|
+
environment_id="terminal-bench-2",
|
|
125
|
+
)
|
|
126
|
+
for episode in page.items:
|
|
127
|
+
print(episode.episode_id, episode.outcome)
|
|
128
|
+
|
|
129
|
+
if page.items:
|
|
130
|
+
episode_id = page.items[0].episode_id
|
|
131
|
+
detail = client.get_episode(episode_id) # + member aggregate under .traces
|
|
132
|
+
print(detail.error.type, detail.traces.trace_count)
|
|
133
|
+
|
|
134
|
+
# Member trace summaries use the trace filters (except sort) and pagination.
|
|
135
|
+
client.list_episode_traces(episode_id, has_error=True)
|
|
136
|
+
```
|
|
137
|
+
|
|
138
|
+
Response shapes mirror the service's pinned models: pages are
|
|
139
|
+
`{items, next_cursor}`, a trace summary nests `model` / `score` / `execution`,
|
|
140
|
+
an episode nests `error` and (on point lookup) the member-trace aggregate
|
|
141
|
+
under `traces`, and unrecorded fields come back as `null`.
|
|
142
|
+
|
|
143
|
+
## Configuration
|
|
144
|
+
|
|
145
|
+
| Source | Meaning |
|
|
146
|
+
| ---------------------- | ------------------------------------------------------------------------------------------------------------------------------------------- |
|
|
147
|
+
| `PRIME_API_KEY` | Platform API token (needs `traces:read` / `traces:write` scopes) |
|
|
148
|
+
| `PRIME_TEAM_ID` | Optional team context, sent as `X-Prime-Team-ID` |
|
|
149
|
+
| `PRIME_TRACES_URL` | Base URL of the Prime Traces service; defaults to the platform API base URL. For the service's local compose stack: `http://localhost:8083` |
|
|
150
|
+
| `~/.prime/config.json` | Shared prime CLI config (`api_key`, `team_id`, `traces_url`) |
|
|
151
|
+
|
|
152
|
+
## Not implemented yet (open v0 contract decisions)
|
|
153
|
+
|
|
154
|
+
- Exports, in any form. The service publishes `GET /traces/export` and the two
|
|
155
|
+
job routes, but all three handlers raise `NotImplementedError` — answered as
|
|
156
|
+
500, not the 501 they document — and the streaming route declares no query
|
|
157
|
+
parameters, so there is no filter vocabulary to bind to. Wrapping it now
|
|
158
|
+
would ship a method that cannot succeed.
|
|
159
|
+
- `/search` and free-text queries — deferred with the `trace_components`
|
|
160
|
+
projection.
|
|
161
|
+
- Typed dot-path predicates (`traces.query`) — needs the server-side field
|
|
162
|
+
registry.
|
|
163
|
+
- An async client — the other prime SDKs ship sync/async pairs, and the main
|
|
164
|
+
producers (verifiers, prime-rl) are async; add once the sync surface
|
|
165
|
+
settles rather than freezing a duplicated API now.
|
|
166
|
+
|
|
167
|
+
## Documentation
|
|
168
|
+
|
|
169
|
+
For detailed documentation, visit the
|
|
170
|
+
[Prime Traces SDK documentation](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-traces).
|
|
171
|
+
|
|
172
|
+
## Related Packages
|
|
173
|
+
|
|
174
|
+
- [prime](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime) - Prime CLI (`prime traces ...` commands)
|
|
175
|
+
- [prime-sandboxes](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-sandboxes) - Sandboxes SDK
|
|
176
|
+
- [prime-evals](https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-evals) - Evals SDK
|
|
@@ -0,0 +1,108 @@
|
|
|
1
|
+
#!/usr/bin/env python3
|
|
2
|
+
"""
|
|
3
|
+
Basic usage example for the prime-traces SDK.
|
|
4
|
+
|
|
5
|
+
This demonstrates the standalone SDK without any CLI dependencies.
|
|
6
|
+
|
|
7
|
+
Credentials come from PRIME_API_KEY / ~/.prime/config.json. Point
|
|
8
|
+
PRIME_TRACES_URL at the Prime Traces service — e.g. the service's local
|
|
9
|
+
compose stack: PRIME_TRACES_URL=http://localhost:8083
|
|
10
|
+
"""
|
|
11
|
+
|
|
12
|
+
import tempfile
|
|
13
|
+
import time
|
|
14
|
+
from pathlib import Path
|
|
15
|
+
|
|
16
|
+
from prime_traces import APIError, ForbiddenError, TracesClient, ValidationRejectedError
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def sample_trace(trace_id: str, reward: float, started_at: float) -> dict:
|
|
20
|
+
"""One in-memory Verifiers-compatible trace record.
|
|
21
|
+
|
|
22
|
+
Trimmed to the fields the service's extractor reads, which is a small
|
|
23
|
+
subset of a real v1 record — the serialized record is stored verbatim,
|
|
24
|
+
and every summary column is a projection of it. Two of these are not
|
|
25
|
+
optional: a non-empty string ``id``, and a numeric ``timing.start`` inside
|
|
26
|
+
the accepted window (generous lookback, tight lookahead — producers upload
|
|
27
|
+
completed files, so an old run is ordinary and a future one never is).
|
|
28
|
+
A line missing either is rejected with ``invalid_trace`` /
|
|
29
|
+
``created_at_out_of_window`` and the whole request stores nothing.
|
|
30
|
+
"""
|
|
31
|
+
return {
|
|
32
|
+
"version": 4,
|
|
33
|
+
"id": trace_id,
|
|
34
|
+
"run": {"id": "run_example"},
|
|
35
|
+
"task": {"type": "ExampleTask", "data": {"name": "example-0001"}},
|
|
36
|
+
"agent": {
|
|
37
|
+
"name": "solver",
|
|
38
|
+
"config": {
|
|
39
|
+
"model": "deepseek-v4-flash",
|
|
40
|
+
"client": {"base_url": "https://api.pinference.ai/api/v1"},
|
|
41
|
+
},
|
|
42
|
+
},
|
|
43
|
+
"calls": [{"model": "deepseek-v4-flash", "usage": {"total_tokens": 1834}}],
|
|
44
|
+
"rewards": {"correctness": {"score": reward, "weight": 1.0}},
|
|
45
|
+
"metrics": {},
|
|
46
|
+
"stop_condition": "done",
|
|
47
|
+
"ok": True,
|
|
48
|
+
"errors": [],
|
|
49
|
+
# `timing.start` is the producer's wall clock and becomes `created_at`;
|
|
50
|
+
# `timing.scoring.end` is the last phase, so duration_ms comes from the
|
|
51
|
+
# two together.
|
|
52
|
+
"timing": {"start": started_at, "scoring": {"end": started_at + 12.5}},
|
|
53
|
+
"info": {},
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
|
|
57
|
+
def main():
|
|
58
|
+
# Verifiers Trace objects and prime-rl Rollouts can be passed directly;
|
|
59
|
+
# both expose the same to_record() protocol accepted by upload_records.
|
|
60
|
+
started_at = time.time()
|
|
61
|
+
traces = [
|
|
62
|
+
sample_trace("3f2a9c1e", reward=0.85, started_at=started_at),
|
|
63
|
+
sample_trace("b81d4e77", reward=0.40, started_at=started_at + 1.0),
|
|
64
|
+
]
|
|
65
|
+
output_dir = Path(tempfile.mkdtemp())
|
|
66
|
+
|
|
67
|
+
with TracesClient() as client:
|
|
68
|
+
try:
|
|
69
|
+
print("Uploading...")
|
|
70
|
+
# Content-addressed: rerunning this replays committed receipts
|
|
71
|
+
# without storing anything twice.
|
|
72
|
+
receipts = client.upload_records(
|
|
73
|
+
traces,
|
|
74
|
+
context={"source": "example", "suite_commit": "a1f39c2"},
|
|
75
|
+
)
|
|
76
|
+
for receipt in receipts:
|
|
77
|
+
print(f"✓ upload {receipt.upload_id[:12]}… {receipt.status}")
|
|
78
|
+
|
|
79
|
+
print("\nListing this run's traces...")
|
|
80
|
+
page = client.list(run_id="run_example", limit=10)
|
|
81
|
+
for summary in page.items:
|
|
82
|
+
# `score` is a nested object, and a null `reward` inside it
|
|
83
|
+
# means unscored — distinct from a scored 0.0.
|
|
84
|
+
score = summary.score
|
|
85
|
+
reward = score.reward if score else None
|
|
86
|
+
print(f" {summary.trace_id} reward={reward}")
|
|
87
|
+
|
|
88
|
+
if page.items:
|
|
89
|
+
trace_id = page.items[0].trace_id
|
|
90
|
+
print(f"\nFetching raw document for {trace_id}...")
|
|
91
|
+
dest = output_dir / "trace.json"
|
|
92
|
+
written = client.download_raw(trace_id, dest)
|
|
93
|
+
print(f"✓ wrote {written} bytes to {dest}")
|
|
94
|
+
|
|
95
|
+
except ValidationRejectedError as error:
|
|
96
|
+
# A 400 rejects the whole request and stores nothing; branch on
|
|
97
|
+
# error.code (see prime_traces.ErrorCode), fix the file, rerun.
|
|
98
|
+
print(f"✗ rejected: {error.code}: {error}")
|
|
99
|
+
except ForbiddenError as error:
|
|
100
|
+
# `service_not_enabled` means the account is not in the private
|
|
101
|
+
# beta; `forbidden` means the token lacks traces:read/traces:write.
|
|
102
|
+
print(f"✗ not permitted: {error.code}: {error}")
|
|
103
|
+
except APIError as error:
|
|
104
|
+
print(f"✗ API error: {error}")
|
|
105
|
+
|
|
106
|
+
|
|
107
|
+
if __name__ == "__main__":
|
|
108
|
+
main()
|
|
@@ -0,0 +1,60 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "prime-traces"
|
|
3
|
+
# Version is single-sourced from src/prime_traces/__init__.py via Hatch
|
|
4
|
+
dynamic = ["version"]
|
|
5
|
+
description = "Prime Intellect Traces SDK - Upload, query and export traces"
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.10"
|
|
8
|
+
license = {text = "MIT"}
|
|
9
|
+
authors = [
|
|
10
|
+
{ name = "Prime Intellect", email = "contact@primeintellect.ai" }
|
|
11
|
+
]
|
|
12
|
+
dependencies = [
|
|
13
|
+
"httpx>=0.25.0",
|
|
14
|
+
"pydantic>=2.0.0",
|
|
15
|
+
]
|
|
16
|
+
keywords = ["traces", "observability", "evals", "rollouts"]
|
|
17
|
+
classifiers = [
|
|
18
|
+
"Development Status :: 3 - Alpha",
|
|
19
|
+
"Intended Audience :: Developers",
|
|
20
|
+
"License :: OSI Approved :: MIT License",
|
|
21
|
+
"Operating System :: OS Independent",
|
|
22
|
+
"Programming Language :: Python :: 3",
|
|
23
|
+
"Programming Language :: Python :: 3.10",
|
|
24
|
+
"Programming Language :: Python :: 3.11",
|
|
25
|
+
"Programming Language :: Python :: 3.12",
|
|
26
|
+
"Topic :: Software Development :: Libraries :: Python Modules",
|
|
27
|
+
"Topic :: Scientific/Engineering :: Artificial Intelligence"
|
|
28
|
+
]
|
|
29
|
+
|
|
30
|
+
[project.urls]
|
|
31
|
+
Homepage = "https://github.com/PrimeIntellect-ai/prime"
|
|
32
|
+
Documentation = "https://github.com/PrimeIntellect-ai/prime/tree/main/packages/prime-traces"
|
|
33
|
+
Repository = "https://github.com/PrimeIntellect-ai/prime.git"
|
|
34
|
+
|
|
35
|
+
[project.optional-dependencies]
|
|
36
|
+
dev = [
|
|
37
|
+
"pytest>=7.0.0",
|
|
38
|
+
"ruff>=0.13.1",
|
|
39
|
+
]
|
|
40
|
+
|
|
41
|
+
[build-system]
|
|
42
|
+
requires = ["hatchling"]
|
|
43
|
+
build-backend = "hatchling.build"
|
|
44
|
+
|
|
45
|
+
[tool.hatch.version]
|
|
46
|
+
path = "src/prime_traces/__init__.py"
|
|
47
|
+
|
|
48
|
+
[tool.hatch.build.targets.wheel]
|
|
49
|
+
packages = ["src/prime_traces"]
|
|
50
|
+
|
|
51
|
+
[tool.pytest.ini_options]
|
|
52
|
+
addopts = "-v"
|
|
53
|
+
testpaths = ["tests"]
|
|
54
|
+
|
|
55
|
+
[tool.ruff]
|
|
56
|
+
line-length = 100
|
|
57
|
+
target-version = "py310"
|
|
58
|
+
|
|
59
|
+
[tool.ruff.lint]
|
|
60
|
+
extend-select = ["E", "F", "I"]
|