feat(api): canonical-label enum, options namespace, 0.3.0 bump - #19
Conversation
API surface improvements driven by new-consumer DX: - `categories` is now a closed `CanonicalLabel` enum mirroring `opf_eval.taxonomy.CANONICAL_LABELS` exactly. Clients (and codegen) see the 15 valid values in the spec, and bad values fail at the pydantic layer with a structured 422 instead of a custom 400. - Detector-specific options moved off the top level into a `options.<detector>` namespace. Today only `options.opf` is defined (carrying `decode_mode`), with `extra="forbid"` so typos like `decod_mode` surface as 422. New detector options can be added additively without re-shaping the top-level request. - `schema_version` dropped from every response. The API is pre-1.0; `info.version` is now the single source of truth and bumps to `0.3.0` for this breaking shape change. `/v1/` prefix stays reserved for the eventual 1.0 cutover. - Routes: removed the dead `_validate_categories` helper (pydantic handles it) and the unused `decode_mode` no-op branch. Added a `_ERROR_422_EXAMPLE` with concrete `bad_category` and `unknown_opf_option` payloads so generated docs show what a validation failure looks like. - Guides + spec regenerated; tests updated (422 for bad enum input + new test for `extra=forbid` on `OpfOptions`). Caveat: per-request `decode_mode` is still advisory — `OPFDetector` sets decode mode at construction time from `OPF_DECODE_MODE`. The options namespace reserves the shape for future per-call wiring. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hey @claude could you review please? |
|
Claude finished @jstjoe's task in 1m 36s —— View job PR Review
Overall this is a clean, well-motivated set of changes. The shift to a Bug: stale route description
"OPF-only: pass `decode_mode` to override the default Viterbi decoding."
Subtle behavior change:
|
- routes.py: stale `decode_mode` mention in `/v1/detect` description now points at `options.opf.decode_mode`. The string is rendered verbatim in Scalar/Swagger/ReDoc and would have misled consumers. - schemas.py: `DetectorInfo.categories` typed as `list[CanonicalLabel]` to match the request side. Source is `detector_categories()` which already returns canonical labels, so this is a pure spec-tightening. - tests: add a happy-path test sending `options.opf.decode_mode` and asserting 200, alongside the existing `extra_forbidden` typo case. DetectorOptions intentionally keeps `extra="ignore"` so clients can carry forward-compat blobs across detector swaps. The tradeoff (typo on a future top-level options key passes silently until the field is added) is acceptable pre-1.0. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Hey @claude re-review? |
|
Claude finished @jstjoe's task in 59s —— View job Re-review
The fix commit ( Resolved since first reviewStale route description ✅ — Happy-path test for
Still open
The fix is low-effort: either add a one-liner test asserting that Summary: Everything actionable from the first review is done except the |
Previously `if body.categories:` collapsed `None` and `[]` to the same "no filter" behavior. Strict semantic is clearer and composes better: - `null` / field omitted → keep every category the detector produces. - `[]` → deliberate "match nothing" filter, returns zero spans. Switch the guard to `is not None` and document the contract on the field description. Two new tests pin both branches. Reasoning for strict: clients building `categories` dynamically (e.g. `[c for c in wanted if c in known]`) expect narrowing to win. Silently widening an accidentally-empty filter back to "everything" is the more surprising default. Pre-1.0; reverse is easy if this turns out wrong. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
API surface improvements driven by new-consumer DX:
categoriesis now a closedCanonicalLabelenum mirroringopf_eval.taxonomy.CANONICAL_LABELSexactly. Clients (and codegen) see the 15 valid values in the spec, and bad values fail at the pydantic layer with a structured 422 instead of a custom 400.options.<detector>namespace. Today onlyoptions.opfis defined (carryingdecode_mode), withextra="forbid"so typos likedecod_modesurface as 422. New detector options can be added additively without re-shaping the top-level request.schema_versiondropped from every response. The API is pre-1.0;info.versionis now the single source of truth and bumps to0.3.0for this breaking shape change./v1/prefix stays reserved for the eventual 1.0 cutover._validate_categorieshelper (pydantic handles it) and the unuseddecode_modeno-op branch. Added a_ERROR_422_EXAMPLEwith concretebad_categoryandunknown_opf_optionpayloads so generated docs show what a validation failure looks like.extra=forbidonOpfOptions).Caveat: per-request
decode_modeis still advisory —OPFDetectorsets decode mode at construction time fromOPF_DECODE_MODE. The options namespace reserves the shape for future per-call wiring.