SPB Git

spb/uqo-chat

Public
4.8 KB

# UQO-Chat

Tuteur IA pour les cours IMM1003 — Éléments d'évaluation immobilière et IMM1033 — Méthodes du coût (UQO). Production : https://www.uqo-chat.app · Spécification complète : CLAUDE.md.

Le tuteur explique la matière en citant les notes de cours officielles (RAG), exécute du Python dans un bac à sable isolé, produit des classeurs Excel à formules vivantes, cherche des données de marché actuelles (Firecrawl), analyse les fichiers déposés et génère des quiz interactifs. Tous les modèles (Claude, GPT) passent par OpenRouter avec repli automatique.

# Architecture (déploiement MacLustr)

text
Internet ── ngrok (www.uqo-chat.app) ──► uqo-chat-api  :8190  FastAPI + SSE + SPA React (PM2)
                                              │
                                              ├──► uqo-chat-sandbox :8191  (PM2, sandbox-exec : sans réseau)
                                              ├──► SQLite  data/uqochat.db  (index BM25 en mémoire)
                                              ├──► fichiers data/files/ (TTL 7 j / 30 j épinglés)
                                              └──► OpenRouter · Firecrawl (HTTPS sortant)

Les manifestes Kubernetes (k8s/) et docker-compose.yml reproduisent la cible décrite dans la spécification (Postgres + pgvector, Redis, MinIO, sandbox durci) pour un futur cluster ; la production actuelle tourne sur un nœud Mac du cluster via mld (voir deploy/).

# Démarrage local

bash
cp .env.example .env            # OPENROUTER_API_KEY, FIRECRAWL_API_KEY, RESEND_API_KEY…
make setup                      # venvs uv (backend, sandbox) + npm install
make sandbox &                  # :8191
make api &                      # :8190 (sert frontend/dist s'il existe)
make web                        # :5173 avec proxy /api
make ingest COURSE=imm1033 SITE=~/Desktop/Academique/UQO/UQO_COURS/_Site_web/dist/imm1033
make test && make lint

Connexion : le professeur inscrit les courriels des étudiant·es (onglet Étudiants) ; chacun·e reçoit un courriel Resend « Bienvenue sur UQO-Chat » avec un lien /mot-de-passe?token=… (valide INVITE_TTL_DAYS) pour choisir son mot de passe, puis se connecte avec courriel + mot de passe. « Première connexion ou mot de passe oublié » renvoie un lien (RESET_TTL_MINUTES). Seules les adresses inscrites (ou listées dans PROFESSOR_EMAILS / ADMIN_EMAILS / INVITED_EMAILS) peuvent demander un lien. Sans RESEND_API_KEY (ni SMTP), les liens sont affichés dans le tableau de bord pour être copiés ; en dev, /auth/forgot renvoie dev_link.

# Déploiement

bash
make build                                       # frontend/dist
~/Desktop/cluster-skill/mld stage $PWD uqo-chat  # laptop → passerelle
~/Desktop/cluster-skill/mld deploy uqo-chat      # nœud choisi par score (--node X pour forcer)

Le manifeste de production (M1M32:~/dispatch/apps/uqo-chat.json) contient les secrets ; la copie du dépôt (deploy/uqo-chat.manifest.json) n'a que des gabarits. Après un premier déploiement, ingérer le matériel de cours sur le nœud :

bash
scp -r ~/Desktop/Academique/UQO/UQO_COURS/_Site_web/dist/imm10{03,33} <nœud>:~/apps/uqo-chat/content/
ssh <nœud> 'cd ~/apps/uqo-chat/backend && for c in imm1003 imm1033; do \
  DATA_DIR=~/apps/uqo-chat/data .venv/bin/python -m scripts.ingest --course $c --site ../content/$c; done'
pm2 restart uqo-chat-api

# Écarts assumés par rapport à la spécification

Spécification Réalisation actuelle Pourquoi
PostgreSQL + pgvector, Redis, MinIO, arq SQLite (SQLAlchemy async), cache/limiteur en mémoire, fichiers sur disque, tâches asyncio Déploiement mono-nœud sans dépendances ; DATABASE_URL accepte déjà Postgres, k8s/ décrit la cible complète
Embeddings openai/text-embedding-3-large via OpenRouter BM25 (index en mémoire) + embeddings optionnels via un endpoint OpenAI-compatible (EMBEDDINGS_BASE_URL) OpenRouter n'expose aucun modèle d'embeddings (vérifié 2026-09-05)
Lien magique SMTP à chaque connexion Invitation Resend → mot de passe choisi par l'étudiant·e (PBKDF2), « mot de passe oublié » par courriel ; liste blanche gérée par le professeur Une seule étape par courriel, puis connexion classique ; Resend (RESEND_API_KEY) plutôt qu'un SMTP UQO
Pods sandbox gVisor / Job k8s Service sandbox-runner séparé, sandbox-exec macOS (réseau interdit, écriture confinée) + rlimits + timeout Équivalent local ; sandbox-runner/Dockerfile + NetworkPolicy prêts pour k8s
Alembic create_all au démarrage Une migration initiale sera ajoutée au premier changement de schéma
Export PDF de conversation, mode hors-ligne complet Non faits (PWA : app shell + cache lecture) Phase 2/3
Files · f7b72d80f2bbde2540a0689dd9a0bffc67e6a1b6
Menu

Theme