API design refresh: /v1/detect+sanitize -> /api/find+replace (0.5.0) - #20
Conversation
…place
User-facing terminology landed for new consumers:
- Paths: `/v1/detect` -> `/v1/find` ("Find sensitive data");
`/v1/sanitize` -> `/v1/replace` ("Replace sensitive data").
- Schemas: `DetectRequest`/`DetectResponse` -> `FindRequest`/`FindResponse`;
`SanitizeRequest`/`SanitizeResponse` -> `ReplaceRequest`/`ReplaceResponse`;
`SanitizedSpan` -> `ReplacedSpan`; `SanitizeMode` -> `ReplaceMode`.
- Response body: `sanitized_text` -> `replaced_text` (pairs naturally with
the per-span `replacement` field).
- Tags: `Detect`/`Sanitize` -> `Find`/`Replace`.
- Internal: `_run_detect` -> `_run_find`, `_DetectInput` -> `_FindInput`,
route handlers `detect`/`sanitize` -> `find`/`replace`.
- Bump `info.version` 0.3.0 -> 0.4.0 (breaking shape change pre-1.0).
- Rename `docs/guides/sanitize-modes.md` -> `replace-modes.md`; update
README + every guide ref. Regenerate spec.
Old route names are gone — no aliases. Pre-1.0 contract; clients on the
old names need a one-line change.
Backend method `detector.detect()` (in opf_eval) keeps its name — that's
the in-process model API, not the public HTTP contract, and renaming it
spans an external package.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Pre-1.0, nobody outside this repo consumes the API yet. URL-path versioning is premature commitment; flip to unversioned `/api/...` paths now and plan to add Stripe-style header-based date-string versioning (`API-Version: 2026-05-13`) when the contract stabilizes. - `app.include_router` prefix `/v1` -> `/api`. - Bump `info.version` 0.4.0 -> 0.5.0 (breaking shape change). - Update every route/schema docstring, description, response example reference, and curl snippet that mentioned `/v1/*`. - README, every guide under `docs/guides/`, and `docs/token-vault-setup.md` re-pointed at `/api/*`. - Update versioning section in main.py + overview guide to call out the planned `API-Version` header migration. - Test bootstrap fixture also moved to `prefix="/api"`. Notebook (`notebooks/pii_detector_comparison.ipynb`) imports source directly and never hits the HTTP layer — unaffected by this change. Old `/v1/*` paths return 404 — no aliases. Skyflow management API paths (`/v1/vaults`, `/v1/workspaces`) in `docs/token-vault-setup.md` are external endpoints and stay as-is. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
@claude quick review please? |
|
Claude finished @jstjoe's task in 2m 17s —— View job Code Review
Overall: LGTM. The rename is clean and thorough. No missed references on the API surface, tests all updated, spec regenerated, docs in sync. A few minor notes: Stale comment in
|
`detected_spans` always carried every span the detector emitted, including
ones whose `replacement` was rendered but suppressed as a later-starting
overlap. The `replacement` field was populated on those spans too, so a
client reading them as ground truth would over-count what actually landed
in `replaced_text`. Documented in the route prose, but the field itself
made no distinction.
Add a required `replaced: bool` to `ReplacedSpan`:
- `replaced=true` — span was spliced into `replaced_text`.
- `replaced=false` — span was dropped as a later-starting overlap of an
earlier-starting span. `replacement` is still
populated (the renderer ran) but the string did not
land.
Flag computed inline in `routes.py` using the same `start >= cursor`
walk that `splice_pieces` uses, so the two are guaranteed to agree.
`info.version` -> 0.6.0 (adding a required response field). Pre-1.0;
clients filter to `replaced=true` to reconstruct exactly what landed.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
The route name has churned (`/redact` -> `/sanitize` -> `/replace`) and the comment's point is about eager-loading the 2.8 GB checkpoint, not the endpoint. Reword to "first request" so the next rename doesn't strand the comment again. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
|
Thanks @claude, review again? |
|
Claude finished @jstjoe's task in 1m 57s —— View job Code Review (re-review)
Both items flagged in the last review were shipped in follow-on commits — clean responses.
Both agree on the One minor note on The Overall: LGTM. The rename is complete, the overlap transparency is solid, and the |
The renderer runs for every span before overlap suppression, so a span that ends up `replaced=false` still consumes its label's counter. Kept spans on the same label after a dropped overlap therefore skip numbers (e.g. `[PERSON_1]` then `[PERSON_3]`, gap = the dropped span). Not a bug — a consequence of doing the splice walk after rendering — but worth flagging where someone would actually look: - `/api/replace` route description: mode table row for `label_number` now points at the new overlap-notes paragraph. - `docs/guides/replace-modes.md`: same table-row hint plus a dedicated paragraph in the Overlapping-spans section explaining the gap. No behavior change; spec regenerated to pick up the inline description. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Summary
Two breaking renames on top of #18/#19, in successive commits:
/v1/detect->/v1/find("Find sensitive data") and/v1/sanitize->/v1/replace("Replace sensitive data"). Internal schemas, response fieldsanitized_text->replaced_text, tags, internal helpers all follow./v1/*to/api/*. Pre-1.0; nobody outside this repo consumes the API yet. When the contract stabilizes the plan is header-based date-string versioning in the style of Stripe (API-Version: 2026-05-13).info.versionbumped 0.4.0 -> 0.5.0 across the two commits (one per breaking change).New endpoint surface
/api/find/api/replacemode./api/detectors/api/healthOld
/v1/*paths return 404. No aliases.Notable touches
app.include_router(prefix=\"/api\"), FastAPI tagsFind/Replace/Meta, versioning section ininfo.descriptioncalls out the plannedAPI-Versionheader.find+replacehandlers, internal_run_find+_FindInput.FindRequest/FindResponse/ReplaceRequest/ReplaceResponse/ReplacedSpan/ReplaceMode; field renamedsanitized_text->replaced_text./api/*.sanitize-modes.md; content updated./api/replace(label_tokenmode).version: 0.5.0, paths/api/find,/api/replace,/api/detectors,/api/health.Notebook (
notebooks/pii_detector_comparison.ipynb) imports source and never hits HTTP — unaffected.Versioning narrative (now in spec + overview guide)
The API is pre-1.0.
info.versionis the only version surface today. Each breaking change to request or response shape lands as a minor bump pre-1.0. URL paths stay unversioned (/api/...); future headerAPI-Version: 2026-05-13will pin clients to a release date once the contract is stable.Test plan
uv run --package opf-api opf-api-export-openapi --out /tmp/freshregenerates; diff against committed spec is emptyuvicorn opf_api.main:appboots;/api/health,/api/detectors,/scalar,/docs,/redoc,/openapi.json-> 200;/v1/*-> 404pytest api/tests/test_routes.py— 21/21 passing (all rewritten for/api/*)OpenAPI spec freshnessjob passes on latest push🤖 Generated with Claude Code