Spaces:
Running
Running
| # تبيان الطبي — Deployment Guide | |
| ## Prerequisites | |
| - Docker ≥ 24 + Docker Compose v2 | |
| - A server with ≥ 4 GB RAM, ≥ 20 GB disk | |
| - Domain name with DNS pointed to your server (for HTTPS) | |
| --- | |
| ## 1. Environment Setup | |
| Copy and populate the environment file: | |
| ```bash | |
| cp .env.example .env | |
| ``` | |
| Required variables: | |
| ``` | |
| GROQ_API_KEY=gsk_... | |
| SUPABASE_URL=https://<project>.supabase.co | |
| SUPABASE_KEY=eyJ... | |
| SUPABASE_DB_URL=postgresql+psycopg://... | |
| FRONTEND_URL=https://your-domain.com | |
| NEXTAUTH_SECRET=<32+ random chars> | |
| ``` | |
| Optional (features degrade gracefully without them): | |
| ``` | |
| COHERE_API_KEY=... # reranking (falls back to BM25 only) | |
| GOOGLE_VISION_API_KEY=... # Vision OCR (falls back to EasyOCR) | |
| GOOGLE_TTS_KEY=... # Google TTS (falls back to gTTS) | |
| ELEVENLABS_API_KEY=... # ElevenLabs TTS | |
| SENTRY_DSN=... # Error monitoring | |
| ``` | |
| --- | |
| ## 2. Nginx Configuration | |
| Create `nginx/nginx.conf`: | |
| ```nginx | |
| events { worker_connections 1024; } | |
| http { | |
| upstream backend { server backend:8000; } | |
| upstream frontend { server frontend:3000; } | |
| server { | |
| listen 80; | |
| server_name your-domain.com; | |
| return 301 https://$host$request_uri; | |
| } | |
| server { | |
| listen 443 ssl; | |
| server_name your-domain.com; | |
| ssl_certificate /etc/nginx/certs/fullchain.pem; | |
| ssl_certificate_key /etc/nginx/certs/privkey.pem; | |
| client_max_body_size 25M; | |
| location /api/ { proxy_pass http://backend; proxy_set_header Host $host; } | |
| location /health { proxy_pass http://backend; } | |
| location / { proxy_pass http://frontend; proxy_set_header Host $host; } | |
| } | |
| } | |
| ``` | |
| Place TLS certificates in `nginx/certs/` (Let's Encrypt recommended). | |
| --- | |
| ## 3. Frontend Dockerfile | |
| Create `frontend/Dockerfile`: | |
| ```dockerfile | |
| FROM node:20-alpine AS builder | |
| WORKDIR /app | |
| COPY package*.json ./ | |
| RUN npm ci | |
| COPY . . | |
| ARG NEXT_PUBLIC_API_URL | |
| ENV NEXT_PUBLIC_API_URL=$NEXT_PUBLIC_API_URL | |
| RUN npm run build | |
| FROM node:20-alpine | |
| WORKDIR /app | |
| COPY --from=builder /app/.next/standalone ./ | |
| COPY --from=builder /app/.next/static ./.next/static | |
| COPY --from=builder /app/public ./public | |
| ENV PORT=3000 | |
| EXPOSE 3000 | |
| CMD ["node", "server.js"] | |
| ``` | |
| Enable standalone output in `next.config.js`: | |
| ```js | |
| module.exports = { output: "standalone" } | |
| ``` | |
| --- | |
| ## 4. Deploy | |
| ```bash | |
| # Build and start all services | |
| docker compose -f docker-compose.prod.yml up -d --build | |
| # Check logs | |
| docker compose -f docker-compose.prod.yml logs -f backend | |
| # Check health | |
| curl https://your-domain.com/health | |
| ``` | |
| --- | |
| ## 5. Supabase Setup | |
| Run once to set up the pgvector extension and required tables: | |
| ```sql | |
| -- Enable pgvector | |
| create extension if not exists vector; | |
| -- Documents table for RAG | |
| create table if not exists documents ( | |
| id bigserial primary key, | |
| content text, | |
| embedding vector(1024), | |
| metadata jsonb | |
| ); | |
| create index on documents using ivfflat (embedding vector_cosine_ops) with (lists = 100); | |
| -- match_documents RPC | |
| create or replace function match_documents( | |
| query_embedding vector(1024), | |
| match_count int default 10, | |
| filter jsonb default '{}' | |
| ) | |
| returns table(id bigint, content text, metadata jsonb, similarity float) | |
| language plpgsql as $$ | |
| begin | |
| return query | |
| select d.id, d.content, d.metadata, | |
| 1 - (d.embedding <=> query_embedding) as similarity | |
| from documents d | |
| order by d.embedding <=> query_embedding | |
| limit match_count; | |
| end; | |
| $$; | |
| -- Analyses table | |
| create table if not exists analyses ( | |
| id uuid default gen_random_uuid() primary key, | |
| session_id text not null, | |
| findings jsonb, | |
| summary text, | |
| report jsonb, | |
| created_at timestamptz default now() | |
| ); | |
| create index on analyses(session_id, created_at desc); | |
| ``` | |
| --- | |
| ## 6. PEFT/LoRA Fine-Tuning (Optional) | |
| To improve Arabic medical analysis quality with your own data: | |
| ```bash | |
| # 1. Prepare dataset from Supabase analyses | |
| python training/prepare_dataset.py --source supabase --output_dir data/ | |
| # 2. Train LoRA adapter (requires GPU with ≥16 GB VRAM) | |
| python training/train_lora.py \ | |
| --model_name core42/jais-13b-chat \ | |
| --data_dir data/ \ | |
| --output_dir checkpoints/tebyan-v1 \ | |
| --num_epochs 3 \ | |
| --load_4bit | |
| # 3. Evaluate | |
| python training/evaluate_model.py \ | |
| --base_model core42/jais-13b-chat \ | |
| --lora_adapter checkpoints/tebyan-v1/lora_adapter \ | |
| --val_data data/val.jsonl \ | |
| --use_llm_judge \ | |
| --groq_key $GROQ_API_KEY | |
| ``` | |
| Results are written to `eval_results/eval_summary.json`. | |
| --- | |
| ## 7. Monitoring | |
| The `/api/metrics` endpoint exposes Prometheus-format metrics. Scrape with: | |
| ```yaml | |
| # prometheus.yml | |
| scrape_configs: | |
| - job_name: tebyan | |
| static_configs: | |
| - targets: ['your-domain.com'] | |
| metrics_path: /api/metrics | |
| scheme: https | |
| ``` | |
| Audit logs are written to the `audit_logs` Docker volume at `logs/audit/audit.jsonl` (rotating, max 50 MB × 10 files). | |
| --- | |
| ## 8. Updating | |
| ```bash | |
| git pull | |
| docker compose -f docker-compose.prod.yml up -d --build --no-deps backend | |
| docker compose -f docker-compose.prod.yml up -d --build --no-deps frontend | |
| ``` | |
| The `--no-deps` flag updates only the specified service without restarting nginx or volumes. | |