Přeskočit obsah

Komponenty

Detail každé komponenty v systému: co dělá, kde běží, jak se konfiguruje.

Frontend (Cloudflare Worker)

Repo: Algawebbusiness/nemoreport-ai-frontend-v2 (private) URL: https://nemoreport-ai-frontend-v2.algaweb.workers.dev Stack: Next.js 16 + Tailwind v4 + Supabase SSR + Zustand Deploy: npm run deploy (wrangler) — manuální, ne auto

Co dělá

  • SSR rendering — server components fetch dat z Backend API přes JWT v cookies
  • Auth flow — magic link sign-in, session management (Supabase SSR cookies)
  • Upload UI — drag-and-drop, progress tracking přes Supabase Realtime
  • Reports list + detail — grid karet + folder detail s přílohami
  • Chat iframe (Phase A.1 port, Phase D bude rewire na RAG) — embed-first design
  • Admin dashboard — feedback, providers, cost (ADMIN_HASH gated)
  • Migrate UI — claim flow pro v1 testery

Architektonické pivoty

CF Pages → CF Workers: původní plán Pages + static export narazil na nekompatibilitu s Supabase SSR auth (middleware + server components potřebují Node-like runtime). Řešeno: @opennextjs/cloudflare adapter, deploy jako Worker. Build 6.3 MB / 1.3 MB gzip.

Žádný proxy.ts middleware: Next 16 proxy nepodporuje Edge runtime a CF Workers nemají Node.js. Session refresh se děje pasivně přes @supabase/ssr cookie handling při každém server-component requestu. getClaims() v root page.tsx + protected routes + apiFetch 401 fallback = funkční auth gating bez centralizovaného middleware.

Key files

  • src/app/ — Next.js 16 app router pages
  • src/components/ — reusable React komponenty
  • src/lib/supabase/{client,server}.ts — Supabase SSR clients (browser + server)
  • src/lib/api.ts — typed wrapper kolem fetch s JWT injection
  • next.config.ts + open-next.config.ts + wrangler.jsonc — CF Workers config

Backend (Docker kontejner)

Repo: Algawebbusiness/nemoreport-ai-backend-v2 (private, sdílí codebase s workerem) URL: https://nemoreport-ai-backend-v2.sliplane.app Service ID: service_gp6fgtvypx2q Stack: FastAPI 0.135 + Pydantic AI + uvicorn + slowapi (rate limit) Deploy: auto-deploy z main branch (Docker hosting GitHub integration)

Co dělá

  • REST API pro frontend (JWT-authed) i Nette integration (HMAC-authed)
  • Storage uploads přes Supabase service_role (bypass RLS for path-scoped buckets)
  • Async job dispatch — enqueue worker tasks přes taskiq broker
  • Rate limiting — slowapi s Redis backendem (per-IP defaults)
  • Auth verification — JWT přes JWKS cache, custom claims extraction, HMAC verification pro Nette path
  • Retrieval orchestration (Phase C) — embed query, RPC call, Cohere rerank, response assembly

Key files

app/
├── main.py              # FastAPI app + middleware + router registration
├── config.py            # Pydantic Settings (fail-fast on missing secrets)
├── auth.py              # JWT verification, AuthUser, require_admin, verify_nette_jwt (Phase E)
├── deps.py              # FastAPI dependencies (CurrentUser, ServiceDB, RequireAdmin)
├── db.py                # DB facade (40+ methods, service_role + user_jwt clients)
├── storage.py           # Storage helpers (upload/download/sign URL/path builders)
├── supabase_client.py   # Supabase Python client factories
├── rate_limit.py        # slowapi limiter setup
├── routers/             # FastAPI routers (1 file per concern)
│   ├── ingestion.py     # POST /ingest, /uploads, GET /attachments
│   ├── retrieve.py      # POST /retrieve (Phase C)
│   ├── chat.py          # POST /chat (legacy, Phase D rewrite TBD)
│   ├── reports.py       # GET /reports, /reports/{id}
│   ├── admin.py         # /admin/* (HASH-gated)
│   ├── nette.py         # /reports/{id}/attachments/system (HMAC)
│   ├── me.py            # GET /me
│   ├── migrate.py       # /migrate/* (v1 → v2 claim)
│   └── ...
├── ingestion/           # Phase B helpers (Mistral, schemas, chunking)
├── embedding/           # Phase C — Gemini Embedding 2 wrapper
├── retrieval/           # Phase C — query orchestration, rerank, rewrite
└── worker.py            # taskiq tasks (5-stage pipeline)

