Files
OmniRoute/docs/i18n/fr/docs/frameworks/MEMORY.md
Diego Rodrigues de Sa e Souza 8feea123bb feat(docs): mirror every docs/ page in all 65 locales (#14106)
* feat(docs): mirror every docs/ page in all 65 locales

Extends the documentation mirrors from the 22-page core set (#13940) to
every Markdown page under docs/: 152 sources x 65 locales = 9,880 mirrors
(6,208 new), language bars rewritten for the full locale list, state
adopted so the blocking drift gate now covers all 152 pages.

run-translation.mjs: an oversized block made only of table rows or list
items (PROVIDER_REFERENCE.md 244-row table, FREE_TIERS.md 71-item list) is
cut at item boundaries and rejoined without a blank line — the single
16-40 KB request outlived the backend socket for verbose scripts. 48
older mirrors whose tables had lost rows were retranslated with --force.

* docs(i18n): refresh mirrors for the sources the base changed since the branch cut

Section-level retranslation of the 29 docs (and README.md) whose source
or mirrors moved on release/v3.8.51 during the run, then state adoption;
the drift gate is green again on the merged tree.
2026-09-18 13:16:46 -03:00

76 KiB
Raw Blame History

Memory System (Français)

🌐 Languages: 🇺🇸 English · 🇪🇹 am · 🇸🇦 ar · 🇦🇿 az · 🇧🇬 bg · 🇧🇩 bn · 🇨🇿 cs · 🇩🇰 da · 🇩🇪 de · 🇬🇷 el · 🇪🇸 es · 🇪🇪 et · 🇮🇷 fa · 🇫🇮 fi · 🇮🇪 ga · 🇮🇳 gu · 🇳🇬 ha · 🇮🇱 he · 🇮🇳 hi · 🇭🇷 hr · 🇭🇺 hu · 🇦🇲 hy · 🇮🇩 id · 🇳🇬 ig · 🇮🇹 it · 🇯🇵 ja · 🇬🇪 ka · 🇰🇭 km · 🇮🇳 kn · 🇰🇷 ko · 🇱🇹 lt · 🇱🇻 lv · 🇮🇳 ml · 🇮🇳 mr · 🇲🇾 ms · 🇲🇹 mt · 🇲🇲 my · 🇳🇵 ne · 🇳🇱 nl · 🇳🇴 no · 🇮🇳 or · 🇮🇳 pa · 🇵🇭 phi · 🇵🇱 pl · 🇵🇹 pt · 🇧🇷 pt-BR · 🇷🇴 ro · 🇷🇺 ru · 🇱🇰 si · 🇸🇰 sk · 🇸🇮 sl · 🇷🇸 sr · 🇸🇪 sv · 🇰🇪 sw · 🇮🇳 ta · 🇮🇳 te · 🇹🇭 th · 🇹🇷 tr · 🇺🇦 uk-UA · 🇵🇰 ur · 🇺🇿 uz · 🇻🇳 vi · 🇳🇬 yo · 🇨🇳 zh-CN · 🇹🇼 zh-TW


Source de vérité : src/lib/memory/ et src/app/api/memory/ Dernière mise à jour : 2026-06-28 — v3.8.40 (désactivée par défaut + rattrapage de la quantification int8)

OmniRoute fournit une mémoire conversationnelle persistante indexée par clé API (et éventuellement par identifiant de session). Les souvenirs sont extraits automatiquement des réponses du LLM au moyen dune correspondance légère par expressions régulières, puis réinjectés dans les requêtes suivantes sous la forme dun message système initial (ou du premier message utilisateur pour les fournisseurs qui refusent le rôle système).

La mémoire est DÉSACTIVÉE par défaut (v3.8.30+). DEFAULT_MEMORY_SETTINGS.enabled vaut désormais false (src/lib/memory/settings.ts). Lactivation de la mémoire injecte jusquà maxTokens (~2k) de contexte récupéré dans chaque requête de chat, ce qui est facturé — un coût inattendu pour les nouvelles installations et pour les clients qui gèrent leur propre contexte. Activez-la explicitement dans Paramètres → Mémoire (longlet MemorySkillsTab affiche un avertissement relatif au coût en jetons lorsque la mémoire est activée). Un client peut exclure une requête spécifique à laide de len-tête de requête x-omniroute-no-memory (true/1/yes) — consultez le tableau des en-têtes de requête dans API_REFERENCE.md. Une requête sans mémoire définit memoryOwnerId = null, ce qui désactive à la fois linjection de mémoire et de compétences pour cette requête (open-sse/handlers/chatCore/headers.ts::isNoMemoryRequested).

La mémoire est limitée à chaque clé API, et non à chaque utilisateur — toutes les requêtes authentifiées avec la même clé API partagent le même pool de mémoire, avec une limitation supplémentaire facultative par sessionId.

Architecture

Client → /v1/chat/completions (apiKeyInfo résolu en amont)
  → handleChatCore() [open-sse/handlers/chatCore.ts]
    → resolveMemoryOwnerId(apiKeyInfo)        # extrait lidentifiant
    → getMemorySettings()                     # paramètres mis en cache
    → shouldInjectMemory(body, {enabled})     # contrôle
    → retrieveMemories(apiKeyId, config)      # SQL + FTS5 + vecteur facultatif
    → injectMemory(body, memories, provider)  # message système ou utilisateur
  → appel au fournisseur en amont
  → à la réponse : extractFacts(text, apiKeyId, sessionId)  # non bloquant
    → setImmediate → createMemory(fact) pour chaque correspondance
                   → embed(content) + upsertVector(id, vec)

Les points dappel dinjection et dextraction sont configurés dans open-sse/handlers/chatCore.ts (recherchez retrieveMemories, injectMemory et extractFacts).

Architecture du moteur (résolution à 3 niveaux)

Le moteur de mémoire détermine le chemin de récupération au moment de lexécution en fonction de linfrastructure disponible et des paramètres. Il existe trois niveaux, appliqués par ordre de priorité :

  ┌─────────────────────────────────────────────────────────────┐
  │  NIVEAU 0 — Mot-clé (FTS5)                                  │
  │  Disponibilité déterminée par une vérification : FTS5 lorsque│
  │  la version de SQLite le prend en charge (better-sqlite3 /   │
  │  node:sqlite / bun:sqlite) ; indisponible avec les versions  │
  │  sans FTS5 (p. ex. sql.js/WASM — "no such module: fts5").    │
  │  Utilisé lorsque strategy = "exact" ou comme solution de     │
  │  repli ; létat keyword du moteur reflète la vérification.   │
  └──────────────────────────────────┬──────────────────────────┘
                                     │ strategy = semantic|hybrid ?
                                     ▼
  ┌─────────────────────────────────────────────────────────────┐
  │  NIVEAU 1 — Vecteur intégré (sqlite-vec)                     │
  │  sqlite-vec v0.1.9 chargé via db.loadExtension().            │
  │  Recherche KNN par force brute sur des vecteurs Float32.     │
  │  Actif lorsque :                                             │
  │   • le chargement de sqlite-vec par loadExtension réussit    │
  │   • une source dembeddings est disponible (remote | static |│
  │     transformers) et peut produire un Float32Array           │
  │   • la table vec_memories existe (créée au premier ready())  │
  └──────────────────────────────────┬──────────────────────────┘
                                     │ qdrant.enabled ?
                                     ▼
  ┌─────────────────────────────────────────────────────────────┐
  │  NIVEAU 2 — Qdrant (base vectorielle externe facultative)    │
  │  Lorsquil est activé, remplace sqlite-vec pour les stratégies│
  │  semantic/hybrid. Nécessite une instance Qdrant en cours     │
  │  dexécution ainsi quun hôte et un port configurés.         │
  └─────────────────────────────────────────────────────────────┘

La dégradation est automatique et transparente :

  • Si le chargement de sqlite-vec échoue, le niveau 1 est indisponible → repli sur le niveau 0.
  • Si la source dembeddings renvoie une erreur, le niveau 1 se replie sur le niveau 0.
  • Si Qdrant est défaillant, le niveau 2 se replie sur le niveau 1 (ou sur le niveau 0 si le niveau 1 est également indisponible).

Sources d'embeddings

La couche d'embedding (src/lib/memory/embedding/) détermine la source à utiliser en fonction de MemorySettingsExtended.embeddingSource :

Source Description Clé requise Démarrage à froid
remote Utilise l'API d'embedding d'un fournisseur configuré (OpenAI, Cohere, etc.) Oui Aucun
static Embedding local par table de correspondance via potion-base-8M (WordPiece + agrégation moyenne) Non ~200ms
transformers Inférence ONNX locale via @huggingface/transformers v4, all-MiniLM-L6-v2 Non ~3s + ~400MB RAM
auto Résolution à l'exécution : distant (si une clé existe) → statique → transformers → null Variable Variable

Ordre de résolution pour auto :

  1. Trouver le premier fournisseur dans listEmbeddingProviders() avec hasKey === trueremote.
  2. Si settings.staticEnabled === truestatic.
  3. Si settings.transformersEnabled === truetransformers.
  4. Sinon → null (repli sur la recherche par mots-clés FTS5).

Le cache d'embeddings (src/lib/memory/embedding/cache.ts) utilise une table LRU en mémoire indexée par ${source}:${model}:${dim}:${sha256(text)}, limitée à MEMORY_EMBEDDING_CACHE_MAX entrées (1000 par défaut), avec une durée de vie de MEMORY_EMBEDDING_CACHE_TTL_MS (5 min par défaut). Il est partagé entre tous les appelants pendant le cycle de vie du processus.

RRF hybride (k=60)

Lorsque strategy = "hybrid" et que le magasin vectoriel est disponible, la récupération utilise la fusion réciproque des rangs (Reciprocal Rank Fusion) pour combiner les résultats FTS5 et vectoriels :

RRF(d) = Σ  1 / (k + rank_i(d))      où k = 60 (configurable via MEMORY_RRF_K)
          i

Concrètement :

  1. Exécuter la recherche FTS5 → liste classée R_fts (positions 1..N).
  2. Exécuter la recherche vectorielle KNN → liste classée R_vec (positions 1..M).
  3. Pour chaque memoryId unique :
    rrf_score = 1/(60 + fts_rank) + 1/(60 + vec_rank) (0 s'il est absent de la liste).
  4. Trier par rrf_score dans l'ordre décroissant, puis appliquer le parcours selon le budget de jetons.

RRF est reconnu comme efficace sans nécessiter de normalisation des scores entre des systèmes de récupération hétérogènes. La valeur par défaut k=60 provient de l'article original de Cormack et al. et fonctionne bien pour les petits corpus (<10k souvenirs).

Remplissage rétroactif (différé + réindexation)

Lorsque le modèle d'embedding change (détecté via embedding_signature), le magasin vectoriel est reconstruit et tous les souvenirs existants sont marqués avec needs_reindex = 1 dans la table memories.

Remplissage rétroactif différé : lors de la récupération suivante, tout souvenir dépourvu d'une entrée vectorielle est converti en embedding et inséré dans vec_memories avant l'exécution de la recherche. Cela amortit le coût du remplissage rétroactif sur les requêtes réelles sans bloquer le démarrage.

Réindexation explicite : l'onglet Moteur dans /dashboard/memory fournit un bouton « Réindexer maintenant » qui appelle POST /api/memory/reindex. Le gestionnaire appelle runReindexBatch() depuis src/lib/memory/reindex.ts, qui traite jusqu'à limit entrées en attente par requête. La progression peut être interrogée via GET /api/memory/engine-status (vectorStore.needsReindex).

La table memory_vec_meta (migration 083_memory_vec.sql) stocke :

  • active_dim — dimension vectorielle actuelle (null = pas encore étalonnée).
  • embedding_signature${source}:${model}:${dim} utilisée pour détecter les changements.
  • last_reset_at — horodatage de la dernière réinitialisation complète.
  • vec_loaded — indicateur 0/1 précisant si sqlite-vec a été chargé avec succès.

Extension des paramètres

Neuf champs dembedding et de stockage vectoriel sont disponibles dans MemorySettingsExtended, dans src/shared/schemas/memory.ts, et sont persistés via src/lib/db/settings.ts :

Champ Type Valeur par défaut Description
embeddingSource "remote" | "static" | "transformers" | "auto" "auto" Source dembedding à utiliser
embeddingProviderModel string | null null Fournisseur/modèle au format provider/model
customBaseUrl string | null null URL de base dun endpoint compatible OpenAI dédié à Memory
customModelId string | null null ID du modèle envoyé à lendpoint personnalisé
transformersEnabled boolean false Activation explicite de Transformers.js (MiniLM, ~400 Mo)
staticEnabled boolean false Activation explicite du modèle local statique potion-base-8M
rerankEnabled boolean false Active létape de reclassement (ajoute 200 à 500 ms/requête)
rerankProviderModel string | null null Fournisseur/modèle de reclassement au format provider/model
vectorStore "sqlite-vec" | "qdrant" | "auto" "auto" Backend vectoriel à utiliser

Ces paramètres sont exposés via GET /PUT /api/settings/memory (schéma MemorySettingsExtendedSchema).

Pour la source remote, Memory accepte également les paramètres facultatifs customBaseUrl et customModelId. Ensemble, ils permettent de sélectionner un endpoint /embeddings compatible OpenAI et un modèle sans modifier le registre global des embeddings. Lendpoint est normalisé avant utilisation et vérifié par la politique relative aux URL sortantes du fournisseur : HTTP(S) est obligatoire, les identifiants intégrés et les chaînes de requête sont rejetés, et les adresses de métadonnées cloud restent bloquées. Les valeurs vides conservent le fournisseur sélectionné dans le registre. Les erreurs renvoyées au tableau de bord sont assainies et les identifiants de lendpoint ne sont jamais journalisés.

TODO (D20) : La portée global (partage des mémoires entre toutes les clés API) nest pas implémentée dans cette version. Elle nécessite des modifications du schéma et un chemin de récupération global. À suivre séparément.

Couches de stockage

Principale : SQLite (table memories)

Créée par la migration 015_create_memories.sql :

Colonne Type Remarques
id TEXT PRIMARY KEY UUID généré via crypto.randomUUID()
api_key_id TEXT NOT NULL Clé API propriétaire
session_id TEXT Portée facultative par conversation
type TEXT NOT NULL Une valeur parmi factual, episodic, procedural, semantic
key TEXT Clé dupsert stable, p. ex. preference:i_prefer_python
content TEXT NOT NULL Texte factuel proprement dit
metadata TEXT Blob JSON (catégorie, extractedAt, source, ...)
created_at / updated_at TEXT Chaînes ISO 8601
expires_at TEXT Expiration facultative ; NULL signifie permanent
memory_id INTEGER UNIQUE Ajouté par 023_fix_memory_fts_uuid.sql pour relier les UUID aux rowid FTS5

Index : api_key_id, session_id, type, expires_at, ainsi que lindex unique memory_id.

Sémantique de lupsert : createMemory() recherche une ligne existante ayant les mêmes (api_key_id, key) et la met à jour sur place lorsquelle est trouvée (en fusionnant metadata via une décomposition superficielle). Cela empêche la table de croître sans limite lors de déclarations de préférence répétées.

Recherche en texte intégral (table virtuelle memory_fts)

022_add_memory_fts5.sql crée une table virtuelle FTS5 sur content et key. 023_fix_memory_fts_uuid.sql corrige un bug rencontré en conditions réelles, dans lequel la clé primaire UUID ne pouvait pas être jointe au rowid entier de FTS5 — la migration ajoute la colonne memory_id, recrée la table FTS et configure des déclencheurs (memory_fts_ai, memory_fts_ad, memory_fts_au) qui maintiennent la synchronisation de FTS lors des opérations INSERT, DELETE et UPDATE.

Utilisée par retrieval.ts pour les stratégies semantic et hybrid (voir ci-dessous). Le code de récupération effectue une vérification avec hasTable("memory_fts") et revient à lordre chronologique si la table FTS est absente ou si la requête FTS lève une exception.

Facultatif : Qdrant (magasin vectoriel de niveau 2)

src/lib/memory/qdrant.ts implémente une intégration facultative de Qdrant comme magasin vectoriel de niveau 2. La récupération nest acheminée vers Qdrant que lorsque le sélecteur de moteur memoryVectorStore === "qdrant" — la valeur par défaut "auto" (ainsi que "sqlite-vec") ne sélectionne jamais Qdrant. Le bouton de longlet Engine définit simultanément à la fois qdrantEnabled et memoryVectorStore : son activation fait de Qdrant le magasin principal, tandis que sa désactivation rétablit "auto" (#5597 — avant ce correctif, lactivation était sans effet, car aucun mécanisme ne définissait le sélecteur de moteur). Si Qdrant est inaccessible ou ne renvoie aucun résultat, la récupération revient à sqlite-vec → FTS5.

  • upsertSemanticMemoryPoint() — incorpore key + content à laide du modèle dembedding configuré, vérifie que la collection existe (crée des vecteurs utilisant la distance cosinus lors de la première utilisation), puis insère ou met à jour un point avec la charge utile {memoryId, apiKeyId, sessionId, key, content, metadata, createdAtUnix, expiresAtUnix}.
  • searchSemanticMemory(query, topK, scope) — incorpore la requête, recherche dans la collection avec un filtre sur kind = "omniroute_memory" et, facultativement, sur apiKeyId / sessionId. Limite topK à [1, 20].
  • deleteSemanticMemoryPoint(id) — supprime un seul point. Appelée par deleteMemory() après la suppression de la ligne SQLite (D15).
  • cleanupSemanticMemoryPoints({retentionDays}) — supprime en masse les points dont expiresAtUnix est passé ou dont createdAtUnix est antérieur au seuil de conservation. Effectue dabord un décompte afin que le tableau de bord puisse afficher les nombres réels.
  • checkQdrantHealth() — sonde détat GET /readyz avec mesure de la latence.

Linterface des paramètres expose la configuration de Qdrant, la vérification de létat, le test de recherche sémantique et le nettoyage dans longlet Moteur de /dashboard/memory. Les routes correspondantes sous src/app/api/settings/qdrant/ sont toutes connectées depuis la v3.8.6 :

Route Méthode Description
/api/settings/qdrant GET / PUT Lire / mettre à jour les paramètres Qdrant
/api/settings/qdrant/health GET Sonde de disponibilité + latence
/api/settings/qdrant/search POST Test de recherche sémantique
/api/settings/qdrant/cleanup POST Supprimer les points expirés / anciens
/api/settings/qdrant/embedding-models GET Répertorier les modèles dembedding disponibles

Remarques sur le comportement (à quoi sattendre) :

  • Sélection du moteur — lactivation de Qdrant dans longlet Moteur en fait le stockage principal (définit memoryVectorStore="qdrant"); sa désactivation rétablit "auto" (#5597).
  • Aucun remplissage rétroactif — seules les mémoires créées/mises à jour après lactivation de Qdrant y sont écrites (double écriture sans attente de résultat). Les mémoires SQLite préexistantes ne sont pas migrées ; « Réindexer maintenant » reconstruit uniquement lindex sqlite-vec, pas celui de Qdrant.
  • La dimension vectorielle est détectée automatiquement à partir de lembedding réel lors de la première utilisation — aucun champ de dimension nest à renseigner. Le changement de modèle dembedding après la création dune collection nest pas géré automatiquement : la collection existante reste inchangée, les écritures/recherches dont les dimensions ne correspondent pas échouent et basculent sur sqlite-vec. Recréez la collection (avec un nouveau nom, ou supprimez-la dans Qdrant) pour changer de modèle dembedding.
  • Métrique de distance — toujours Cosinus (codée en dur lors de la création de la collection ; non configurable).
  • Authentification — clé API uniquement (envoyée dans len-tête api-key ; facultative pour un Docker local sans authentification). JWT/RBAC ne sont pas utilisés.
  • Champs de configuration — linterface expose host, port, collection, embeddingModel, apiKey. vectorSize / hnswEfConstruct sont disponibles uniquement via lenvironnement/la BDD et vectorSize nest pas utilisé pour la création de la collection (la dimension provient de lembedding).

Quantification vectorielle (int8 — facultative, pour les deux backends)

Les deux backends vectoriels prennent en charge une quantification int8 facultative afin de réduire lempreinte mémoire des vecteurs stockés (environ 4 fois plus petite quavec Float32), au prix dune légère baisse du rappel. Elle est désactivée par défaut sur les deux backends — les vecteurs conservent leur précision maximale, sauf activation explicite.

Backend Paramètre Type Valeur par défaut Emplacement de lecture
Qdrant qdrantQuantization (clé BDD) "none" | "int8" | "binary" "none" src/lib/memory/qdrant.ts::normalizeQdrantConfig()
sqlite-vec MEMORY_VEC_QUANTIZATION (env) "none" | "int8" "none" src/lib/memory/vectorStore.ts::requestedVecQuantization()
  • Qdrant est configuré pour chaque instance au moyen de la clé de paramètre qdrantQuantization (exposée sous la forme du champ quantization sur PUT /api/settings/qdrant). Avec "int8", buildQuantizationConfig() demande une quantification scalaire (always_ram, quantile 0.99) et les recherches activent rescore: true afin que les vecteurs en pleine précision affinent lensemble des candidats int8.
  • La quantification de sqlite-vec est disponible uniquement via lenvironnement (ce nest pas un paramètre de BDD) : définissez MEMORY_VEC_QUANTIZATION=int8 pour stocker les vecteurs locaux dans une colonne int8[dim] via vec_quantize_int8(?, 'unit'). Le mode choisi est intégré à embedding_signature (un suffixe :int8), de sorte quun changement de mode déclenche une réindexation complète de la table vec_memories — le même mécanisme de remplissage rétroactif différé que celui utilisé lors dun changement de modèle dembedding.

Types de mémoire

MemoryType (src/lib/memory/types.ts) :

Type Utilisation
factual Préférences, faits stables sur lutilisateur, habitudes comportementales
episodic Décisions liées à un moment précis (« Jai choisi Postgres »)
procedural Mémoire des workflows et procédures (réservée ; aucun extracteur automatique actuellement)
semantic Réservée aux entrées du magasin vectoriel

La stratégie de récupération de MemoryConfig est exact, semantic ou hybrid, et sa portée est session, apiKey ou global. La portée par défaut renvoyée par getMemorySettings() est apiKey.

Extraction des faits (extraction.ts)

Lextraction repose sur des expressions régulières, et non sur un LLM — elle sexécute dans le processus avec setImmediate() afin de ne jamais bloquer le flux de réponse :

  • Motifs de préférenceMemoryType.FACTUAL (par ex. I prefer …, I really like …, my favorite is …, I hate …)
  • Motifs de décisionMemoryType.EPISODIC (par ex. I'll use …, I chose …, I went with …, I'm going to adopt …)
  • Motifs comportementauxMemoryType.FACTUAL (par ex. I usually …, I always …, I tend to …)

Chaque correspondance est nettoyée (trim, réduction des espaces, limitation à 500 caractères), dédupliquée au sein du lot au moyen dun factKey(category, content) stable, puis stockée via createMemory() avec les métadonnées {category, extractedAt, source: "llm_response"}. Le texte dentrée est limité à 64 Kio (MAX_EXTRACTION_TEXT_LENGTH) — lorsquil est plus long, la fin du texte est utilisée afin que le contenu le plus récent de lassistant soit toujours pris en compte.

extractFactsFromText(text) est exportée pour les tests et renvoie les faits structurés sans les stocker.

Récupération (retrieval.ts)

retrieveMemories(apiKeyId, config) est le point dentrée principal. Cette fonction :

  1. Normalise et valide la configuration au moyen de MemoryConfigSchema.
  2. Renvoie immédiatement [] lorsque enabled vaut false ou que maxTokens <= 0.
  3. Limite maxTokens à lintervalle [1, 8000].
  4. Détecte si la table moderne memories existe (par opposition à lancienne table memory) afin que les bases de données plus anciennes continuent de fonctionner.
  5. Construit la requête de base avec une condition dexpiration (expires_at IS NULL OR datetime(expires_at) > datetime('now')), une portée de session facultative et une date limite facultative fondée sur retentionDays.
  6. Adapte le traitement selon la stratégie :
    • exact (par défaut) : ordre chronologique ORDER BY created_at DESC LIMIT 100.
    • semantic : si config.query est défini et que memory_fts existe, effectue une jointure avec memory_fts MATCH ? et trie selon le classement FTS ; revient à lordre chronologique lorsque FTS ne renvoie aucune ligne.
    • hybrid : union des résultats FTS (pertinence supérieure) et de lensemble chronologique, dédupliquée par identifiant.
  7. Calcule un score de pertinence par mots-clés (getRelevanceScore) sur content, key et le JSON metadata lorsquune requête est fournie. Les lignes dont le score est nul sont filtrées.
  8. Trie par score décroissant, puis par createdAt décroissant.
  9. Parcourt la liste classée et accepte les entrées tant que le cumul de estimateTokens(content) (≈ length / 4) reste inférieur au budget. Renvoie toujours au moins une entrée lorsquune correspondance existe.

estimateTokens est exportée et utilisée par la récupération, la synthèse et loutil MCP omniroute_memory_search.

Injection (injection.ts)

injectMemory(request, memories, provider) :

  1. Regroupe tous les contenus de mémoire dans une seule chaîne Memory context: ….
  2. Sélectionne une stratégie selon le nom du fournisseur :
    • Message système (par défaut pour OpenAI, Anthropic, Gemini, …) — ajoute un {role: "system", content: memoryText} avant tous les messages système existants afin que les invites système de lutilisateur restent prioritaires.
    • Message utilisateur (solution de repli) — pour les fournisseurs figurant dans PROVIDERS_WITHOUT_SYSTEM_MESSAGE : o1, o1-mini, o1-preview, glm, glmt, glm-cn, zai, qianfan. Ceux-ci rejettent le rôle système et renverraient autrement une erreur 400 (cf. problème #1701 pour GLM/Zhipu).
  3. Journalise le nombre, la stratégie et le modèle sous memory.injection.injected.

providerSupportsSystemMessage(provider) est exportée pour les appelants qui doivent prendre leurs propres décisions de routage. Les fournisseurs inconnus utilisent true par défaut (rôle système autorisé) par mesure de sécurité.

Paramètres (settings.ts)

La configuration de la mémoire est stockée dans la table des paramètres de la base de données, et non dans des variables denvironnement. getMemorySettings() lit les données depuis getSettings() et met le résultat en cache dans le processus ; invalidateMemorySettingsCache() est appelée par la route PUT des paramètres après les écritures.

Champs historiques (toutes les versions)

Clé de la base de données Type Valeur par défaut Contrôle de linterface utilisateur
memoryEnabled booléen false (désactivé par défaut depuis v3.8.30) Activation/désactivation de la mémoire
memoryMaxTokens entier 2000 (plage 016000) Budget de jetons pour linjection
memoryRetentionDays entier 30 (plage 1365) Fenêtre de conservation
memoryStrategy énum. "hybrid" (parmi recent, semantic, hybrid) Stratégie de récupération
skillsEnabled booléen false Active linjection de compétences par clé (voir SKILLS.md)

Remarque : la stratégie "recent" de linterface utilisateur correspond à la stratégie de récupération interne "exact" via toMemoryRetrievalConfig() (ordre chronologique).

Nouveaux champs (v3.8.6, plan 21 D9)

Voir également la section « Extension des paramètres » ci-dessus pour la description des champs.

Clé de la base de données Champ de lAPI Valeur par défaut
memoryEmbeddingSource embeddingSource "auto"
memoryEmbeddingModel embeddingProviderModel null
memoryTransformersEnabled transformersEnabled false
memoryStaticEnabled staticEnabled false
memoryRerankEnabled rerankEnabled false
memoryRerankModel rerankProviderModel null
memoryVectorStore vectorStore "auto"

Les clés de la base de données liées à Qdrant (qdrantEnabled, qdrantHost, qdrantPort, qdrantApiKey, qdrantCollection avec "omniroute_memory" par défaut, qdrantEmbeddingModel avec "openai/text-embedding-3-small" par défaut) sont lues par normalizeQdrantConfig() dans qdrant.ts.

Variables denvironnement (v3.8.6)

Six variables denvironnement facultatives ajustent le comportement dexécution du moteur (documentées dans .env.example) :

Variable Valeur par défaut Description
MEMORY_EMBEDDING_CACHE_TTL_MS 300000 Durée de vie du cache des plongements (5 min)
MEMORY_EMBEDDING_CACHE_MAX 1000 Nombre maximal dentrées dans le cache LRU des plongements
MEMORY_TRANSFORMERS_MODEL Xenova/all-MiniLM-L6-v2 Dépôt HF du modèle Transformers.js
MEMORY_STATIC_MODEL minishlab/potion-base-8M Dépôt HF du modèle statique potion
MEMORY_STATIC_CACHE_DIR <DATA_DIR>/embeddings Emplacement de stockage des modèles téléchargés
MEMORY_VEC_TOP_K 20 Valeur top-K par défaut pour la recherche vectorielle
MEMORY_RRF_K 60 Constante k de RRF pour la recherche hybride
MEMORY_VEC_QUANTIZATION none Définissez cette valeur sur int8 pour stocker les vecteurs sqlite-vec locaux sous forme quantifiée (environ 4× plus petits ; option facultative). Tout changement de mode impose une réindexation.

Résumé (summarization.ts)

summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000) compacte le contenu ancien lorsque le nombre total courant de jetons dans les mémoires d'une clé dépasse le budget. La fonction parcourt les lignes par ordre décroissant de created_at, conserve celles qui tiennent dans le budget et, pour les autres, remplace content directement par les trois premières phrases du contenu d'origine. tokensSaved correspond à la différence de estimateTokens entre l'ancien et le nouveau contenu.

Cette routine est disponible, mais n'est pas appelée automatiquement dans le pipeline de discussion actuel — appelez-la depuis une tâche cron, une action d'administration ou une intégration à MemoryConfig.autoSummarize si vous avez besoin d'une compaction continue. La perte de données est irréversible : le texte d'origine est écrasé.

API REST

Tous les points de terminaison nécessitent l'authentification de gestion (requireManagementAuth).

Points de terminaison principaux de la mémoire (existants + mis à jour)

Méthode Chemin Description
GET /api/memory Liste paginée avec les filtres suivants : apiKeyId, type, sessionId, q, limit, page, offset. La réponse inclut stats.total, stats.tokensUsed, stats.hitRate, cacheStats
POST /api/memory Crée une entrée (validée par Zod : content, key, ainsi que les champs facultatifs type, sessionId, apiKeyId, metadata, expiresAt). Appelle createMemory(), qui effectue un upsert sur (apiKeyId, key)
GET /api/memory/[id] Récupère une entrée unique par UUID
PUT /api/memory/[id] Met à jour les champs de l'entrée (type, key, content, metadata). Corps : MemoryUpdatePutSchema. Synchronise également le vecteur si la source d'embedding est disponible.
DELETE /api/memory/[id] Supprime une entrée ; la supprime également de vec_memories (D15) et de Qdrant dans la mesure du possible. Renvoie 404 si elle est introuvable.
GET /api/memory/health Exécute verifyExtractionPipeline("health-check") — cycle complet création→liste→suppression. Renvoie {working, latencyMs, error?}

Nouveaux points de terminaison du moteur de mémoire (plan 21)

Méthode Chemin Description
POST /api/memory/retrieve-preview Simulation de retrieveMemories — renvoie des résultats classés avec leur score, leur niveau et leur nombre de jetons. Corps : RetrievePreviewSchema. N'injecte PAS de mémoires et ne les modifie pas.
GET /api/memory/embedding-providers Répertorie les fournisseurs avec leurs modèles d'embedding et indique ceux pour lesquels une clé API est configurée.
GET /api/memory/engine-status Renvoie l'état complet du moteur : niveau des mots-clés, résolution des embeddings, statistiques du magasin vectoriel, état de Qdrant et configuration du reclassement. Structure : MemoryEngineStatusSchema.
POST /api/memory/summarize Déclenche manuellement la compaction de la mémoire. Corps : MemorySummarizeSchema (olderThanDays, apiKeyId?, dryRun). Renvoie {candidates, tokensSaved}.
POST /api/memory/reindex Déclenche la réindexation vectorielle des mémoires ayant needs_reindex=1. Corps : MemoryReindexSchema (force). Renvoie {started, pending}.

Points de terminaison des paramètres

Méthode Chemin Description
GET /api/settings/memory Paramètres MemorySettingsExtended normalisés actuels (7 nouveaux champs + champs hérités)
PUT /api/settings/memory Met à jour n'importe quel champ de MemorySettingsExtendedSchema (12 champs au total)
GET /api/settings/qdrant Paramètres Qdrant actuels (QdrantSettingsSchema)
PUT /api/settings/qdrant Met à jour les paramètres Qdrant. Corps : QdrantSettingsUpdateSchema. apiKey = une chaîne vide supprime la clé.
GET /api/settings/qdrant/health Sonde de disponibilité de l'instance Qdrant configurée. Renvoie QdrantHealthResultSchema.
POST /api/settings/qdrant/search Test de recherche sémantique dans Qdrant. Corps : QdrantSearchSchema (query, topK).
POST /api/settings/qdrant/cleanup Supprime de Qdrant les points correspondant aux mémoires expirées / anciennes.
GET /api/settings/qdrant/embedding-models Répertorie les modèles d'embedding disponibles pour Qdrant.

La requête de liste /api/memory prend en charge soit une pagination basée sur page (parsePaginationParams), soit une valeur offset brute — lorsque offset est présent, il est prioritaire et une valeur page dérivée est calculée pour la structure de la réponse.

Outils MCP (open-sse/mcp-server/tools/memoryTools.ts)

Lorsque le serveur MCP est activé, trois outils de mémoire sont enregistrés :

  • omniroute_memory_search{apiKeyId, query?, type?, maxTokens?, limit?} → encapsule retrieveMemories(). Depuis la v3.8.6 (D16), la strategy est lue depuis getMemorySettings() au lieu dêtre codée en dur sur "exact". Si query est fournie et que strategy vaut semantic ou hybrid, le magasin vectoriel est utilisé lorsquil est disponible.
  • omniroute_memory_add{apiKeyId, sessionId?, type, key, content, metadata?} → encapsule createMemory(). Accepte uniquement les 4 types canoniques : factual, episodic, procedural, semantic (D17).
  • omniroute_memory_clear{apiKeyId, type?, olderThan?} → répertorie les entrées correspondantes, les filtre éventuellement selon un horodatage de création antérieur, puis supprime chacune via deleteMemory() (qui supprime également les vecteurs de sqlite-vec + Qdrant).

Consultez MCP-SERVER.md pour plus de détails sur le transport et la portée.

Tableau de bord (Studio de mémoire)

src/app/(dashboard)/dashboard/memory/page.tsx est désormais un Studio à 3 onglets :

Onglet : Mémoires

  • Carte conceptuelle (explication réductible « Fonctionnement »).
  • Liste en temps réel, recherche et pagination (temporisation de 300 ms).
  • Filtre par type (factual / episodic / procedural / semantic / tous).
  • Fenêtre modale dajout de mémoire (clé, contenu, type).
  • Modification en ligne (bouton crayon → PUT /api/memory/[id]).
  • Suppression par ligne (avec boîte de dialogue de confirmation).
  • Export JSON de la page actuelle ; import JSON via un sélecteur de fichiers.
  • Cartes de statistiques : totalEntries, tokensUsed, hitRate.
  • Bouton « Compacter les anciennes » → POST /api/memory/summarize (une simulation affiche dabord le nombre de candidats, puis demande confirmation).
  • Un indicateur détat vert/rouge piloté par GET /api/memory/health.

Onglet : Bac à sable

  • Champ de requête + sélecteur de stratégie (Exacte / Sémantique / Hybride) + budget de jetons.
  • « Simuler » → POST /api/memory/retrieve-preview — affiche les résultats classés avec score, tier, tokens, vecScore, ftsScore.
  • Panneau de résolution indiquant la source dintégration / le magasin vectoriel utilisé et si un mécanisme de repli a été déclenché.

Onglet : Moteur

  • Panneau détat du moteur (badge FTS5 par mots-clés, badge dintégration, badge du magasin vectoriel, badge détat de Qdrant, badge de reclassement).
  • Bouton « Réindexer maintenant » → POST /api/memory/reindex.
  • Sélecteur de source dintégration (auto / distante / statique / transformers + options).
  • Carte de configuration de Qdrant (activation, hôte/port/collection/clé, test de connexion, test de recherche sémantique, nettoyage).
  • Carte de configuration du reclassement (activation, sélecteur de fournisseur/modèle).

Les paramètres de mémoire et de Qdrant sont également disponibles sous /dashboard/settings → Memory & Skills (MemorySkillsTab.tsx) pour linterface des paramètres hérités/globaux.

Mise en cache

src/lib/memory/store.ts conserve un cache en mémoire du processus de type LRU (MEMORY_CACHE_TTL = 1 min, MEMORY_MAX_CACHE_SIZE = 500, avec éviction des 20 % éléments les plus anciens) pour les lectures getMemory(id), ainsi quune couche générique clé/valeur memoryCache (src/lib/memory/cache.ts) avec des méthodes get/set/invalidate utilisées par les appelants qui souhaitent leur propre cache délimité (LRU de 1 000 entrées, TTL par défaut de 5 min).

Confidentialité et cycle de vie

  • Le propriétaire de la mémoire est lidentifiant de la clé API (resolveMemoryOwnerId dans chatCore.ts). Sans apiKeyInfo.id, ni la récupération, ni linjection, ni lextraction ne sont exécutées.
  • Les entrées dont la valeur expires_at se situe dans le futur sont exclues de la récupération ; les anciennes entrées dépassant retentionDays sont exclues par la clause created_at >= cutoff dans retrieveMemories.
  • Pour une suppression définitive, utilisez DELETE /api/memory/[id] ou omniroute_memory_clear.
  • Lextraction est lancée sans attendre son résultat via setImmediate ; les échecs sont journalisés sous memory.extraction.background.failed et ne sont jamais remontés à lappelant.
  • Les allers-retours de vérification (verifyExtractionPipeline) nettoient leurs propres entrées de test dans un bloc finally.

Voir aussi

  • SKILLS.md — le paramètre skillsEnabled injecte les définitions doutils en même temps que la mémoire.
  • MCP-SERVER.md — transport et portées MCP.
  • API_REFERENCE.md — surface dAPI plus étendue.
  • Modules sources :
    • src/lib/memory/types.ts, schemas.ts
    • src/lib/memory/store.ts, retrieval.ts, injection.ts, reindex.ts
    • src/lib/memory/extraction.ts, summarization.ts, verify.ts
    • src/lib/memory/settings.ts, qdrant.ts, cache.ts
    • src/lib/memory/vectorStore.ts — sqlite-vec + RRF hybride
    • src/lib/memory/embedding/index.ts — couche dembedding multisource
    • src/lib/memory/embedding/types.ts, remote.ts, staticPotion.ts, transformersLocal.ts, cache.ts
    • src/shared/schemas/memory.ts — schémas Zod pour tous les corps de requête de lAPI de mémoire
    • src/shared/schemas/qdrant.ts — schémas Zod pour les paramètres/opérations Qdrant
    • src/lib/db/memoryVec.ts — opérations CRUD pour memory_vec_meta
    • src/lib/db/migrations/015_create_memories.sql, 022_add_memory_fts5.sql, 023_fix_memory_fts_uuid.sql, 083_memory_vec.sql
    • src/app/api/memory/route.ts, [id]/route.ts, health/route.ts
    • src/app/api/memory/retrieve-preview/route.ts
    • src/app/api/memory/engine-status/route.ts
    • src/app/api/memory/embedding-providers/route.ts
    • src/app/api/memory/summarize/route.ts
    • src/app/api/memory/reindex/route.ts
    • src/app/api/settings/memory/route.ts
    • src/app/api/settings/qdrant/route.ts + sous-routes
    • src/app/(dashboard)/dashboard/memory/ — interface utilisateur Studio (page + composants + onglets + hooks)
    • open-sse/handlers/chatCore.ts (câblage de linjection et de lextraction)
    • open-sse/mcp-server/tools/memoryTools.ts

Choisir un fournisseur dembeddings (v3.8.16+)

Le moteur de mémoire dOmniRoute prend en charge quatre sources dembeddings (src/lib/memory/embedding/). Chacune présente différents compromis en matière de latence, coût, qualité du modèle et complexité de configuration.

Les sources dembeddings

Fournisseur Source Latence Coût Qualité Configuration
transformers Modèle ONNX local (Xenova/all-MiniLM-L6-v2) ~50-150ms (CPU) Gratuit Bonne npm install uniquement
static Vecteurs précalculés (mis en cache) <1ms Gratuit S/O (dépend du cache) Aucune
remote API OpenAI / Cohere / Voyage ~100-300ms $0.02-0.10/1M tokens Excellente Clé API
auto Sélectionne la meilleure source disponible à lexécution Identique à la source choisie Gratuit Identique à la source choisie Aucune
(cache) Couche LRU en mémoire sur nimporte quelle source <1ms (succès), latence complète (échec) Gratuit Identique à la source sous-jacente Toujours active (source non sélectionnable)

Arbre de décision

                  Quel est votre contexte de déploiement ?
                  │
      ┌───────────┼───────────┬──────────────┐
      │           │           │              │
  DÉV/TEST    PETITE PROD  GRANDE PROD   EDGE / HORS LIGNE
      │           │           │              │
      ▼           ▼           ▼              ▼
  transformers transformers remote (Qdrant) transformers
  (gratuit, sans API)       (qualité optimale) (sans Internet)
      │           │           │              │
      └────────┬──┴───────────┴──────────────┘
               │
               ▼
            TOUJOURS ajouter la couche `cache` par-dessus
            (`LruCache` encapsule nimporte quel fournisseur)

Configuration de la base de données et de lAPI

Les options dembedding de la mémoire sont configurées via lAPI/linterface utilisateur des paramètres, et non via des variables denvironnement. Les clés de base de données pertinentes sous Paramètres (normalizeMemorySettings dans src/lib/memory/settings.ts) sont :

  • memoryEmbeddingSource : "transformers" (local), "remote" (basé sur une API, par ex. OpenAI), "static" (stockage externe) ou "auto"
  • memoryEmbeddingProviderModel : identifiant du modèle pour les sources distantes/statiques (par ex., "text-embedding-3-small")
  • memoryTransformersEnabled : true | false
  • memoryStaticEnabled : true | false
  • memoryVectorStore : "sqlite-vec", "qdrant" ou "auto"

Modèle local (transformers)

Utilise transformers.js en interne pour exécuter des modèles locaux :

# Variables denvironnement lues dans le code (src/lib/memory/embedding/index.ts) :
MEMORY_TRANSFORMERS_MODEL=Xenova/all-MiniLM-L6-v2  # Dépôt du modèle HF
MEMORY_STATIC_MODEL=minishlab/potion-base-8M       # Modèle potion statique HF
MEMORY_STATIC_CACHE_DIR=<DATA_DIR>/embeddings      # Répertoire du cache

Cache LRU des embeddings

Le cache est toujours activé par défaut et configuré via des variables denvironnement :

MEMORY_EMBEDDING_CACHE_MAX=1000                    # Nombre maximal déléments mis en cache
MEMORY_EMBEDDING_CACHE_TTL_MS=300000               # TTL (5 min)

Mesures de performances

Benchmark sur un serveur x86 classique à 4 cœurs (textes denviron 100 tokens chacun) :

Fournisseur p50 p95 p99 Coût / 1 M de représentations vectorielles
transformers (CPU) 80ms 180ms 350ms Gratuit
remote (OpenAI) 120ms 220ms 400ms ~$0.02 (ada-002) / $0.13 (3-large)
static (Qdrant) 15ms 30ms 60ms Dépend de lhébergement de Qdrant
cache (succès) <1ms <1ms 2ms Gratuit

Modèles dextraction de faits (v3.8.16+)

Le module extraction.ts (src/lib/memory/extraction.ts) utilise une correspondance par expressions régulières pour extraire des faits structurés des messages de conversation. Comprendre ces modèles vous aide à ajuster la qualité de lextraction à votre cas dutilisation.

Catégories de modèles par défaut

Catégorie Exemple de modèle Éléments capturés
PREFERENCE_PATTERNS "Je préfère <X>", "Jaime <X>", "Je déteste <X>" Préférences de lutilisateur
DECISION_PATTERNS "Jutiliserai <X>", "Jai décidé de <X>", "Jai choisi <X>" Décisions de lutilisateur (épisodiques)
PATTERN_PATTERNS "Dhabitude, je <X>", "Je <X> toujours", "Je ne <X> jamais" Modèles comportementaux persistants

Exemples de modèles (simplifiés)

// Extrait de src/lib/memory/extraction.ts
const PREFERENCE_PATTERNS = [
  /\bI\s+(?:really\s+)?prefer\s+([^.,\n]+)/gi,
  /\bI\s+(?:really\s+)?like\s+([^.,\n]+)/gi,
  /\bI\s+(?:hate|dislike|avoid)\s+([^.,\n]+)/gi,
];
const DECISION_PATTERNS = [
  /\bI'?(?:ll|will)\s+use\s+([^.,\n]+)/gi,
  /\bI\s+(?:have\s+)?decided\s+(?:to\s+)?([^.,\n]+)/gi,
];
const PATTERN_PATTERNS = [/\bI\s+usually\s+([^.,\n]+)/gi, /\bI\s+always\s+([^.,\n]+)/gi];

Éléments extraits

Lorsquun utilisateur dit :

« Je préfère TypeScript. Jutiliserai Postgres pour ce projet. Je fais toujours un commit avant de pousser. Je naime pas Python. » Lextraction produit 4 souvenirs :

Clé Catégorie Type Contenu
preference:typescript preference factual « TypeScript »
decision:postgres_for_this_project decision episodic « Postgres pour ce projet »
pattern:commit_before_pushing pattern factual « faire un commit avant de pousser »
preference:python preference factual « Python »

Limites de lextraction

Pour éviter une extraction incontrôlée, les limites suivantes sappliquent :

| Longueur minimale du contenu | 3 caractères | | Longueur maximale du contenu | 500 caractères |

Quand désactiver lextraction

Lextraction sexécute automatiquement chaque fois que la mémoire est activée ; il nexiste pas doption distincte permettant dactiver ou de désactiver uniquement lextraction. Pour la désactiver, désactivez entièrement la mémoire (enabled: false via PUT /api/settings/memory). Envisagez de le faire dans les cas suivants :

  • Vous traitez un volume élevé de messages et le coût de lextraction nest pas négligeable
  • Vos conversations sont principalement temporaires (discussion, débogage) et nont aucune valeur à long terme
  • Vous capturez déjà le contexte au moyen de plugins personnalisés

Ajustement du RRF hybride (v3.8.16+)

Lalgorithme Reciprocal Rank Fusion (RRF) combine les résultats de FTS5 (mots-clés) et vectoriels (sémantiques). Le paramètre k contrôle le poids accordé aux résultats moins bien classés.

La formule

Pour chaque souvenir candidat, le score RRF est :

RRF(d) = Σ  1 / (k + rank_i(d))

Où :

  • k est la constante (60 par défaut)
  • rank_i(d) est le rang du document d dans le iᵉ système de recherche (FTS, vectoriel)
  • La somme porte sur tous les systèmes de recherche

Influence de k sur les résultats

Valeur de k Effet Idéal pour
k=0 Fusion pure des rangs (sans lissage) Référence théorique
k=10-30 Accorde beaucoup de poids aux premiers résultats ; les rangs faibles contribuent à peine Lorsque les 3 premiers résultats sont généralement corrects
k=60 (par défaut) Équilibré — les 10 premiers résultats contribuent tous de manière significative Recherche généraliste
k=100+ Plus uniforme — même les résultats de rang faible peuvent dominer sils apparaissent dans plusieurs systèmes Lorsque le rappel est plus important que la précision

Ajustement de k en pratique

# Valeur par défaut
MEMORY_RRF_K=60

# Précision élevée (petite mémoire, peu de documents)
MEMORY_RRF_K=20

# Rappel maximal (grande mémoire, requêtes variées)
MEMORY_RRF_K=120

Exemple avec k=20 :

  • Rang FTS 1 → contribution 1/21 = 0.048
  • Rang FTS 10 → contribution 1/30 = 0.033
  • Rang vectoriel 1 → contribution 0.048
  • Maximum combiné : 0.096

Exemple avec k=60 :

  • Rang FTS 1 → contribution 1/61 = 0.016
  • Rang FTS 10 → contribution 1/70 = 0.014
  • Rang vectoriel 1 → contribution 0.016
  • Maximum combiné : 0.033

Avec une valeur de k plus élevée, la différence relative entre le premier résultat et celui de rang 10 est plus faible ; lalgorithme sappuie donc davantage sur le consensus entre les systèmes de recherche que sur le niveau de confiance associé au premier rang.

Quand modifier k

Symptôme Essai recommandé
Le premier résultat gagne toujours, mais il est incorrect Réduire k (p. ex., 20) — le niveau de confiance du premier rang compte davantage
La bonne réponse figure dans les 5 premiers résultats, mais pas en première position Augmenter k (p. ex., 100) — un score plus uniforme favorise le consensus
Le rappel est élevé, mais la précision est faible Réduire k — affiner le classement
Le rappel est faible (des documents pertinents manquent) Augmenter k — donner une chance aux documents moins bien classés

Pondération RRF

La fusion réciproque des rangs utilise des poids égaux pour le rang vectoriel sémantique et le rang de recherche en texte intégral :

RRF(d) = 1/(k + rank_vector) + 1/(k + rank_fts)

Il nexiste aucune variable denvironnement permettant dajuster les poids individuels (MEMORY_RRF_VECTOR_WEIGHT/MEMORY_RRF_FTS_WEIGHT nexistent pas).


Stratégie de synthèse (v3.8.16+)

Le module summarization.ts (src/lib/memory/summarization.ts) compresse les souvenirs plus anciens afin de limiter la taille de lensemble actif tout en préservant les capacités de rappel.

Quand la synthèse se déclenche

Déclencheur Seuil (par défaut)
Déclenchement manuel via lAPI s/o

Ce qui est synthétisé

Deux points dentrée sont exportés depuis summarization.ts :

  • summarizeMemories(apiKeyId, sessionId?, maxTokens = 4000) — condense les souvenirs dune session en un seul texte de synthèse respectant un budget de jetons.
  • summarizeMemoriesOlderThan(apiKeyId, days, dryRun) — la compaction basée sur lâge utilisée par lAPI : elle sélectionne chaque souvenir antérieur à days, crée à partir de ceux-ci un unique souvenir de synthèse condensé et, lorsque dryRun vaut false, supprime les originaux. Transmettez dryRun: true pour prévisualiser lensemble des candidats et le nombre total de jetons sans rien modifier.

Il nexiste aucune étape de regroupement par étiquette/clé ni aucune notation « essentiel ou synthétisable » par souvenir — la sélection repose uniquement sur la limite dâge, et le texte de synthèse est constitué dune ligne condensée préfixée par le type pour chaque candidat.

Déclencher la synthèse

La synthèse est manuelle / facultative — le paramètre autoSummarize vaut false par défaut, de sorte que rien nest compacté automatiquement. Déclenchez-la via lAPI :

curl -X POST http://localhost:20128/api/memory/summarize \
  -H "Authorization: Bearer $OMNIROUTE_KEY"

Pour la laisser désactivée, conservez simplement la valeur par défaut de autoSummarize (false).

Conseils pour améliorer la qualité de la synthèse

  • Commencez par une prévisualisation avec dryRunsummarizeMemoriesOlderThan(..., true) renvoie la liste des candidats et le nombre total de jetons afin que vous puissiez vérifier ce qui serait fusionné avant de supprimer les originaux.
  • Exécutez la synthèse pendant les heures de faible trafic si vous disposez dun corpus de souvenirs volumineux — lappel au LLM est létape la plus lente
# Style cron : effectuer la synthèse chaque jour à 3 h
0 3 * * * curl -X POST http://localhost:20128/api/memory/summarize \
  -H "Authorization: Bearer $OMNIROUTE_KEY"

Modèle de fournisseur MemoryBackend

Source de référence : src/lib/memory/backend.ts, src/lib/memory/genericBackend.ts, src/lib/memory/manager.ts Tests : src/lib/memory/__tests__/generic-backend.test.ts

Le modèle de fournisseur MemoryBackend introduit une couche dabstraction de backend extensible au-dessus du moteur de mémoire existant. Au lieu dêtre lié à une seule implémentation de stockage, le système de mémoire prend désormais en charge plusieurs backends (SQLite, Obsidian, Notion, backends HTTP personnalisés) avec un routage configurable entre le backend principal et les backends de secours.

Architecture

┌──────────────────────────────────────────────────────────┐
│                    Routes API                             │
│            (src/app/api/memory/route.ts)                  │
└──────────────────────┬───────────────────────────────────┘
                       │
┌──────────────────────▼───────────────────────────────────┐
│                   MemoryManager                           │
│       Orchestrateur singleton (manager.ts)                │
│                                                          │
│  Principal ─► Backend A  (p. ex. SQLite)                 │
│  Secours ───► Backend B  (p. ex. Obsidian)               │
│               Backend C  (p. ex. Notion via GenericBackend)│
└──────────────────────┬───────────────────────────────────┘
                       │
        ┌──────────────┼──────────────┐
        ▼              ▼              ▼
┌────────────┐ ┌────────────┐ ┌──────────────────┐
│ Backend    │ │ Backend    │ │ Backend mémoire  │
│ SQLite     │ │ Obsidian   │ │ générique (HTTP) │
└────────────┘ └────────────┘ └──────────────────┘

Interface principale (backend.ts)

Chaque backend doit implémenter linterface MemoryBackend :

interface MemoryBackend {
  readonly id: string;
  readonly displayName: string;

  // CRUD
  create(input: CreateMemoryInput): Promise<Memory>;
  get(id: string): Promise<Memory | null>;
  update(id: string, updates: Partial<...>): Promise<boolean>;
  delete(id: string): Promise<boolean>;
  list(filter: MemoryFilter): Promise<{ data: Memory[]; total: number; byType: Record<string, number> }>;

  // Recherche
  search(config: SearchConfig): Promise<Memory[]>;

  // État
  health(): Promise<HealthCheckResult>;

  // Cycle de vie (facultatif)
  initialize?(): Promise<void>;
  shutdown?(): Promise<void>;
}

MemoryManager (manager.ts)

Orchestrateur singleton qui :

  • Enregistre les backends via register(backend) — appelé au démarrage depuis index.ts
  • Configure le backend principal et les backends de secours via configure(primary, fallbacks)
  • Achemine les opérations CRUD et les recherches vers le backend principal, avec une chaîne de secours en cas déchec
  • Vérifie létat de tous les backends périodiquement

Comportement de secours :

Opération Principal Backends de secours
create Principal uniquement
get Essayer dabord le principal Secours si la valeur est nulle
update Principal uniquement Synchronisation sans attente de résultat
delete Principal uniquement Synchronisation sans attente de résultat
list Principal uniquement
search Principal dabord Secours en cas derreur

GenericMemoryBackend (genericBackend.ts)

Un connecteur HTTP générique qui adapte nimporte quelle API REST en MemoryBackend. Utile pour :

  • Notion — connexion via lAPI Notion
  • Obsidian — connexion via lAPI REST locale dObsidian
  • Backends personnalisés — tout service exposant une API RESTful de mémoire

Configuration :

interface GenericBackendConfig {
  baseUrl: string;           // URL de base de lAPI du backend
  apiKey?: string;           // Jeton Bearer pour lauthentification
  headers?: Record<string, string>;  // En-têtes HTTP personnalisés
  timeout?: number;          // Délai dexpiration de la requête (par défaut : 30000ms)
  backendType?: string;      // Pour la journalisation

  // Remplacements des points de terminaison (les valeurs par défaut suivent les conventions REST)
  endpoints?: {
    search?: string;   // par défaut : "/memories/search"
    create?: string;   // par défaut : "/memories"
    list?: string;     // par défaut : "/memories"
    get?: string;      // par défaut : "/memories/{id}"
    update?: string;   // par défaut : "/memories/{id}"
    delete?: string;   // par défaut : "/memories/{id}"
    health?: string;   // par défaut : "/health"
  };

  // Correspondances des noms de paramètres de requête
  queryParams?: {
    query?/apiKeyId?/limit?/offset?/strategy?/maxTokens?/type?/sessionId?/orderBy?/orderDir?/options?
  };

  // Correspondances des noms de paramètres de chemin
  pathParams?: {
    id?/memoryId?
  };
}

Les backends connus sont préconfigurés dans KNOWN_BACKENDS :

createKnownBackend("obsidian"); // → GenericMemoryBackend pointant vers localhost:27123
createKnownBackend("notion"); // → GenericMemoryBackend pointant vers api.notion.com/v1

Backends intégrés

SQLiteBackend (sqliteBackend.ts)

Le backend principal par défaut. Encapsule le stockage de mémoire SQLite existant à laide de src/lib/memory/store.ts. Il est automatiquement enregistré au démarrage.

import { sqliteBackend } from "./sqliteBackend";
memoryManager.register(sqliteBackend);
ObsidianBackend (obsidianBackend.ts)

Encapsule lintégration Obsidian existante (src/lib/memory/obsidianBackend.ts). Se connecte à un coffre Obsidian via lAPI REST locale dObsidian.

Paramètres

Les paramètres des backends de mémoire sont stockés dans la table des paramètres de lapplication et gérés via src/lib/memory/settings.ts :

Paramètre Clé denvironnement/configuration Valeur par défaut Description
Backend principal memoryPrimaryBackend "sqlite" ID du backend principal
Backends de repli memoryFallbackBackends [] ID ordonnés des backends de repli
Configurations des backends memoryBackendConfigs {} Remplacements de configuration par backend

Les paramètres sont normalisés via normalizeMemorySettings() et mis en cache dans getMemorySettings().

Flux dinitialisation

Amorçage de lapplication
  → Importations de index.ts (effet secondaire) : enregistre SQLiteBackend
  → initMemoryBackends() appelé depuis le cycle de vie de lapplication :
      1. Charger les paramètres (getMemorySettings)
      2. Configurer le backend principal et les backends de repli
      3. Initialiser tous les backends (vérification de létat)
      4. Prêt à traiter les requêtes

Ajout dun nouveau backend

  1. Implémenter linterface MemoryBackend dans src/lib/memory/<name>Backend.ts
  2. Exporter depuis src/lib/memory/index.ts
  3. Enregistrer avec memoryManager.register(yourBackend) au démarrage
  4. Configurer via les paramètres : définir memoryPrimaryBackend sur lID de votre backend
  5. Tester en prenant src/lib/memory/__tests__/generic-backend.test.ts comme référence

Exemple : backend Brain

import { createGenericMemoryBackend } from "./genericBackend";

const brainBackend = createGenericMemoryBackend("brain", "BK-Brain", {
  baseUrl: process.env.BRAIN_API_URL || "http://localhost:9099",
  apiKey: process.env.BRAIN_API_KEY,
  endpoints: {
    search: "/api/memory/search",
    create: "/api/memory",
    health: "/api/health",
  },
});

memoryManager.register(brainBackend);

Vérification

Tests unitaires

npx vitest run src/lib/memory/__tests__/generic-backend.test.ts --reporter=verbose

Résultat attendu : 35 tests, tous réussis, couvrant :

  • Constructeur (2)
  • Vérification de létat (4) — réussite, échec 500, erreur réseau, latence
  • Initialisation (2) — réussite, échec
  • Création (2) — point de terminaison par défaut, point de terminaison personnalisé
  • Récupération (4) — réussite, 404 → null, exception pour un code autre que 404, paramètres de chemin personnalisés
  • Mise à jour (2) — réussite, 404 → false
  • Suppression (2) — réussite, 404 → false
  • Liste (2) — paramètres de requête, noms de paramètres personnalisés
  • Recherche (3) — paramètres de requête, point de terminaison personnalisé, sérialisation des options
  • En-têtes dauthentification (2) — jeton Bearer, en-têtes personnalisés
  • Fabrique (1)

Vérification des types

npm run typecheck:core

Résultat attendu : 0 erreur.