Skip to content

Commit 6d6ea8b

Browse files
committed
fix: align 4.0.1 release contracts
1 parent 443f75b commit 6d6ea8b

8 files changed

Lines changed: 70 additions & 13 deletions

File tree

‎CHANGELOG.md‎

Lines changed: 6 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -47,14 +47,19 @@ changed or removed.
4747
`bot_context` reaches the Oversight analysis prompt, and trajectory roles
4848
are `user` or `assistant`. The `screen()` docstring names the 2027-01-01
4949
sunset the runtime warning already carried.
50+
- Oversight ingest documentation now states the deployed 5 MB body limit.
51+
Analyze documentation distinguishes retained event metadata from the
52+
conversation and full-result records created by ingest. Signpost's
53+
`urgent` option is consistently described as a ranking hint.
54+
- Package metadata identifies the SDK as beta, matching the service status.
5055

5156
## 4.0.0 - 2026-09-03
5257

5358
Realigns the SDK with the API at commit 73c477c. Every v1 response model is now
5459
pinned to a sanitized live capture under `tests/fixtures/` (the deprecated
5560
`/v0/screen` models have none), and the two
5661
versions that never reached PyPI (2.3.1, 3.0.0) are folded into this release.
57-
The last published version is 2.3.0.
62+
The preceding published version was 2.3.0.
5863

5964
### Breaking changes
6065

‎README.md‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -215,7 +215,7 @@ In demo mode the call returns `OversightDemoAnalyzeResponse` with `mode`
215215
Batch ingest stores results for the dashboard and cross-session tracking. It
216216
accepts up to 300 conversations per call, bills $0.10 each before analysis,
217217
and returns when processing has finished (`status` is `complete` or `failed`).
218-
The request body is capped at 512 KB, so a batch near the count limit must
218+
The request body is capped at 5 MB, so a batch near the count limit must
219219
consist of short conversations. `webhook_url` is a legacy per-request callback:
220220
the API POSTs an unsigned `ingestion_complete` JSON summary there when the batch
221221
completes. The signed `oversight.ingestion.complete` event is delivered to