Konfigurace

ENV vars (Docker hosting secrets), všechny strict-validated v config.py:

SUPABASE_URL                    # https://cubdrgjdkatyecrgckwp.supabase.co
SUPABASE_ANON_KEY               # PostgREST JWT-based ops
SUPABASE_SERVICE_ROLE_KEY       # NEVER send to FE, secret
GEMINI_API_KEY                  # Gemini 3 Flash + Embedding 2
MISTRAL_API_KEY                 # Mistral OCR
COHERE_API_KEY                  # Cohere Rerank 4.0 (Phase C.7)
ADMIN_HASH                      # /admin/* gate
NETTE_HMAC_SECRET               # Phase B.11 attachments endpoint
ALLOWED_ORIGINS                 # CORS — workers.dev,localhost:3000
RATE_LIMIT_STORAGE_URI          # redis://nemoreport-redis.internal:6379/0
REDIS_URL                       # taskiq broker — db 1
EMBEDDING_MODEL                 # gemini-embedding-2 (GA)
EMBEDDING_MODEL_VERSION         # ga-2026-04
COHERE_RERANK_MODEL             # rerank-v4.0-pro
COHERE_RERANK_ENABLED           # true / false
LLM_MODEL                       # google-gla:gemini-3-flash-preview
APP_ENV                         # production / staging / development

Worker (Docker kontejner)

Service ID: service_p8nz4hbtcw1r Codebase: stejný jako Backend (nemoreport-ai-backend-v2) CMD: uv run python -m app.worker_entry Stack: taskiq + RedisStreamBroker + Pydantic AI + Mistral SDK

Co dělá

Konzumuje Redis Stream nemoreport_ingestion (consumer group nemoreport_workers), provádí 5-stage pipeline:

  1. scan_target — re-validate MIME + transition status='parsing'
  2. parse_target — Mistral OCR (PDF) / BS4 (MHTML) / python-docx (DOCX) → INSERT parsed_sections + figures
  3. annotate_target — Gemini multimodal fallback pro figures s annotation_source='pending'
  4. embed_target — chunkování + Gemini Embedding 2 (text + multimodal) → INSERT chunks
  5. finalize_target — status='ready'

Each stage je samostatný taskiq task s retry_on_error=True, max_retries=3 a idempotentní (DELETE existing data před re-run).

Lifecycle

app/worker_entry.py — production runner:

  • Spustí uvicorn na :8000 (container healthcheck)
  • Spustí taskiq worker subprocess (taskiq worker app.worker:broker --max-async-tasks 10 --ack-type when_executed)
  • SIGTERM propagation, supervisor thread

2 worker procesy × 10 async tasks = až 20 souběžných stages.

Redis (Docker kontejner)

Service ID: service_omzjff1bshe5 Internal DNS: nemoreport-redis.internal:6379 Persistence: AOF (append-only file)

DB rozdělení

DB Použití
redis://...:6379/0 slowapi rate limit storage
redis://...:6379/1 taskiq broker (nemoreport_ingestion stream + result backend, 1h retention)

Supabase

Project ref: cubdrgjdkatyecrgckwp URL: https://cubdrgjdkatyecrgckwp.supabase.co

Postgres + RLS

Schema: nemoreport (NOT public — explicit isolation) Tabulky: 22 (vytvořené 18 migracemi 0001-0018)

Detail viz Datový model a Databáze.

Auth

  • Magic link přes Resend SMTP (sandbox = jen owner email zatím)
  • Custom JWT hook (private.custom_access_token_hook) injektuje claims:
  • personal_tenant_id
  • active_tenant_id
  • Custom email template — token_hash query flow (ne implicit hash flow)
  • handle_new_user trigger — auto-creates personal tenant + user_profiles + tenant_member

Config v supabase/config.toml (commited do repa, push přes supabase config push --yes — žádná Dashboard click-ops).

Storage (4 buckety)

