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.
@@ -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.2.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
@@ -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,
@@ -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.2.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" }