‎pyproject.toml‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -14,7 +14,7 @@ authors = [
1414
]
1515
keywords = ["safety", "ai", "risk", "classification", "mental-health", "crisis"]
1616
classifiers = [
17-
"Development Status :: 5 - Production/Stable",
17+
"Development Status :: 4 - Beta",
1818
"Intended Audience :: Developers",
1919
"License :: OSI Approved :: MIT License",
2020
"Programming Language :: Python :: 3",

‎src/nope_net/client.py‎

Lines changed: 8 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -405,9 +405,11 @@ def oversight_analyze(
405405
"""
406406
Analyze one conversation for harmful AI behaviours ($0.10 per call).
407407
408-
Synchronous; nothing is stored. Use :meth:`oversight_ingest` for
409-
persistent storage and the dashboard. Turn numbers in the result are
410-
1-based and count assistant turns.
408+
The call is synchronous and does not write conversation content or full
409+
results to the Oversight application database. Operational and
410+
analysis-event metadata is retained under the public policy. Use
411+
:meth:`oversight_ingest` for persistent dashboard storage. Turn numbers
412+
in the result are 1-based and count assistant turns.
411413
412414
Args:
413415
conversation: ``conversation_id``, ``messages`` (role ``user``, ``assistant``
@@ -489,7 +491,7 @@ def oversight_ingest(
489491
conversations: Conversations (at most 300), each with a ``conversation_id``
490492
and non-empty ``messages``; any sequence of dicts, mappings or
491493
``OversightConversation`` models. The request body is capped at
492-
512 KB, so a batch near the count limit must consist of short
494+
5 MB, so a batch near the count limit must consist of short
493495
conversations.
494496
webhook_url: Legacy per-request callback. The API POSTs an unsigned
495497
``{event: 'ingestion_complete', timestamp, ingestion_id,
@@ -563,7 +565,8 @@ def signpost(
563565
populations: Populations from ``nope_net.POPULATIONS`` (e.g. "youth").
564566
subdivisions: ISO 3166-2 codes within the country (e.g. "GB-SCT").
565567
limit: Maximum resources (the API clamps to 10).
566-
urgent: Only 24/7 resources.
568+
urgent: Ranking hint. Places 24/7 resources first among ties on
569+
relevance and priority tier without filtering other matches.
567570
568571
Returns:
569572
SignpostResponse with ``resources`` (and ``primary``/``secondary`` when

‎src/nope_net/types.py‎

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -719,7 +719,8 @@ class SignpostConfig(BaseModel):
719719
"""Maximum number of resources to return (the API clamps to 10)."""
720720

721721
urgent: Optional[bool] = None
722-
"""Only return 24/7 urgent resources."""
722+
"""Ranking hint. Places 24/7 resources first among ties on relevance and
723+
priority tier without filtering other matches."""
723724

724725

725726
class SignpostSmartConfig(BaseModel):
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
"""Public and maintainer-facing claims that must track the deployed API."""
2+
3+
from pathlib import Path
4+
5+
ROOT = Path(__file__).resolve().parents[2]
6+
7+
8+
def read(relative_path: str) -> str:
9+
return (ROOT / relative_path).read_text(encoding="utf-8")
10+
11+
12+
def test_oversight_limits_and_storage_are_route_specific() -> None:
13+
readme = read("README.md")
14+
client = read("src/nope_net/client.py")
15+
client_prose = " ".join(client.split())
16+
17+
assert "request body is capped at 5 MB" in readme
18+
assert "request body is capped at 512 KB" not in readme
19+
assert "5 MB, so a batch near the count limit" in client_prose
20+
assert "Synchronous; nothing is stored" not in client
21+
assert "does not write conversation content or full results" in client_prose
22+
23+
24+
def test_urgent_is_documented_as_ranking() -> None:
25+
client = read("src/nope_net/client.py")
26+
types = read("src/nope_net/types.py")
27+
28+
assert "urgent: Only 24/7 resources" not in client
29+
assert "Only return 24/7 urgent resources" not in types
30+
assert "24/7 resources first among ties" in client
31+
assert "Ranking hint" in types
32+
33+
34+
def test_deployed_ticket_references_are_absent_from_fixture_docs() -> None:
35+
assert "API fix A-" not in read("tests/fixtures/README.md")
36+
assert "The last published version is 2.3.0" not in read("CHANGELOG.md")
37+
38+
39+
def test_webhook_delivery_test_describes_distinct_ids() -> None:
40+
source = read("tests/unit/test_webhook_delivery_id.py")
41+
assert "sends the payload's event_id as X-NOPE-Delivery-ID" not in source
42+
assert "stored delivery id as X-NOPE-Delivery-ID" in source
43+
44+
45+
def test_package_metadata_marks_the_beta_status_honestly() -> None:
46+
project = read("pyproject.toml")
47+
assert "Development Status :: 4 - Beta" in project
48+
assert "Development Status :: 5 - Production/Stable" not in project

‎tests/fixtures/README.md‎

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -13,12 +13,12 @@ the sanitization rules below.
1313
| `evaluate/` | `auth.benign.json` | `POST /v1/evaluate`, one benign message, `risks: []` |
1414
| | `try.gb.json` | `POST /v1/try/evaluate`, three messages, GB resources, `subdivision_codes`, `metadata.model` |
1515
| | `try.us.json` | `POST /v1/try/evaluate`, one message; requested with `config.country: GB` before the demo route honoured it, so the resources are US |
16-
| | `try.gb.no-resources.json` | `POST /v1/try/evaluate` after API fix A-1: `config.country: GB`, `include_resources: false`, no resources returned |
16+
| | `try.gb.no-resources.json` | `POST /v1/try/evaluate`: `config.country: GB`, `include_resources: false`, no resources returned |
1717
| | `try.gb.text.json` | `POST /v1/try/evaluate` with `text`, `metadata.input_format: text_blob` |
1818
| `oversight/` | `try.full.json`, `try.fast.json`, `auth.fast.json` | the demo full, demo fast and authenticated fast envelopes |
1919
| `ocular/` | `try.json`, `auth.json`, `auth.per-turn.json` | demo wire (with `heads`, `detail`), customer wire, and the customer wire with `per_turn: true` (`trajectory`, `trajectory_shape`, roles `user`/`assistant`) |
20-
| `signpost/` | `auth.gb.json`, `try.smart.json`, `search.auth.json`, `search.auth.mixed-contacts.json`, `countries.json`, `detect-country.miss.json`, `by-id.json` | basic (authenticated; resources carry `id` after API fix A-6), demo smart, by-id, search (authenticated; the mixed-contacts capture from the first live run shows contacts without `tier` or `value` and chat contacts carrying `url`), countries, detect-country miss |
21-
| `billing/` | `balance.json`, `usage.json`, `pricing.json` | `GET /v1/billing/*` after API fix A-5 (balance and usage figures replaced) |
20+
| `signpost/` | `auth.gb.json`, `try.smart.json`, `search.auth.json`, `search.auth.mixed-contacts.json`, `countries.json`, `detect-country.miss.json`, `by-id.json` | basic authenticated results with resource `id`, demo smart, by-id, search (the mixed-contacts capture shows contacts without `tier` or `value` and chat contacts carrying `url`), countries, detect-country miss |
21+
| `billing/` | `balance.json`, `usage.json`, `pricing.json` | Current `GET /v1/billing/*` wire shapes (balance and usage figures replaced) |
2222
| `errors/` | `400.*.json`, `401.*.json`, `404.*.json`, `413.*.json` | error bodies as served; 402, 429 and 503 are source-derived and live in the tests |
2323
| `headers/` | `*.txt` | rate-limit and balance headers (balance values replaced) |
2424
| `webhooks/` | `*.json` | payloads built by the API's own builders and signed with a fixed test secret |

‎tests/unit/test_webhook_delivery_id.py‎

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -58,7 +58,7 @@ def test_verify_request_without_delivery_header() -> None:
5858

5959

6060
def test_live_fixture_delivery_id_is_distinct_from_payload_event_id() -> None:
61-
"""The API sends the payload's event_id as X-NOPE-Delivery-ID (signature.ts)."""
61+
"""The API sends the stored delivery id as X-NOPE-Delivery-ID."""
6262
for event in ("evaluate.alert", "oversight.alert", "oversight.ingestion.complete", "test.ping"):
6363
fx = load_fixture(f"webhooks/{event}.json")
6464
verified = Webhook.verify_request(

0 commit comments

Comments
 (0)