primate-intelligence 0.2.0__tar.gz → 0.3.0__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.
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/CHANGELOG.md +16 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/PKG-INFO +1 -1
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/_error_registry.json +28 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/_resources.py +83 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/pyproject.toml +1 -1
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/.gitignore +0 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/README.md +0 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/__init__.py +0 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/_client.py +0 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/_errors.py +0 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/webhooks.py +0 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/tests/test_parity.py +0 -0
- {primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/tests/test_sdk.py +0 -0
|
@@ -1,5 +1,21 @@
|
|
|
1
1
|
# Changelog — primate-intelligence (Python SDK)
|
|
2
2
|
|
|
3
|
+
## 0.3.0 — 2026-07-31
|
|
4
|
+
|
|
5
|
+
Parity release with the PRI-496 private→public API parity program. Behavior verified against the live dev API on 2026-07-31 (recorded stream with narrative + status events + mid-stream update_prompt). Dict-based responses — fully additive.
|
|
6
|
+
|
|
7
|
+
### Added
|
|
8
|
+
|
|
9
|
+
- `client.streams.recording(stream_id)` — `GET /v1/streams/{id}/recording`: signed 1h playback URL for the server-side session recording (create the stream with `recording=True`; raw H.264 Annex-B — `ffmpeg -i in.h264 -c copy out.mp4`).
|
|
10
|
+
- `client.videos.list_analyses(video_id, status=, limit=, starting_after=)` — `GET /v1/videos/{id}/analyses`: per-video analysis history, newest first.
|
|
11
|
+
- `client.analyses.rerun(analysis_id)` — `POST /v1/analyses/{id}/rerun`: free re-run of an eligible failed analysis (`rerun_eligible: true`; never billed).
|
|
12
|
+
|
|
13
|
+
### Documentation (docstrings)
|
|
14
|
+
|
|
15
|
+
- `streams.create`: full signaling protocol summary incl. mid-stream `update_prompt` → `prompt_updated`, the narrative opt-in (`options={"narrative": True}` → `narrative_update {t_s, text}` on result detections + `status` events `prompt_context | combined_prompt | recalculating`), and the `recording=True` opt-in.
|
|
16
|
+
- `analyses.create`: the narrative opt-in and the async `narrative {status, entries}` object on completed analyses.
|
|
17
|
+
- `videos.retrieve`: the `media {url, expires_at}` signed source-playback field (sign-on-read, 1h TTL).
|
|
18
|
+
|
|
3
19
|
## 0.2.0 — 2026-07-28
|
|
4
20
|
|
|
5
21
|
Parity release with the 2026-07-22 → 2026-07-28 API changes (PRI-480/482 rounds + streaming DX). Behavior verified against the live OpenAPI document and recorded live responses. Dict-based responses mean no breaking type changes — this release is additive.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: primate-intelligence
|
|
3
|
-
Version: 0.
|
|
3
|
+
Version: 0.3.0
|
|
4
4
|
Summary: Official Python SDK for the Primate Vision API (Primate Intelligence). Video scene understanding: upload, analyze, stream.
|
|
5
5
|
Project-URL: Homepage, https://primateintelligence.ai/docs
|
|
6
6
|
Project-URL: Repository, https://github.com/Primate-Intelligence/primate-intelligence-api
|
{primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/_error_registry.json
RENAMED
|
@@ -204,6 +204,13 @@
|
|
|
204
204
|
"idem": false,
|
|
205
205
|
"docs_url": "https://primateintelligence.ai/docs/errors#analysis_not_cancelable"
|
|
206
206
|
},
|
|
207
|
+
"rerun_not_eligible": {
|
|
208
|
+
"kind": "http",
|
|
209
|
+
"status": 409,
|
|
210
|
+
"retryable": false,
|
|
211
|
+
"idem": false,
|
|
212
|
+
"docs_url": "https://primateintelligence.ai/docs/errors#rerun_not_eligible"
|
|
213
|
+
},
|
|
207
214
|
"rate_limit_exceeded": {
|
|
208
215
|
"kind": "http",
|
|
209
216
|
"status": 429,
|
|
@@ -260,6 +267,27 @@
|
|
|
260
267
|
"idem": false,
|
|
261
268
|
"docs_url": "https://primateintelligence.ai/docs/errors#upgrade_limit_exceeded"
|
|
262
269
|
},
|
|
270
|
+
"github_verification_required": {
|
|
271
|
+
"kind": "http",
|
|
272
|
+
"status": 403,
|
|
273
|
+
"retryable": false,
|
|
274
|
+
"idem": false,
|
|
275
|
+
"docs_url": "https://primateintelligence.ai/docs/errors#github_verification_required"
|
|
276
|
+
},
|
|
277
|
+
"github_token_invalid": {
|
|
278
|
+
"kind": "http",
|
|
279
|
+
"status": 401,
|
|
280
|
+
"retryable": false,
|
|
281
|
+
"idem": false,
|
|
282
|
+
"docs_url": "https://primateintelligence.ai/docs/errors#github_token_invalid"
|
|
283
|
+
},
|
|
284
|
+
"github_account_already_used": {
|
|
285
|
+
"kind": "http",
|
|
286
|
+
"status": 409,
|
|
287
|
+
"retryable": false,
|
|
288
|
+
"idem": false,
|
|
289
|
+
"docs_url": "https://primateintelligence.ai/docs/errors#github_account_already_used"
|
|
290
|
+
},
|
|
263
291
|
"device_code_expired": {
|
|
264
292
|
"kind": "http",
|
|
265
293
|
"status": 410,
|
{primate_intelligence-0.2.0 → primate_intelligence-0.3.0}/primate_intelligence/_resources.py
RENAMED
|
@@ -27,6 +27,15 @@ class Videos:
|
|
|
27
27
|
return self._http.post("/v1/videos", json_body=params)
|
|
28
28
|
|
|
29
29
|
def retrieve(self, video_id: str) -> dict[str, Any]:
|
|
30
|
+
"""GET /v1/videos/{id}.
|
|
31
|
+
|
|
32
|
+
Ready videos carry ``media: {url, expires_at} | None`` — a signed
|
|
33
|
+
playback URL for the original source video, generated FRESH on every
|
|
34
|
+
read with a 1-hour TTL (sign-on-read). Re-fetch the video for a new
|
|
35
|
+
URL after expiry; old videos always stay playable. ``media`` is None
|
|
36
|
+
for non-ready videos and rows without a stored source object (e.g.
|
|
37
|
+
sandbox fixture videos).
|
|
38
|
+
"""
|
|
30
39
|
return self._http.get(f"/v1/videos/{video_id}")
|
|
31
40
|
|
|
32
41
|
def list(self, **params: Any) -> dict[str, Any]:
|
|
@@ -42,6 +51,18 @@ class Videos:
|
|
|
42
51
|
"""POST /v1/videos/{id}/complete — after the presigned S3 PUT."""
|
|
43
52
|
return self._http.post(f"/v1/videos/{video_id}/complete")
|
|
44
53
|
|
|
54
|
+
def list_analyses(self, video_id: str, **params: Any) -> dict[str, Any]:
|
|
55
|
+
"""GET /v1/videos/{id}/analyses — per-video analysis history.
|
|
56
|
+
|
|
57
|
+
Every analysis run against this video, newest first. Same list
|
|
58
|
+
envelope + filters (``status``, ``limit``, ``starting_after``) as
|
|
59
|
+
GET /v1/analyses — the resource-nested spelling of
|
|
60
|
+
``analyses.list(video_id=...)``. Combine with ``Video.media`` (signed
|
|
61
|
+
source playback) and ``Analysis.artifacts`` (annotated result video)
|
|
62
|
+
for full video-library semantics: list, replay, per-video history.
|
|
63
|
+
"""
|
|
64
|
+
return self._http.get(f"/v1/videos/{video_id}/analyses", params=params)
|
|
65
|
+
|
|
45
66
|
def upload(
|
|
46
67
|
self,
|
|
47
68
|
file: bytes,
|
|
@@ -72,6 +93,14 @@ class Analyses:
|
|
|
72
93
|
def create(self, **params: Any) -> dict[str, Any]:
|
|
73
94
|
"""POST /v1/analyses.
|
|
74
95
|
|
|
96
|
+
Pass ``options={"narrative": True}`` to generate a timestamped
|
|
97
|
+
narrative (event sentences) for the video: the completed analysis
|
|
98
|
+
then carries ``narrative: {status: "generating" | "ready" | "failed",
|
|
99
|
+
entries: [{t_s, text}]}``. Generation runs asynchronously AFTER
|
|
100
|
+
completion — the first completed read may show ``generating`` with
|
|
101
|
+
empty entries; keep polling the GET until ``ready``. No surcharge.
|
|
102
|
+
Without the opt-in, ``narrative`` is always None.
|
|
103
|
+
|
|
75
104
|
Pass ``validate_only=True`` for a dry run: compiles the prompt, reports
|
|
76
105
|
whether the query is assessable, and estimates duration/cost — WITHOUT
|
|
77
106
|
creating an analysis or spending credits. The response is then an
|
|
@@ -135,6 +164,18 @@ class Analyses:
|
|
|
135
164
|
def cancel(self, analysis_id: str) -> dict[str, Any]:
|
|
136
165
|
return self._http.post(f"/v1/analyses/{analysis_id}/cancel")
|
|
137
166
|
|
|
167
|
+
def rerun(self, analysis_id: str) -> dict[str, Any]:
|
|
168
|
+
"""POST /v1/analyses/{id}/rerun — free re-run of an eligible failed analysis.
|
|
169
|
+
|
|
170
|
+
Failed analyses carry ``rerun_eligible: bool`` (present ONLY on
|
|
171
|
+
failed status) — true when the failure occurred during a declared
|
|
172
|
+
platform incident or was flagged by ops. Creates a FRESH analysis at
|
|
173
|
+
no charge (same video, prompt/query, model, options; new id; usage
|
|
174
|
+
stays None — never billed). One free re-run per failed analysis;
|
|
175
|
+
ineligible → 409 ``rerun_not_eligible``.
|
|
176
|
+
"""
|
|
177
|
+
return self._http.post(f"/v1/analyses/{analysis_id}/rerun")
|
|
178
|
+
|
|
138
179
|
def create_and_wait(
|
|
139
180
|
self,
|
|
140
181
|
*,
|
|
@@ -177,6 +218,29 @@ class Streams:
|
|
|
177
218
|
self._http = http
|
|
178
219
|
|
|
179
220
|
def create(self, **params: Any) -> dict[str, Any]:
|
|
221
|
+
"""POST /v1/streams — create a real-time WebRTC analysis stream.
|
|
222
|
+
|
|
223
|
+
Returns ``signaling.url`` + ``ice_servers`` + ``limits``. Signaling
|
|
224
|
+
protocol v1 over the WS (auth: ``?token=`` with a pvct\\_ client token,
|
|
225
|
+
never a secret key): join → ready|queued → offer/answer/ice → live →
|
|
226
|
+
result frames; 5s metering ticks; ``end {reason}``. Mid-stream, send
|
|
227
|
+
``{"type": "update_prompt", "prompt": "..."}`` (≤2000 chars) to change
|
|
228
|
+
the question without reconnecting — the server confirms with
|
|
229
|
+
``{"type": "prompt_updated"}`` and subsequent frames echo the new
|
|
230
|
+
prompt.
|
|
231
|
+
|
|
232
|
+
Opt-ins:
|
|
233
|
+
|
|
234
|
+
- ``options={"narrative": True}`` — live narrative. Result-frame
|
|
235
|
+
detections then carry ``narrative_update: {t_s, text}`` whenever the
|
|
236
|
+
engine emits a new sentence (event-driven, absent otherwise), and
|
|
237
|
+
the WS additionally delivers ``status`` events
|
|
238
|
+
(``{"type": "status", "status": "prompt_context" |
|
|
239
|
+
"combined_prompt" | "recalculating", "message": ...}`` — closed
|
|
240
|
+
vocabulary) for ordering/annotating narrative entries. No surcharge.
|
|
241
|
+
- ``recording=True`` — server-side session recording; retrieve the
|
|
242
|
+
playback URL after the stream ends via :meth:`recording`.
|
|
243
|
+
"""
|
|
180
244
|
return self._http.post("/v1/streams", json_body=params or {})
|
|
181
245
|
|
|
182
246
|
def retrieve(self, stream_id: str) -> dict[str, Any]:
|
|
@@ -212,6 +276,25 @@ class Streams:
|
|
|
212
276
|
"""
|
|
213
277
|
return self._http.post(f"/v1/streams/{stream_id}/end")
|
|
214
278
|
|
|
279
|
+
def recording(self, stream_id: str) -> dict[str, Any]:
|
|
280
|
+
"""GET /v1/streams/{id}/recording — signed session-recording URL.
|
|
281
|
+
|
|
282
|
+
Requires the stream to have been created with ``recording=True`` and
|
|
283
|
+
ended with a stored recording (``stream["recording"]["status"] ==
|
|
284
|
+
"available"``). Returns::
|
|
285
|
+
|
|
286
|
+
{object: "stream_recording", stream_id, url, expires_at,
|
|
287
|
+
content_type: "video/h264", container: "h264-annex-b"}
|
|
288
|
+
|
|
289
|
+
The URL is signed FRESH on every call (1h TTL) — re-fetch for a new
|
|
290
|
+
one. The recording is a raw H.264 Annex-B elementary stream (the
|
|
291
|
+
exact annotated frames the client saw); lossless remux to MP4:
|
|
292
|
+
``ffmpeg -i recording.h264 -c copy recording.mp4``.
|
|
293
|
+
404 when recording wasn't enabled; 409 while the stream is active,
|
|
294
|
+
when capture failed, or when nothing was stored.
|
|
295
|
+
"""
|
|
296
|
+
return self._http.get(f"/v1/streams/{stream_id}/recording")
|
|
297
|
+
|
|
215
298
|
|
|
216
299
|
class ClientTokens:
|
|
217
300
|
def __init__(self, http: HttpClient) -> None:
|
|
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
|
|
|
4
4
|
|
|
5
5
|
[project]
|
|
6
6
|
name = "primate-intelligence"
|
|
7
|
-
version = "0.
|
|
7
|
+
version = "0.3.0"
|
|
8
8
|
description = "Official Python SDK for the Primate Vision API (Primate Intelligence). Video scene understanding: upload, analyze, stream."
|
|
9
9
|
readme = "README.md"
|
|
10
10
|
license = { text = "MIT" }
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|
|
File without changes
|