AHU AI Platform — Team Handoff (2026-07-07)
One page to orient a new engineer: what each repo is, what runs where, and how it all connects. Canonical wire contract: ahu-gpu-manager/docs/CONVENTIONS.md v1.0 — read it before touching any model/OCR call path; on any conflict it wins.
The one-paragraph story
Three AI engines (chatbot, OCR, doc-classifier) serve users. As of 2026-07-07, every AI call they make — LLM chat, embeddings, OCR jobs, Azure DI, document classification — flows through one Go gateway (ahu-gpu-manager, :8200) that enforces priority queueing per GPU upstream, on-prem policy, idempotency, and emits one audit event per call onto a Redis stream. The observatory (ahu-ai-observatory, :8300) ingests that stream into TimescaleDB and serves a query API; the dashboard (ahu-observatory-dashboard, :8320) renders it as role-aware BI. Engines are dormant-integrated: unset the gateway env vars and each engine talks to its backends directly again (instant rollback).
Repositories (GitHub org: Virtue-Digital-Indonesia)
| Repo | Stack | What it is | Live service(s) on ai-ahu |
Status |
|---|---|---|---|---|
ai-ahu-chatbot |
TS/Node monorepo + Python (agno 2.4.7) | Indonesian chatbot for Ditjen AHU: public Document-RAG surface + internal staff surface. Monorepo apps: public-web :3500, internal-web :3510, public-agent, internal-agent (agno), llm-gateway, workers. Owns its own RAG pipeline (pgvector, ingestion, orchestration). |
ahu-ai-chatbot-public :3500, ahu-ai-chatbot-internal :3510, ahu-ai-agent-public/internal, ahu-ai-workers, ahu-dash-db-shared (pgvector) |
Active. LLM + embeddings via gateway (tenant ahu-chatbot) |
ahu-ocr-tidyup |
TS (Hono) backend + React frontend + Python gpu-server (FastAPI + Celery) + PaddleOCR service | The current OCR engine — Akta Notaris document intelligence: PT flows (pendirian, perubahan, peleburan, PP flows, apostille…), verifier workspace, shareholder e-voting. Contains gpu-server/ (VLM extraction, Celery queues akta/llm) and paddle-ocr-service/. |
ahu-ai-ocr-web :3520, ahu-ai-ocr-db :5434, ahu-gpu-server :8000 (loopback), ahu-paddle-ocr :8108 |
Active. All 4 AI seams via gateway (tenant ahu-ocr) |
ahu-ocr-akta-notaris |
same shape as tidyup | Legacy OCR repo — superseded by ahu-ocr-tidyup (same engine, pre-cleanup). Keep for history; do not develop here. |
— | Superseded |
ahu-gpu-manager |
Go 1.25 | The AI gateway (“ahu-platform”): OpenAI-compatible /v1 proxy, per-upstream priority slot pools + queue-aware SSE, async Job API, gpu-server compat façade (12 job paths + /summarize + /cek-bukti), Azure-DI + PaddleOCR + classifier native façades (P2.7), audit emission, idempotency, on-prem enforcement, drain-aware shutdown. Tags p0.9→p2.7. |
ahu-platform-gateway-1 :8200, ahu-platform-redis |
Active — the hub. Everything routes through it |
ahu-ai-observatory |
Go 1.25 | Audit ingester + query API: consumes Redis stream ahu.ai.audit (schema v1, hash-chained), stores in TimescaleDB, serves /api/summary, /api/series, /api/calls, /api/verify-chain, /api/access-log behind role-gated bearer tokens. |
ahu-observatory-observatory-1 :8300, ahu-observatory-db (Timescale) |
Active |
ahu-observatory-dashboard |
React + Vite + IBM Carbon (@carbon/charts) | Role-aware BI for the observatory: executive (KPIs + trends), operator (call explorer + bodies), auditor (chain verification + access log). Light/dark themes; timestamps display in GMT+7 (WIB). | ahu-observatory-dashboard :8320 |
Active |
ahu-doc-classifier |
Python (LayoutLMv3, torch/transformers) | Document-type classifier (KTP, NPWP, bukti setor, akta kinds, PASPOR, SIM…) combining text + layout + vision. Used by the OCR engine's intake. | ahu-classifier :5004 (loopback, behind nginx /api/classifier) |
Active. Called via gateway façade |
ahu-chatbot-orchestrator |
Python | Archived — orchestrator was translated to TS inside ai-ahu-chatbot (apps/public-web/src/lib/orchestrator). |
(name survives as a legacy container) | Archived |
ai-ahu-data-dash |
Python | Archived — staff data-dash absorbed into the chatbot monorepo (DATA_DASH_API_URL proxy). |
— | Archived |
local document-classification/ |
Python | Pre-LayoutLMv3 classifier experiment; not deployed. | — | Experiment only |
Model servers on ai-ahu (not repos — deployed containers the gateway fronts): ahu-vllm :8001 (Qwen3.6-35B-A3B-FP8, the main LLM), ahu-cleanup-llm :8003 (3B cleanup model), tei-qwen3-embed :8100 (Qwen3-Embedding-4B), plus the gpu-server's own vLLM VLM (qwen-vlm). External: DashScope qwen3.5-397b-a17b (dev-only, class: external_dev).
Topology
x056.ahu-azure.val.id"] subgraph chatbot["ai-ahu-chatbot (tenant: ahu-chatbot)"] cbpub["public-web :3500
RAG chat UI + orchestrator"] cbint["internal-web :3510 + workers"] agents["agno agents
public + internal"] end subgraph ocr["ahu-ocr-tidyup (tenant: ahu-ocr)"] ocrweb["ocr-web :3520
Hono backend + React"] ocrdb[("ocr-db :5434
Postgres")] end subgraph platform["ahu-gpu-manager — AI GATEWAY :8200"] gw["gateway
/v1 OpenAI-compat · Job API
gpu-server facade · Azure-DI facade
classifier + paddle sync facades
priority pools · idempotency · on-prem policy"] redis[("ahu-platform-redis
stream: ahu.ai.audit")] end subgraph upstreams["AI backends"] vllm["ahu-vllm :8001
Qwen3.6-35B"] cleanup["cleanup-llm :8003
3B"] tei["tei-embeddings :8100
Qwen3-Embedding-4B"] gpusrv["ahu-gpu-server :8000
FastAPI + Celery + VLM
12 OCR/LLM job ops"] azdi["Azure DI on-prem
4 API prefixes"] clf["ahu-classifier :5004
LayoutLMv3"] paddle["ahu-paddle-ocr :8108
PP-OCRv5 (dormant)"] end subgraph obs["Observability"] obsapi["ahu-ai-observatory :8300
ingester + query API"] tsdb[("TimescaleDB")] dash["observatory-dashboard :8320
Carbon BI · WIB"] end end ext["DashScope cloud
qwen3.5-397b — DEV ONLY"] pub --> cbpub staff --> cbint staff --> nginx --> ocrweb cbpub -- "chat + embeddings
X-Tenant-Id: ahu-chatbot" --> gw agents -- "agno LLM calls" --> gw cbint --> agents ocrweb -- "GPU_SERVER_URL
12 job paths + summarize/cek-bukti" --> gw ocrweb -- "AZURE_ON_PREM_BASE_URL
analyze + poll" --> gw ocrweb -- "CLASSIFIER_URL
/api/classifier/classify" --> gw ocrweb --- ocrdb gw --> vllm gw --> cleanup gw --> tei gw -- "facade: forward verbatim" --> gpusrv gw -- "facade: via nginx" --> azdi gw -- "facade: via nginx" --> clf gw -. "flip-ready" .-> paddle gw -. "synthesis route
allow_external_upstreams: true" .-> ext gw -- "1 audit event / call
fire-and-forget" --> redis redis --> obsapi --> tsdb dash -- "bearer-token REST" --> obsapi
Audit / BI data flow
Key operational facts
- Host:
ai-ahu= 192.168.83.20 (2× H100 NVL 96 GB, 12 cores, 122 GB RAM, disk ~92% full). SSH key auth asefran. Host nginx servesx056.ahu-azure.val.id(+ OCR demo vhost). Deploy survey:ahu-gpu-manager/docs/deploy/2026-07-04-gpu-host-survey-and-deploy.md. - Deploys are compose-based per service: gateway
~/ahu-gpu-manager/deploy(projectahu-platform), dashboard~/ahu-observatory-dashboard/deploy, OCR~/ahu-ai-staging/ocr/infra(projectahu-ocr-staging, env inenv/shared.env, ship scriptdeploy/build-and-ship.sh). - Engine→gateway wiring is env-only (rollback = restore one env line + recreate): chatbot
MODEL_GATEWAY_URL+EMBEDDER_BASE_URL; OCRGPU_SERVER_URL,AZURE_ON_PREM_BASE_URL,CLASSIFIER_URL(all →http://192.168.83.20:8200; backupsshared.env.bak-*).GPU_SERVER_DIRECT_URLkeeps cache-ops direct. - Queue semantics (CONVENTIONS §3): 429/503 +
Retry-After= graceful degradation, not an error; job polls distinguishqueuedvsprocessing;404 {"code":"JOB_UNKNOWN"}→ resubmit with the same idempotency key. - Per-engine integration prompts (how each engine was wired, and how to wire the next one):
ahu-gpu-manager/docs/integration/.
Update (2026-07-07, later): PaddleOCR flip + P2.8 cache + BI additions
- PaddleOCR is now the LIVE OCR engine for all per-doc-type extraction (Azure DI is per-page billed; paddle is free on-prem). Flipped via env:
OCR_LAYOUT_PROVIDER=paddleocr+KTP/NPWP/CONTACT_INFO/DOMISILI/BUKTI_SETOR/SP_PENDIRIAN_PP/PASSPORT_EXTRACTION_SOURCE=paddleocr+PADDLE_OCR_URL={gateway}+CLEANUP_LLM_URL={gateway}/v1(3B cleanup for paddle card paths, modelcleanup-3b). The Azure DI façade stays deployed as an instant fallback (revert =shared.env.bak-prepaddle). Watch extraction quality per doc type. - P2.8 response cache (tag
p2.8): the gateway caches 200s forcache_ttl_s-enabled sync façade paths (live: paddle/layout, classifier/api/classifier/classify, 7d TTL) keyed by tenant + operation + query + a multipart-aware content fingerprint — document rescans skip queueing and upstream work entirely (verified 2.7ms vs 71ms) and audit ascache_hit. - BI: timestamps + chart tooltips render fixed GMT+7 (WIB);
/api/summarynow returns per-row p50/p95/p99 plus an exact ungroupedoveralldistribution (fleet tile on the executive view); dashboard-native alert banners (error-rate spike ≥20% / queue saturation ≥10s over a 15-min window).
Update 2 (2026-07-08): BI buildout + GPU telemetry
- Dashboard now has 4 views (all roles): Overview, SLA & Throughput (fleet p50/p95/p99 from an exact ungrouped aggregate, per-engine p95 trends, error-rate/queue trends, per-GPU utilization panels), Analytics (calls by operation/model, tokens by engine, cache-hit rate, per-tenant rollup), Report (print/PDF operational report + CSV exports, WIB).
- DCGM GPU telemetry (Health Monitoring): the observatory scrapes the host dcgm-exporter (:9400) every 15s into
gpu_samplesand serves/api/gpu/now+/api/gpu/series; the dashboard raises a GPU-saturation banner at ≥95% util. - Observatory API:
/api/seriesnow supportsgroup_by=operation|statusand returns per-bucketp95_total_ms+cache_hits;/api/summaryrows andoverallcarry cache hits; auditcache_hitis ingested (migration 002). - Success-status vocabulary is
{ok, completed}on every view (job-path OCR calls reportcompleted).
Update 3 (2026-07-08): GPU-health enforcement (tag p2.9)
The gateway now couples real GPU health (host DCGM :9400) into routing + admission — the enforcement half of Health Monitoring (the visibility half shipped in Update 2):
- Gateway /metrics exposes gateway_gpu_{util,mem_used_pct,temp_c,faulted} per GPU + gateway_gpu_shed_total.
- Fault-coupling LIVE (couple_faults: true): a thermally-critical GPU (≥90°C) drops its pinned upstreams from the endpoint rotation (fail-open on stale/missing DCGM data — never drops on no-data). Not firing today (both GPUs 40°C, XID 0).
- XID coupling opt-in (couple_xid: false): DCGM XID_ERRORS latches benign codes, so XID-as-fault is deliberately off until the live XID stream is understood — thermal is the safe default signal.
- Batch-shed built but OFF (shed_batch: false): when enabled, a sustained-saturated (≥97% × 3 polls) upstream sheds BATCH-class admissions (429 GPU_SATURATED); interactive/system are never shed.
- GPU→upstream pinning (verified): gpu 0 → qwen-35b; gpu 1 → cleanup-3b, tei-embeddings, gpu-server, doc-classifier-svc. Per-upstream gpu_ids: in gateway.yaml; dormant for any upstream without it.
Update 4 (2026-07-08): Security analytics — the "catch bad actors" layer
- Observatory:
/api/actors(per-actor behavior rollup: calls, errors, token egress, off-hours WIB activity, ops/model breadth — actor = engine/surface/user, blank user = "unattributed" until X-User-Id is enforced),/api/security-warnings(24h rule evaluation: token-egress outlier vs peer median [critical], error/refusal probing, volume outlier, off-hours activity, bulk body-reading — all evidence-citing, tiny-sample guarded),/api/body-access-stats(auditor-only watcher rollup). Gated operator+auditor. - Dashboard: new Security view (operator+; hidden from executive) — warning banners with explicit all-clear, the per-actor table (toned error-rate/egress/off-hours), the auditor-only "watchers" panel, and an unattributed-share tile that makes the identity gap visible.
- Live on day one it flagged real traffic: an egress outlier + an error-probing pattern on the chatbot-internal actor (test traffic — but exactly the detection path a real incident follows: banner → actors table → Calls explorer → body inspection → verify-chain).
- To sharpen it: enforce
X-User-Id+ tenant tokens (roadmap #5) so actors resolve to real people; later, post-hoc body content-scanning (LLM-as-judge) for exfil/PII patterns.
Update 5 (2026-07-08): Gateway-proxied RAG-search route (tag rag-proxy-v1)
- Why: post production-split, the public chatbot (DMZ) calling
ai-ahu-document-rag(Internal) directly would cross the zone boundary. Migration decision #1 chose a Gateway proxy over a firewall exception. This is that route. - What: a new upstream type
rag-httplets the gateway's existing generalizedsync-facadeadapter proxy RAG'sPOST /searchverbatim (sameSearchRequest/{query,hits[]}shape), audited asoperation=search(new value, added to CONVENTIONS §5 — observatory stores operation as free-text, ingests/groups it fine). Reuses all existing sync-facade machinery: tenant resolution, on-prem enforcement, pool admission, priority (search defaults to interactive), verbatim relay, audit emission. Zero new handler code — the change is a config-validation widening + one audit constant. e2e testTestRAGSyncFacadeSearchEndToEndproves forward+audit; opus adversarial review returned SHIP after one doc-drift fix. - Dormant until configured: with no
rag-httpupstream indeploy/gateway.yaml, the gateway is byte-identical to before. - ENGINE FLIP DONE + VERIFIED 2026-07-08: chatbot landed the header work (commits cf49dbb+bed6b29, scoped
RAG_SEARCH_URLseam soRAG_BASE_URLstill serves /admin/ingest + admin search direct); audited PASS (trace sharing, deterministic idempotency key, 429/503-vs-500 backpressure, dormancy, 5 tests). Flipped the live envRAG_SEARCH_URL=http://192.168.83.20:8200in ahu-ai-staging/chatbot/infra/env/public.env (backup public.env.bak-pre-ragflip), recreated only public-web (agent untouched). Verified: a real public Tanya query produced one turn where theoperation=search(ai-ahu-rag) event and the synthesischatevents SHARE ONE trace_id — retrieval + answer stitched. Rollback: remove the env line, recreate public-web. - RAG
/sessionscross-zone path CLOSED BY DELETION 2026-07-08 (commit f42e355): investigation found it dead (RAG endpoints are stubs returning[]; thread history moved to public-web's local ThreadStore; the UI gates the call to Akun-only so guests never reach it). Rather than build a gateway GET-proxy for a dead path, the chatbot's twoapi/threads/docroutes had their cookie-less RAG passthrough removed (prompt-ai-ahu-chatbot-drop-rag-sessions.md) — cookie-less callers now return empty directly, no DMZ→Internal fetch. Behavior-neutral (guests already got empty); Akun history unchanged.threads-upstreamlib KEPT (still used by internal-web staff Data-Agent threads). This completes the RAG cross-zone boundary work:/searchproxied+live,/sessionsclosed. - Engine flip (hands-off):
docs/integration/prompt-ai-ahu-chatbot-rag-proxy.md— point the public orchestrator'sRagToolbase URL (RAG_BASE_URL) at the gateway + add the §2 headers (the/searchpath and body are unchanged). Also flags the browser-directNEXT_PUBLIC_DOC_RAG_API_URLpath as a separate DMZ-entry question. - DEPLOYED LIVE 2026-07-08 13:33 UTC:
ai-ahu-ragupstream added to the livedeploy/gateway.yaml(backup:gateway.yaml.bak-pre-rag), endpointhttp://192.168.83.20:8110(host-published — docker DNS won't resolve across networks), gateway rebuilt + restarted (9 upstreams, 3 sync paths). End-to-end smoke verified: realPOST /searchthrough the gateway returned live corpus hits in 39 ms, and the observatory recordedoperation=search · engine=ahu-chatbot · surface=public · model=rag-search · upstream=ai-ahu-rag · traffic_class=interactive · status=okwith the request's trace_id. Post-restart LLM path re-verified (200 on /v1/chat/completions). Remaining: the engine-side flip viaprompt-ai-ahu-chatbot-rag-proxy.md(RAG_BASE_URL swap + headers).
Update 6 (2026-07-09): EWS out-of-band notifications + per-person identity gap-closing prompt
- Observatory EWS notifier (tag
obs-ews-notify-v1, DEPLOYED DORMANT): a background loop evaluates the same security + early-warning warnings the dashboard shows, and dispatches NEW/escalating ones to email (SMTP) + webhook with dedup (per stable warning subject), escalate-once, and severity routing. Config blocknotifications:inobservatory.yaml; secrets (SMTP password, webhook token) come from ENV by name (password_env/token_env), never yaml. Dormant untilenabled:true+ ≥1 channel — byte-identical startup otherwise. Adversarial review caught + fixed a critical fingerprint-storm bug (was hashing volatile evidence values → an email every tick; now hashes ID|Metric|Subject only, regression-tested), plus dedup-window/interval bound validation, ctx-bounded SMTP (no goroutine/socket leak), and recover() so a bad tick can't crash the observatory. Fills EWS Notification (40→~85) + Escalation (0→~75). To activate: add anotifications:block to the liveobservatory.yaml, setSMTP_PASSWORD/WEBHOOK_TOKENin the compose env,docker compose restart observatory— Efran's call (real recipients). - Escalation → self-hosted Keep (keephq), DEPLOYED + LIVE + verified end-to-end 2026-07-09: decided against a bespoke escalation UI (reinvents PagerDuty/OnCall); then found Grafana OnCall OSS was archived Mar 2026, so pivoted to Keep (maintained, on-prem). Keep runs on
ai-ahu(VPN-only: UI 192.168.83.20:8330, API :8331, ws :8332;~/keep-deploy/compose.keep.yml, SQLite in./state, telemetry off, NO_AUTH). The observatory EWS notifier is enabled + wired (notifications.webhook.url → http://192.168.83.20:8331/alerts/event, min_severity=warning, every 5m, webhook-only so Keep owns escalation/ack); the webhook emits Keep'sAlertDto(tagobs-keep-webhook-v1) with our stable fingerprint → Keep dedups + auto-resolves. Verified: realsec_error_probing+sec_volume_outlierwarnings landed in Keep, grouped by fingerprint; firing→resolved confirmed. Runbook + hardening backlog (enable auth [top], Postgres, pin images, Keep outbound providers, edge DNS):ahu-ai-observatory/docs/deploy/2026-07-09-keep-deployment.md. Escalation policies / on-call schedules / ack are configured in the Keep UI. - Identity per-person (security view): platform side (observatory + dashboard Security view) done + live; public chat now resolves to real users. Two gaps remain engine-side (internal staff surface 66/68 blank; embeddings unattributed) — closed by prompt
docs/integration/prompt-ai-ahu-chatbot-identity-internal-embed.md(hands-off). RAG quality monitoring is parked pending Efran's Fable signal.
Update 7 (2026-07-09): RAG Quality Monitor — LIVE (tag obs-rag-quality-v1)
- What: post-hoc grades the PUBLIC chatbot's answer quality from the audit trail — zero chatbot changes. The observatory reads each synthesis turn's stored body (question + inlined
HASIL ALATretrieved context + answer), computes deterministic metrics (had_context, num_passages, top_score, refused, parse_ok) + an on-prem LLM-judge (via the gateway,X-Priority: batch, deterministic idem-key) scoring faithfulness / answer-relevance / context-relevance. Stored inrag_quality; served at/api/quality/{summary,recent,series}; new "RAG Quality" dashboard view (operator+); degradation folds into the EWS→Keep pipe (rag_low_faithfulness,rag_ungrounded_spike). - Verified live: 4 real public turns graded (faithfulness 1.0, answer 1.0, context 0.95 w/ Indonesian reasons);
parse_ok_rate=1.0(synthesis-scoped, excludes planning calls). Dormant unlessrag_quality.enabled. - Adversarial review (opus) caught + fixed before enable: judge 429/503 backpressure was terminal → permanent grading holes (now retryable, left un-graded for next tick, per CONVENTIONS §3); parse/refusal rates diluted by non-synthesis planning rows → added
is_synthesiscolumn + synthesis-scoped denominators; grader body reads now recorded inbody_access_audit(PDP, actorrag-quality-evaluator); config validation; question truncation. - Deploy gotchas (documented): (1) judge model must be the FULL vLLM id
Qwen/Qwen3.6-35B-A3B-FP8— theqwen-35bgateway alias 404s (vLLM only accepts its real id). (2) Qwen3 is a reasoning model — the judge sendschat_template_kwargs:{enable_thinking:false}or it burns the token budget on thinking and emits no JSON. (3)docker compose up -dafterbuildmay NOT recreate the container with the new image — use--force-recreate. (4) YAML: quote marker values with trailing colons (question_marker: "PERTANYAAN:"). - Coverage caveat: grades the orchestrator synthesis path (which inlines context into the prompt) — covers public answers broadly. Phase 2 (separate specs): staff/internal surface + a golden Q&A regression set on the same judge harness.
Update 8 (2026-07-10): Golden Q&A regression set — LIVE (tag obs-golden-v1)
- What: RAG Quality phase 2b — a
goldenevalCLI (inahu-ai-observatory/cmd/goldeneval) that drives the public chatbot with a curated question set, scores each answer (a deterministic must_include/must_not_include fact gate + the on-prem LLM judge from phase 1), writesgolden_results, and gates deploys (non-zero exit below pass-rate, on a verified-item regression, or on an unverified set without-allow-unverified).GET /api/quality/golden+ a "Golden set" panel on the dashboard RAG Quality view show the latest run. - Why not live-grade the internal chatbot: the internal/staff chatbot is a text-to-SQL data agent, not doc-RAG — its real risk is query correctness (wrong SQL, faithfully restated), which live faithfulness can't catch. A golden set with SME-verified answers is the right gate for both surfaces; internal live-faithfulness grading was deprioritized.
- HONESTY: the shipped golden set is an example (
verified:false) — the harness + a starter set + an SME guide (docs/golden-set-guide.md); the authoritative Q&A content is AHU SMEs' to author/verify. Both the CLI report and the dashboard flag an unverified set as non-authoritative, and an unverified set can't exit-0 green. - Verified live 2026-07-10: a run drove 7 example items through the real public chatbot; the deterministic gate correctly FAILED an item the judge scored 1.00 (missing required phrase) — proving the fact-floor is judge-independent; 1 genuine pass; below-gate → exit 1 + non-authoritative banner; rows + dashboard panel populated.
- Adversarial review (opus) caught + fixed: regression gate was counting transient (429/503 backpressure) errors as "verified regressions" → would spuriously block deploys; a deterministic fact-fail was discarded when the judge call errored → false green; an all-unverified set could exit 0. All fixed + tested.
- Deferred: internal data-agent golden drive (needs the internal query endpoint + drift-stable questions); CI/deploy-hook wiring (the binary is the integration point).
Deferred roadmap (updated 2026-07-08 — build next, in this order)
- ~~Predictive Intelligence Engine harness~~ — DONE (2026-07-08, tag
obs-predict-v1). Folded into the observatory (Go) — not a new service — querying TimescaleDB directly (no new container/token/HTTP hop). New pureinternal/predictpackage (Holt-Winters -> Holt -> drift -> naive forecasting, z-score/IQR anomaly, evidence-citing early-warning rules) +internal/store.QueryMetricSeries(recombination-safe reductions) + endpoints/api/forecast,/api/anomalies,/api/early-warnings(metric allowlist rejects percentiles). New dashboard "Prediksi" view (/forecast). Honest throughout:insufficient_dataover fabrication; success vocabulary{ok,completed}. Moves Time Series Forecasting / Anomaly Detection / Early Warning System off 0%. Data source is the only DWH seam. - Enable
shed_batchonce saturation behavior is observed; enablecouple_xidafter triaging the live XID stream. - GPU Auto-scaling Engine MVP (readiness sheet: 20%): a scaler loop over the signals we already record (queue depth, admission wait, 429 rate, DCGM util) driving the levers this fleet actually has — dynamic pool resizing, model warm/cold on the second H100, load-shedding hints — and emitting scale-recommendation metrics a future k8s HPA can consume. Design constraint: single-box until the new GPU servers arrive.
- Gateway-registry GPU-health coupling: mark an upstream degraded (stop admitting batch) when its GPU is saturated — the enforcement half of Health Monitoring (the visibility half shipped).
- Per-tenant fair-share quotas (Resource Scheduler): weighted-fair admission within a pool so one tenant cannot monopolize slots; per-tenant daily token/call budgets with 429 QUOTA_EXCEEDED.
- Scheduled reports: the /report view emailed daily/weekly (needs an SMTP/channel decision); plus report presets per role.
- Prod-hardening flip (unchanged, awaiting new GPU servers): synthesis → on-prem,
allow_external_upstreams: false, tenant bearer tokens, HA pair (coordination: redis). - RAG quality monitoring (unchanged): chatbot-side telemetry → RAGAS-style eval harness → golden-set regression.
Known gaps / next steps (in priority order)
- Prod hardening flip (config, awaiting go): live gateway still has
allow_external_upstreams: trueandsynthesis → ext-dashscope-397b(cloud). Prod = repoint synthesis to on-premqwen-35b+ setfalse. - Tenant tokens: tenants are header-trust (
token: ""); real bearer tokens exist in the machinery but aren't issued. - HA:
coordination: local— single gateway instance; redis-backed coordination is built, not deployed. Server specs for MVP/HA already sent to DevOps. - RAG quality monitoring: observatory sees calls, not RAG semantics (no retrieved-doc ids / groundedness). Planned next initiative — starts with chatbot-side telemetry.
- Small logged follow-ups: gateway façade events lack
upstream_class; Azure completiontraffic_classmirrors the poll; streaming relay for very large Azure results; dashboard chart hover-tooltips still Carbon-default time (axes are WIB); p95/p99 percentiles + alerting.