Architektura — Přehled¶
Tato kapitola popisuje co je za komponentu zodpovědné za co a jak spolu komunikují. Detail per komponenta je v Komponenty, data flow v Pipeline.
High-level diagram¶
graph TB
User([Uživatel])
NetteApp([Nette aplikace<br/>Phase E])
subgraph CloudflareFE["☁️ Cloudflare Workers"]
direction LR
FE[Frontend<br/>Next.js 16 SSR]
end
subgraph DockerContainers["🐳 Docker kontejnery"]
direction LR
BE[Backend<br/>FastAPI]
Redis[(Redis<br/>queue + rate limit)]
Worker[Worker<br/>taskiq]
end
subgraph External["🤖 Externí AI služby"]
direction LR
Mistral[Mistral OCR]
Gemini[Gemini 3 Flash<br/>+ Embedding 2]
Cohere[Cohere Rerank 4.0]
end
subgraph Supabase["⚡ Supabase"]
direction LR
Auth[Auth<br/>magic link]
DB[(Postgres + RLS<br/>nemoreport schema)]
Storage[Storage<br/>4 buckety]
Realtime[Realtime<br/>WebSocket]
end
%% INVISIBLE EDGES — force vertical stacking order
CloudflareFE ~~~ DockerContainers
DockerContainers ~~~ External
External ~~~ Supabase
%% User-facing flow
User -->|HTTPS| FE
NetteApp -.->|Phase E<br/>JWT bridge + HMAC| BE
%% Frontend → Backend / Supabase
FE -->|JWT auth REST| BE
FE -->|signInWithOtp| Auth
FE <-->|WebSocket| Realtime
%% Backend internal
BE -->|enqueue| Redis
Worker -->|consume stream| Redis
%% Docker → External AI (vertical down)
BE -->|embed query| Gemini
BE -->|rerank| Cohere
Worker -->|OCR + annotations| Mistral
Worker -->|figure annot.<br/>+ embed| Gemini
%% Docker → Supabase (verify, DB, storage)
BE -->|verify JWT| Auth
BE -->|service_role + signed URLs| DB
BE -->|signed URLs| Storage
Worker -->|service_role| DB
Worker -->|read/write files| Storage
%% Realtime publish
DB -->|publish UPDATE| Realtime
%% Color coding
classDef userClass fill:#e8f5e9,stroke:#2e7d32,stroke-width:2px,color:#000
classDef containerClass fill:#fff3e0,stroke:#e65100,stroke-width:2px,color:#000
classDef aiClass fill:#f3e5f5,stroke:#6a1b9a,stroke-width:2px,color:#000
classDef supabaseClass fill:#e3f2fd,stroke:#1565c0,stroke-width:2px,color:#000
class User,NetteApp userClass
class BE,Worker,Redis,FE containerClass
class Mistral,Gemini,Cohere aiClass
class DB,Auth,Storage,Realtime supabaseClass
%% Invisible link styling (poslední 3 linky = invisible edges)
linkStyle 0 stroke-width:0px,fill:none
linkStyle 1 stroke-width:0px,fill:none
linkStyle 2 stroke-width:0px,fill:none
Princip designu¶
1. Multi-tenant first¶
Každý uživatel má personal_tenant vytvořený automaticky při registraci (přes handle_new_user trigger v auth.users). RLS policy na všech nemoreport.* tabulkách scope-uje data per tenant_id.
JWT custom claims (custom JWT hook v migraci 0007):
personal_tenant_id— vždy vlastní tenantactive_tenant_id— aktuální workspace (default = personal, později team workspaces)
Backend extrahuje tyto claims, předává active_tenant_id při insertech, RLS pak enforce-uje v DB.
2. Embed-first frontend¶
Frontend obsahuje dva typy stránek:
- Parent UI (
/,/reports,/upload,/admin,/migrate) — testing shell pro testery; v produkci ho nahradí Nette aplikace - Embed chat (
/chat?id=...) — iframe child, komunikuje s parent přes postMessage protokolnemoreport-ai:*
Vše se vyvíjí jako embed — i když parent je teď náš testing UI, chat je vždy iframe. Phase E změní jen parent origin + JWT source.
3. Async ingestion s durable queue¶
Upload PDF nemá čekat na 5min OCR. Backend:
- Validuje + uloží do storage + DB row
- Enqueue worker job přes Redis Stream (taskiq + RedisStreamBroker)
- Vrátí 202 Accepted
Worker:
- Konzumuje stream s ack-based delivery
- Provádí 5-stage pipeline (scan → parse → annotate → embed → finalize)
- Updatuje
reports.statuspo každém stage - Realtime emit přes Postgres
WAL → supabase_realtimepublication
Frontend subscribe na Realtime channel → vidí progress live.
Retry strategy: každý stage je @broker.task(retry_on_error=True, max_retries=3). Idempotentní (DELETE existing data před re-run).
4. Defense-in-depth security¶
Vrstvy ochrany dat:
- Network: HTTPS, internal Docker DNS pro privát komunikaci mezi kontejnery
- Auth: Supabase JWT s RS256, JWKS rotation, custom claims
- Authz: RLS policies per table,
private.user_has_tenant_access(tenant_id)helper - Defense-in-depth grants:
revoke insert/update/delete from authenticatedna worker-managed tabulky (figures, parsed_*, chunks, retrieval_log) — i kdyby se omylem přidala permissive policy, table grant by ji blokoval - Sequence grants: explicitní USAGE grants per sequence (zachyceno v Phase A.1 hotfixu)
- HMAC: Nette attachments verify HMAC nad canonical string
<report_id>|<nette_id>|<attachment_type>|<sha256(file_bytes)>
Viz Bezpečnost.
5. Soft-fail graceful degradation¶
Phase C decision D7: pokud embed_target stage selže (např. Gemini API outage), report přejde do status='ready' přesto. parsed_metadata.embedding_status='failed' se zapíše. BM25 leg pořád funguje, vector leg ne. Phase D chat bude fallbackovat na full-report dump pro reporty bez chunks.
Stejný pattern u Cohere reranku v /retrieve — pokud API down/rate limit, fallback na hybrid RRF order.
Data flow zjednodušeně¶
1. UPLOAD User → FE → BE → DB row + Storage upload + Redis enqueue → 202
2. WORKER Redis stream → Worker → 5 stages → DB updates → Realtime
3. STATUS Realtime → FE → progress bar
4. READY User vidí "Hotovo" → /reports/{id} detail
5. RETRIEVE User query → FE → BE → embed query → Postgres RPC (hybrid) → Cohere rerank → top-K chunks
6. CHAT (Phase D) → BE volá retrieve interně → injektuje chunks do LLM prompt → SSE stream
Detail viz Pipeline (data flow).
Folder model¶
Klíčový koncept Phase B post-deploy iterace.
Report = container, ne single file. Reálný NemoReport obsahuje:
- 1 main report (PDF / MHTML s 12-18 sekcemi)
- 5-15 attachments (vyjádření CETIN, ČEZ, geometrický plán, scan razítka, foto pozemku...)
- Cross-source figures (mapy povodní, územního plánu, technické výkresy)
Implementace v DB:
nemoreport.reports= parent containernemoreport.attachments= file rows uvnitř reportu (FKreport_id,source ∈ {nette, user_upload})nemoreport.parsed_sectionsmáattachment_id(NULL = main report)nemoreport.figures.report_idje VŽDY parent (i pro figures z attachmentu);attachment_idje optional FKnemoreport.chunks.report_id= parent → folder retrieval jediným SELECTem
Worker _Target dataclass sjednocuje "report" a "attachment" cesty — všechny stages pracují generic.
Phase rozdělení¶
Phase A — Auth + multi-tenant (✅ hotovo)¶
Supabase Auth setup, JWT custom claims, RLS policies, 12 migrací (0001-0012), pgTAP testy, custom magic link template.
Phase A.1 — UI port (✅ hotovo)¶
v1 → v2 frontend port: /reports, /chat, /admin přepsány na v2 schema, Vitest scaffold, TS strict.
Phase B — Ingestion pipeline (✅ hotovo)¶
5-stage worker pipeline. PDF → Mistral OCR. MHTML → BS4 + trafilatura. DOCX → python-docx. Gemini fallback pro figures s thin/no annotation. Folder model post-deploy. Cost tracking per stage.
Phase C — Vector RAG (✅ hotovo)¶
pgvector 0.8 + halfvec(1536) + Gemini Embedding 2 multimodal. Hybrid retrieval (vector + BM25 + RRF) + Cohere Rerank 4.0. HyDE pro krátké queries. Per-source diversity. Retrieval observability.
Phase D — Chat s RAG (⏳ plánováno)¶
/chat endpoint přepsat na use /retrieve interně. Injektovat top-K chunks místo full textu. Streamovaný SSE response s citacemi.
Phase E — Nette integration (⏳ plánováno)¶
Embed chat do Nette aplikace (parent iframe). JWT bridge (Nette signed RS256 JWT → Supabase session). HMAC webhooks pro automatické attachment uploady.
Detail per fázi viz Vývojové fáze.