Bucket Účel Path layout
nemoreport-uploads main uploaded files (user uploads) {tenant_id}/reports/{report_id}/original.{ext}
nemoreport-attachments přílohy (Nette + user_upload) {tenant_id}/reports/{report_id}/attachments/{attachment_id}.{ext}
nemoreport-figures extrahované obrázky / mapy / výkresy {tenant_id}/reports/{report_id}/figures/{figure_id}.{ext}
nemoreport-reports legacy (z Phase A migrace 0004, v2 nepoužívá)

Storage backend: Supabase managed storage (jejich infrastruktura, S3-compatible API). Cloudflare R2 přímo NEPOUŽÍVÁME — cf-ray headers v Supabase Storage responses jsou jen kvůli tomu, že Supabase má Cloudflare jako CDN/edge proxy před svým API (vendor implementační detail).

Co je v DB: jen relativní storage_path (např. {tenant_id}/reports/{report_id}/original.pdf), žádné celé URL.

Jak frontend dostane file: backend volá supabase.storage.from_(bucket).create_signed_url(path, expires_in=3600) → vrací time-limited signed URL na endpoint https://cubdrgjdkatyecrgckwp.supabase.co/storage/v1/object/sign/....

RLS policies: bucket má path-based scoping {tenant_id}/... — user vidí jen svoje cesty. Service role bypassuje (worker, BE).

Realtime

Publication supabase_realtime obsahuje:

  • nemoreport.reports
  • nemoreport.attachments

(NE chunks, figures, parsed_sections — frontend si je pulluje on-demand když status='ready'.)

Frontend subscribe per report_id, dostává UPDATE events pro live status tracking.

Externí AI služby

Detail viz Externí API.

Mistral OCR

  • Model: mistral-ocr-latest
  • Účel: PDF + image OCR + bbox annotations + document annotations
  • Cena: $0.001-0.030 / page
  • Limity: HTTP timeout 15 min (config mistral_timeout_ms=900_000), velké PDFs > 500 KB skipnu bbox annotations
  • License: komerční API (Mistral AI)

Google Gemini

  • Modely: gemini-3-flash-preview (LLM), gemini-embedding-2 (GA, embedding)
  • Účel: figure annotation (multimodal), HyDE generation, embedding (text + multimodal)
  • Cena: ~$0.0001-0.001 / call
  • Limity: standard Gemini quotas (1500 RPM tier 1)
  • License: Google AI Terms of Service (commercial OK)

Cohere Rerank 4.0

  • Modely: rerank-v4.0-pro (default), rerank-v4.0-fast (cost fallback)
  • Účel: cross-encoder rerank top-N kandidátů z hybrid retrieve
  • Cena: $0.0025 / search (= 1 query, top_n libovolný)
  • Limity: 32K context, 100+ jazyků vč. CZ
  • Trial limit: ~1000 calls / měsíc, 10 RPM (potřeba production key pro pilot)
  • License: Cohere Terms of Service (commercial OK pro paid tier)

Docker kontejnery

3 Docker services na společném hostu (single Docker daemon, internal network):

Service Účel Spec
nemoreport-ai-backend-v2 FastAPI Single instance, auto-deploy z GitHub main
nemoreport-ai-worker-v2 taskiq worker 2 procs × 10 tasks (až 20 simultánních)
nemoreport-redis Redis 7 (queue + cache) AOF persistent

Internal Docker network umožňuje BE → Redis a Worker → Redis komunikaci bez public exposure (stejný host, internal DNS resolution).

Aktuální hosting platforma viz Deployment.

Cloudflare

Pages projekt: ❌ původně plánovaný, opuštěný kvůli SSR nekompatibilitě Workers: nemoreport-ai-frontend-v2 (manuální deploy přes wrangler) R2: NEPOUŽÍVÁME přímo. Supabase Storage má vlastní backend (vendor managed).

Verzování + dual push

Kód je v dvou git remotes:

  • GitHub (primary): Algawebbusiness/nemoreport-ai-{backend,frontend}-v2
  • GitLab (mirror): git.algaweb.cz/algaweb/nemoreport-ai-{backend,frontend}-v2

origin má oba pushURL — git push origin main pushne na oba zároveň. fetch jen GitHub.