SPB Git forge

spb/ora-ka

Public

Ora-Ka — cinq agrégateurs Ka, une barre de recherche hybride (exact + sémantique)

4commits 1branches 0releases
13.4 MBsize
maindefault branch
22 days agolast push
Python 80% TypeScript 12.9% CSS 6.8%
ZIP tar.gz
NameLast commitUpdated
apps chore: repo ora-ka relié sur M4M64a + README ultra détaillé + visite... 29 days ago
docs chore: repo ora-ka relié sur M4M64a + README ultra détaillé + visite... 29 days ago
oraka Ora-Ka v2 — moteur hybride, SSO KA ID, README complet (badges,... 1 mo ago
scripts Ora-Ka v2 — moteur hybride, SSO KA ID, README complet (badges,... 1 mo ago
.gitignore chore: repo ora-ka relié sur M4M64a + README ultra détaillé + visite... 29 days ago
README.md chore(retrait cluster): synchro copie du nœud M4M64a + README « URL... 22 days ago
requirements.txt Ora-Ka v2 — moteur hybride, SSO KA ID, README complet (badges,... 1 mo ago
run.py Ora-Ka v2 — moteur hybride, SSO KA ID, README complet (badges,... 1 mo ago
README.md source

⚠️ URL live hors fonction pour le moment. L'application a été retirée du cluster MacLustr le 2026-09-04 (processus arrêtés, copie du nœud supprimée). Ce dépôt spbgit est désormais la seule source de vérité du projet. Ancienne URL : https://www.ora-ka.com

# Ora·Ka — Cinq agrégateurs. Une barre.

Auteur : Simon-Pierre Boucher — contact@spboucher.ai

Live Python FastAPI React SQLite Embeddings Inscriptions Node

Ora-Ka unifie les cinq agrégateurs automatisés de la famille Ka en une seule plateforme : une barre de recherche, un moteur hybride exact + sémantique, et les cinq applications d'origine reproduites fidèlement sous leur propre préfixe — sans modifier une ligne de leurs backends.

Un seul point d'entrée pour cinq univers : propriétés à vendre (Immo·Ka), logements à louer (Lou·Ka), produits québécois (Fabri·Ka), véhicules d'occasion (Auto·Ka) et épicerie comparée (Food·Ka). L'utilisateur tape une requête en langage naturel — « condo 2 chambres montréal », « fromage du québec », « VUS hybride récent » — et Ora-Ka répond avec les meilleures inscriptions, tous univers confondus, classées par un score unifié.

Landing Ora-Ka

Ora-Ka mobile · mobile

Captures du 2026-08-25.

# Métriques (prod, 2026-08-13)

Univers Préfixe Contenu Inscriptions Sources Accent
Immo·Ka /immo/ Propriétés à vendre (QC) 48 487 33 #e23744
Lou·Ka /lou/ Logements à louer 16 563 255 #d9f26b
Fabri·Ka /fabri/ Produits québécois 291 132 3 224 boutiques #c4532e
Auto·Ka /auto/ Véhicules d'occasion 17 997 138 #ff5a2a
Food·Ka /food/ Épicerie comparée 26 319 30 #1f7a4d
Total 400 498

Au 2026-08-28, la prod affiche 410 988 inscriptions (immo 59 255 · lou 15 987 · fabri 291 573 · auto ~17,8 k · food ~26,3 k) — le chiffre vit au rythme des synchronisations, GET /api/overview fait toujours foi.

Dimension Valeur
Index sémantique 362 508 vecteurs · text-embedding-3-small · 256 d float16 · ≈ 164 Mo npz
Bases SQLite ≈ 1,4 Go (5 bases + 2 bases quartier)
Moteur oraka/ 2 090 lignes Python (8 modules)
Backends embarqués 339 fichiers Python · 65 852 lignes (inchangés)
Frontends 5 SPA React 18 + Vite, rebuildées sous préfixe
Cadence d'ingestion immo 180 min · lou 60 · auto 120 · fabri/food 360
Latence recherche brute-force numpy sur npz en RAM, hot-reload sur mtime

# Visite guidée (captures du 2026-08-28)

Dix captures de la plateforme en production (docs/screenshots/).

# 1 — Accueil : une barre, cinq univers

Accueil Ora-Ka

La landing « famille Ka × verre » : logo Ora·Ka (badge lime incliné), compteur vivant (« Une barre. 410 988 inscriptions. Tout le Québec. »), barre de recherche unifiée au liseré conic-gradient aux 5 couleurs d'accent des univers, filtres de portée (Tout · Propriétés · Logements · Produits QC · Véhicules · Épicerie), suggestions de requêtes en langage naturel, ticker des compteurs par univers et pilule KA ID (SSO du Groupe KA) en haut à droite.

# 2 — /immo/ : Immo·Ka, les propriétés à vendre

Immo-Ka sous /immo/

L'app Immo·Ka reproduite à l'identique sous son préfixe : héro rouge « Toutes les propriétés à vendre, un seul endroit », ticker des agences (RE/MAX, DuProprio, Kijiji, Via Capitale…), compteurs live (59 255 propriétés · 26 agences · 2 847 villes · prix moyen), barre de filtres (ville, type, prix), vues Liste/Carte.

# 3 — /lou/ : Lou·Ka, les logements à louer

Lou-Ka sous /lou/

Lou·Ka au complet — recherche par ville/quartier, carte MapLibre (worker rebasé sous le préfixe), fiches enrichies (quartier, POI, géocodage) — servie par son backend d'origine, intact.

# 4 — /fabri/ : Fabri·Ka, les produits québécois

Fabri-Ka sous /fabri/

Le répertoire des produits d'ici : 291 573 produits, 1 263 boutiques, 17 régions, navigation par catégories (mode, bijoux, épicerie fine, café & thé, art & artisanat, maison & déco) et recherche plein texte.

# 5 — /auto/ : Auto·Ka, les véhicules d'occasion

Auto-Ka sous /auto/

L'agrégateur de véhicules d'occasion du Québec (concessionnaires et plateformes), avec ses filtres marque/modèle/année/prix et ses fiches détaillées d'origine.

# 6 — /food/ : Food·Ka, l'épicerie comparée

Food-Ka sous /food/

Le comparateur d'épicerie : produits des grandes bannières agrégés et comparés, catégories, rabais et historique — même app, même UX que le déploiement individuel.

# 7 — Conditions d'utilisation

Conditions d'utilisation

/conditions (oraka/legal.py) : nature du service (agrégation d'inscriptions publiques — chaque inscription demeure la propriété de sa source), exactitude des données, encadrement de la recherche sémantique (scores indicatifs, ni classement commercial ni recommandation), utilisation permise.

# 8 — Politique de confidentialité

Politique de confidentialité

/confidentialite : données traitées, compte KA ID, cookies de session, absence de revente de données — le pendant confidentialité du cadre légal commun au Groupe KA.

# 9 & 10 — Accueil mobile

Accueil mobile Accueil mobile (suite)

La landing en format téléphone : mêmes éléments (barre, filtres empilés, suggestions, ticker, footer des cinq univers), disposition en colonne, cibles tactiles conformes au socle mobile Ka (anti-zoom 16 px, safe-areas).

# La recherche hybride

Une seule barre (/api/search) interroge les cinq univers avec un score unifié par candidat — pas de cascade exact-puis-sémantique :

  • Candidats : correspondance exacte par jetons AND (LIKE/FTS5) ∪ top-k sémantique par univers.
  • Score : cosinus (lookup par uid, même pour les hits exacts) + 0,22·couverture-jetons + 0,10·phrase + 0,05·exact + affinité d'univers data-driven.
  • Coupure dynamique (best − 0,22) + garantie de diversité : chaque univers ≥ best − 0,30 est épinglé dans le top 12.
  • Affichage : pertinence relative en % (score/best), jauge, surlignage des jetons (préfixe en début de mot, stopwords exclus).

# Le pipeline, étape par étape

  1. Normalisation de la requête : minuscules, accents retirés, découpage en jetons ≥ 2 caractères, stopwords français exclus (_tokens dans oraka/search.py).
  2. Volet exact : chaque univers est interrogé dans sa propre base SQLite (ouverte en lecture seule mode=ro) — FTS5 quand la table virtuelle existe, sinon LIKE en AND sur tous les jetons ; chaque hit est mis en forme par un _shape_<univers> (titre, prix, image, ville, lien vers la fiche sous préfixe).
  3. Volet sémantique : la requête est embarquée via l'API OpenAI (text-embedding-3-small, 256 dimensions, vecteurs stockés en float16) puis comparée en brute-force numpy (produit scalaire sur matrices normalisées, entièrement en RAM) aux ~362 k vecteurs des index data/semantic/<univers>.npz ; seuil plancher de similarité 0,32 (_SEM_FLOOR), top-k par univers.
  4. Fusion : union des candidats exacts et sémantiques, score unifié (cosinus + bonus de couverture/phrase/exactitude + affinité d'univers), coupure dynamique et garantie de diversité inter-univers.
  5. Repli silencieux : si l'index npz ou la clé OpenAI manquent, le moteur sert le volet exact seul (semantic.available()), jamais d'erreur utilisateur.
« condo 2 chambres montréal » « fromage du québec »
Recherche condo Recherche fromage

L'index (oraka/semantic.py) vit dans data/semantic/<univers>.npz ; le serveur le recharge à chaud sur changement de mtime — un simple rsync des npz suffit, sans redémarrage. Rebuild incrémental : run.py index (~10 min, ~0,25 $ ; à relancer après les grosses syncs sinon l'index dérive).

# Les cinq univers

Chaque app est montée telle quelle sous son préfixe — mêmes fiches, mêmes cartes, mêmes filtres que l'app d'origine :

Immo·Ka /immo/ Lou·Ka /lou/
Fabri·Ka /fabri/ Auto·Ka /auto/
Food·Ka /food/ (splash au boot : voir docs/archive/splash.png)

Captures régénérables : node scripts/screenshots.mjs (playwright requis).

# Architecture

text
ora-ka/
  run.py               # serve [port] | sync [app ...] | watch | index
  oraka/
    web.py             # app maîtresse : landing + /api/overview + /healthz + 5 mounts
    landing.py         # landing « famille Ka × verre » (splash, ticker, cartes)
    search.py          # /api/search — score unifié exact ∪ sémantique
    semantic.py        # index npz par univers, hot-reload, OpenAI embeddings
    auth.py            # SSO « KA ID » via le hub groupe-ka.com
    hubfav.py          # favoris synchronisés au hub
    legal.py           # conditions + confidentialité
  apps/
    immo/  { immoka/,  data/{immoka.db, quartier.db, sources.json…}, frontend/ }
    lou/   { louka/,   data/{louka.db,  quartier.db, sources.json},  frontend/ }
    fabri/ { fabrika/, data/{fabrika.db, stores.json},               frontend/ }
    auto/  { autoka/,  data/{autoka.db, sources.json},               frontend/ }
    food/  { foodka/,  data/{foodka.db, sources.json},               frontend/ }
  data/semantic/       # <univers>.npz + .meta.json (non versionnés)

Chaque app conserve son arborescence d'origine (<package>/, data/, frontend/dist/ frères) — ses chemins internes calculés via __file__ restent valides, aucun code backend n'a été modifié. Les frontends React/Vite ont uniquement été rebuildés avec base: '/<préfixe>/' + basename react-router + API_BASE = import.meta.env.BASE_URL (+ cas spéciaux : worker MapLibre de lou, logos de food).

Le montage FastAPI (app.mount("/<alias>", <alias>_app)) confine le catch-all SPA de chaque app à son préfixe ; /immo sans barre oblique redirige en 308 vers /immo/, etc. Le routeur d'authentification KA ID est inclus avant les montages pour que /api/auth/* soit toujours servi par l'app racine. run.py watch lance 5 threads de synchronisation (un par univers, démarrages décalés de 5 s), chacun à la cadence native de son déploiement individuel — une app en échec ne tue jamais la boucle.

Design de la landing : hybride « famille Ka × verre » — papier grainé #f5f3ee, encre #141814, lime #d9f26b, JetBrains Mono uppercase, bordures encre + ombres décalées dures, badge « Ka » incliné −2°, ticker, splash Groupe KA (1,6 s, sessionStorage).

# API & routes

Routes servies par l'app maîtresse (oraka/web.py) :

Route Méthode Description
/ GET Landing : barre unifiée, filtres, ticker, splash
/api/search?q=&scope=&limit= GET Moteur hybride exact + sémantique ; scope optionnel (immo/lou/fabri/auto/food), limit ≤ 120
/api/overview GET Compteurs live des 5 univers + total (lecture ro des 5 bases, cache mémoire 5 min)
/api/favorites GET « Mon univers Ka » : favoris du membre connecté, lus au hub Groupe KA (401 sinon)
/api/auth/* — SSO KA ID (connexion via le hub groupe-ka.com)
/healthz GET Healthcheck : présence des 5 bases SQLite
/conditions · /confidentialite GET Pages légales (HTML servi par oraka/legal.py)
/immo /lou /fabri /auto /food GET Redirection 308 vers /<alias>/
/immo/ /lou/ /fabri/ /auto/ /food/ mount Les 5 apps complètes (leur API interne incluse, ex. /immo/api/...)

La compression gzip (GZipMiddleware, ≥ 1 Ko) s'applique à toute la plateforme, montages compris.

# Lancement

bash
python3 -m venv .venv && .venv/bin/pip install -r requirements.txt
# builds frontend (une fois) : cd apps/<x>/frontend && npm install && npm run build
.venv/bin/python run.py serve 8200   # plateforme complète
.venv/bin/python run.py sync         # ingestion des 5 apps (ou : sync immo lou …)
.venv/bin/python run.py watch        # boucle permanente, cadence native par app
.venv/bin/python run.py index        # (re)construit l'index sémantique

# Réindexation sémantique (run.py index)

oraka/semantic.py construit un corpus texte par univers (titre + ville + caractéristiques + description tronquée), l'envoie par lots de 800 à l'API OpenAI (text-embedding-3-small, dimensions=256, retries exponentiels), normalise les vecteurs et les écrit en float16 dans data/semantic/<univers>.npz accompagnés d'un <univers>.meta.json (uids, comptes, horodatage). Le rebuild est incrémental — seules les inscriptions nouvelles ou modifiées sont ré-embarquées. Le serveur détecte le nouveau mtime et recharge l'index à chaud, sans redémarrage.

.env (non versionné) : OPENAI_API_KEY (index sémantique), FIRECRAWL_API_KEY, SCRAPFLY_KEY, SCRAPFLY_API_KEY (ingestion seulement, jamais le serveur) ; KA_SSO_SECRET, KA_HUB_URL (SSO KA ID).

# Production (MacLustr — nœud M4M64a, port 8200)

bash
pm2 start .venv/bin/python --name ora-ka-web  --interpreter none --cwd ~/apps/ora-ka -- run.py serve 8200
pm2 start .venv/bin/python --name ora-ka-sync --interpreter none --cwd ~/apps/ora-ka -- run.py watch
pm2 start /opt/homebrew/bin/ngrok --name ora-ka-ngrok --interpreter none -- http --url=www.ora-ka.com 8200

Healthcheck : GET /healthz — vérifie la présence des 5 bases SQLite. Stats agrégées en direct : GET /api/overview.

  • Nœud : M4M64a (Mac Studio M4 Max, 16 cœurs, 64 Go) — répertoire ~/apps/ora-ka.
  • Processus PM2 : ora-ka-web (serveur :8200), ora-ka-sync (run.py watch, 5 threads), ora-ka-ngrok (tunnel www.ora-ka.com).
  • Domaine : https://www.ora-ka.com via ngrok (domaine réservé).

# Dépôt & développement (remote-first)

La source de vérité est le repo git sur le nœud M4M64a (~/apps/ora-ka), pas une copie laptop — comme pour toutes les apps du Groupe Ka. Remote origin = spbgit (git perso, bare repo ~/srv/git/ora-ka.git sur M3U96a, alias SSH gitsrv configuré sur les nœuds) :

bash
git remote add origin gitsrv:srv/git/ora-ka.git
git push -u origin main

Ne sont pas versionnés (voir .gitignore) : .env*, .venv/, node_modules/, les bases SQLite (apps/*/data/, data/), les index sémantiques *.npz et les caches. Les frontend/dist/ (≈ 3,7 Mo au total) sont versionnés : le repo se déploie sans étape npm.

# Écosystème Groupe KA

Ora-Ka est la porte d'entrée unifiée des cinq agrégateurs historiques — chacun reste aussi déployé individuellement :

Univers Site individuel Dans Ora-Ka
Immo·Ka www.immo-ka.com /immo/
Lou·Ka www.lou-ka.com /lou/
Fabri·Ka www.fabri-ka.com /fabri/
Auto·Ka www.auto-ka.com /auto/
Food·Ka www.food-ka.com /food/

Le Groupe KA compte aussi vrai-prix, trouve-ka, crea-ka, resto-ka, sorti-ka, job-ka, house-ka, rent-ka… — hub et SSO KA ID : https://www.groupe-ka.com.

# Contact


© 2026 Groupe KA — Ora-Ka agrège des données publiques ; chaque inscription reste la propriété de sa source.