mirror of
https://github.com/MBombeck/HealthLog.git
synced 2026-11-13 22:32:04 +00:00
feat(documents): make the document-AI rate limit an operator setting with honest accounting
The document-AI actions (read/index, extract, suggest, summarise) shared a hard-coded 6/hour bucket that lied twice: the 429 said "try again in a few minutes" while the window is an hour, and several failure paths burned a slot before any provider was dispatched. - DOCUMENT_AI_LIMIT_PER_HOUR configures the shared per-user ceiling (default 6, clamped 1-1000), wired end to end: compose environment whitelist, .env.production.example, env manifest, operator docs. - INSIGHTS_RATE_LIMIT_PER_HOUR joins the compose whitelist and manifest; it was documented but never listed, so an operator value could not reach the container. - The 429 mirrors the bucket's reset instant into meta.retryAt (plus the existing X-RateLimit-Reset header); the client derives "try again in about N minutes" from it in all six locales, with a plural-aware key. - A slot is consumed only when a request actually reaches the provider (or the local index/echo work): malformed bodies, unreadable originals, refused consent, and exhausted daily budgets refund the charged slot via a window-scoped refundRateLimit primitive. Consistent across all four routes sharing the bucket.
This commit is contained in:
1 parent
592d1e9aa8
commit
8e16df6894
26 files changed
+560
-160
No files matched your search
@@ -12495,9 +12495,11 @@ paths:
|
||||
description: "Optional AI assist on an already-stored document. Runs ONE provider call over the stored original (VISION,
|
||||
empty body) or browser-OCR'd text (TEXT, `application/json` `{ mode: \"text\", text }`, opt-in local OCR) and
|
||||
returns a `{ title, kind, documentDate }` DRAFT for the edit form. AI-consent / rate / budget gated (shares the
|
||||
6/hour extract bucket). WRITES NOTHING — never stages facts, never flips status; the user reviews and saves. 422
|
||||
(`documents.inbound.providerUnsupported`) with no provider configured. Never interprets or diagnoses; the title
|
||||
is a neutral filing label."
|
||||
per-user document-AI bucket with extract/summary/index — default 6/hour, operator-tunable via
|
||||
`DOCUMENT_AI_LIMIT_PER_HOUR`; a slot is only consumed when the request reaches the provider, and the 429 carries
|
||||
the reset instant in `X-RateLimit-Reset` and `meta.retryAt`). WRITES NOTHING — never stages facts, never flips
|
||||
status; the user reviews and saves. 422 (`documents.inbound.providerUnsupported`) with no provider configured.
|
||||
Never interprets or diagnoses; the title is a neutral filing label."
|
||||
parameters:
|
||||
- name: id
|
||||
in: path
|
||||
|
||||
@@ -393,6 +393,20 @@ verbose model gets its briefing cut off mid-JSON — the API reports
|
||||
that case as "AI response was cut off" rather than the generic
|
||||
invalid-JSON error.
|
||||
|
||||
`DOCUMENT_AI_LIMIT_PER_HOUR` (default `6`, clamped to 1–1000) bounds
|
||||
the document-AI actions per user per hour. Read/index, extract,
|
||||
suggest and summarise share one bucket, and a slot is only consumed
|
||||
when a request actually reaches the provider — a request that fails
|
||||
during input preparation (unreadable file, exhausted daily budget,
|
||||
malformed body) hands its slot back. Raise it when users batch-read
|
||||
many documents against a generous provider budget; lower it on a
|
||||
tight one. The 429 names the actual wait (the window is an hour) and
|
||||
carries the reset instant in `X-RateLimit-Reset`.
|
||||
|
||||
All three variables must also be listed in the compose
|
||||
`environment:` whitelist to reach the container; the shipped
|
||||
`docker-compose.yml` lists them.
|
||||
|
||||
## Connection test
|
||||
|
||||
`POST /api/ai/test` runs a one-shot probe against the resolved
|
||||
|
||||
Reference in new issue
Block a user