Co-Authored-By: Claude Opus 5 <noreply@anthropic.com> Claude-Session: https://claude.ai/code/session_018BAUeCFpDkRD6tU5wGsc1C
310 lines
11 KiB
TypeScript
310 lines
11 KiB
TypeScript
import { Inject, Injectable, Logger } from '@nestjs/common';
|
||
import { ConfigService } from '@nestjs/config';
|
||
import { createHash } from 'crypto';
|
||
import { CACHE_PORT, CachePort } from '@domain/ports/out/cache.port';
|
||
import {
|
||
TRADE_EMBEDDINGS,
|
||
TradeEmbeddingPort,
|
||
TradePassage,
|
||
TradeRetrievalPort,
|
||
} from '@domain/ports/out/trade-assistant.port';
|
||
import corpus from './knowledge/wiki-corpus.json';
|
||
|
||
/**
|
||
* Recherche documentaire sur le wiki du site.
|
||
*
|
||
* Le corpus est celui de `dashboard.wikiPages` (voir
|
||
* `scripts/setup/build-knowledge-corpus.js`) : l'assistant repond donc a partir
|
||
* de la documentation que l'utilisateur peut ouvrir, et chaque reponse peut
|
||
* citer la page correspondante.
|
||
*
|
||
* Deux caches evitent de refaire le meme calcul :
|
||
*
|
||
* 1. **L'index** est vectorise une seule fois. La cle Redis contient l'empreinte
|
||
* du corpus : tant que le wiki ne change pas, aucun appel d'embedding n'est
|
||
* refait, meme apres un redemarrage ou un deploiement. Il est aussi garde en
|
||
* memoire du processus, donc une recherche ne touche Redis qu'au demarrage.
|
||
* 2. **Les questions** sont vectorisees une fois par formulation, pour tous les
|
||
* utilisateurs : reposer la meme question ne coute pas un second appel.
|
||
*
|
||
* Sans cle OpenAI — ou si l'API echoue — la recherche bascule sur un score
|
||
* lexical. Une reponse moins bien documentee vaut mieux qu'une page en erreur.
|
||
*/
|
||
|
||
interface CorpusDocument {
|
||
id: string;
|
||
locale: string;
|
||
topic: string;
|
||
title: string;
|
||
section: string;
|
||
href: string;
|
||
text: string;
|
||
}
|
||
|
||
const DOCUMENTS = (corpus as { documents: CorpusDocument[] }).documents;
|
||
|
||
/** Empreinte du corpus : change des que la documentation du site change. */
|
||
const CORPUS_FINGERPRINT = createHash('sha256')
|
||
.update(DOCUMENTS.map(d => `${d.id} |