Integration prompt — ahu-ocr-akta-notaris
Paste this into a Claude Code session running inside the
ahu-ocr-akta-notarisrepo. Fill in<GATEWAY_URL>before pasting, or leave it and only the commented env entries will reference it.
We are introducing a central GPU/LLM gateway (ahu-gpu-manager): an OpenAI-compatible proxy + job API that fronts vLLM, PaddleOCR, and Azure DI, with priority queueing and audit logging. Its job API is wire-compatible with this repo's gpu-server contract (202 {job_id} → GET /jobs/{id}), extended with queue_position and eta_ms on queued jobs. Your job is to prepare this repo. Hard constraint: every change must be backwards-compatible and dormant — with no new env vars set, behavior must be identical to today. Do not flip live env values; only add commented-out entries.
1. Consolidate the seven duplicated submit-and-poll clients
These files each independently implement fetch(gpuServerUrl + …) + 1 s polling:
backend/src/ocr/gpu-server.ts(GpuServerOcrProvider)backend/src/llm/pendirian-pp-extract.tsbackend/src/llm/surat-pernyataan-extract.tsbackend/src/llm/override-scrutiny.tsbackend/src/llm/ktp-cleanup.tsbackend/src/llm/perbaikan-advisory.tsbackend/src/services/perbaikan-ai-summary-worker.tsandbackend/src/services/ai-advisory-engine.ts
Extract ONE shared client module (e.g. backend/src/lib/gpu-job-client.ts) that all of them use: submit (with per-call endpoint/payload), poll loop, timeout handling, error mapping. Preserve each caller's exact current endpoint, payload shape, and per-call timeout defaults — this is a refactor, not a behavior change. Existing tests must pass.
2. Queued-vs-processing timeout clocks (the no-RTO fix)
In the shared client: the processing-timeout clock (today gpuServerTimeoutMs = 5 min overall) must run only while the job status is processing. While status is queued, a separate cap applies: new env GPU_QUEUE_WAIT_MS (default 1800000 = 30 min). The gpu-server already returns queued/processing statuses, so this works against the current server too — a document stuck behind a deep queue must no longer be marked ERROR at 5 minutes.
If the poll response contains queue_position and/or eta_ms (the new gateway adds these; the current gpu-server does not), pass them through to callers.
3. Idempotent job submission
The shared client sends an Idempotency-Key header on submit, derived deterministically from document id + pipeline stage (+ content hash where cheap). The current gpu-server ignores unknown headers, so this is safe today; the gateway will dedupe on it so a crash/retry never runs the same 40-page OCR twice.
4. Queue visibility in the frontend
Where the frontend polls document/submission status (e.g. frontend/src/hooks/use-documents.ts and the apostille flow), render a distinct queued state when the backend exposes it — "Dalam antrean — posisi N (±X menit)" instead of the generic processing indicator. Backend: propagate queued + queue_position/eta_ms from the shared client into the existing status responses (additive fields only; omit when unknown).
5. Graceful 429/Retry-After on the direct-vLLM callers
backend/src/services/akta-txn-classifier.ts and backend/src/llm/small-card-cleanup.ts call vLLM directly with 30 s timeouts and already degrade gracefully on error. Verify a 429 response degrades the same way (no crash, no retry storm), honoring Retry-After if a retry is attempted.
6. Env preparation (commented, not active)
In .env.example (and .env as comments), plus gpu-server/.env.example:
# --- ahu-gpu-manager (do not enable until gateway is live) ---
# AKTA_TXN_CLASSIFIER_URL=<GATEWAY_URL>/v1
# CLEANUP_LLM_URL=<GATEWAY_URL>/v1
# GPU_QUEUE_WAIT_MS=1800000
# gpu-server/.env: VLLM_BASE_URL=<GATEWAY_URL>/v1
GPU_SERVER_URL stays pointed at the gpu-server for now; the gateway's job API takes over in a later phase (wire-compatible, so it will be a base-URL change).
7. Verification
- All existing tests pass; add unit tests for the shared client's clock behavior (queued time not counted; queue-wait cap enforced;
JOB_UNKNOWN/404 on poll surfaces a distinct resubmittable error). - Smoke: one document processed end-to-end against the current gpu-server, behavior unchanged.
Report back: files changed, any caller whose poll behavior could not be preserved exactly and why, and test results.