export const TRADE_AI = 'TRADE_AI'; export interface TradeAnswer { text: string; inputTokens: number; outputTokens: number; /** Capacites reellement invoquees pour produire cette reponse. */ actions?: TradeAction[]; } /** Trace d'un appel d'outil, conservee avec le message et affichee a l'utilisateur. */ export interface TradeAction { name: string; ok: boolean; } /** * Outil propose au modele. * * Le domaine ne connait ni OpenAI ni MCP : il decrit un nom, une phrase et un * schema JSON. Chaque adaptateur traduit ensuite vers son propre format. */ export interface TradeToolDefinition { name: string; description: string; parameters: Record; } /** * Execute un outil au nom de l'utilisateur courant. * * La fonction est fournie par la couche application, deja liee a l'identite de * l'appelant : l'adaptateur ne peut pas choisir pour qui il agit. */ export type TradeToolInvoker = ( name: string, args: Record ) => Promise<{ ok: boolean; result: unknown }>; /** Un tour deja echange dans la conversation, envoye au modele comme contexte. */ export interface TradeTurn { role: 'user' | 'assistant'; content: string; } /** Un extrait du wiki retenu par la recherche, cite sous la reponse. */ export interface TradePassage { id: string; /** Titre du sujet wiki, ex. « Procedures Douanieres ». */ title: string; /** Section a l'interieur du sujet, ex. « Regimes Douaniers ». */ section: string; /** Lien vers la page wiki, ex. `/dashboard/wiki/douanes`. */ href: string; text: string; score: number; } export interface TradeAskInput { question: string; language: string; /** Tours precedents, du plus ancien au plus recent. */ history: TradeTurn[]; /** Extraits du wiki a citer en priorite. */ passages: TradePassage[]; /** Capacites ouvertes a cet utilisateur. Vide : l'assistant ne fait que repondre. */ tools?: TradeToolDefinition[]; invokeTool?: TradeToolInvoker; } export interface TradeAiPort { isAvailable(): boolean; answer(input: TradeAskInput): Promise; } /* -------------------------------------------------------------------------- */ /* Recherche documentaire */ /* -------------------------------------------------------------------------- */ export const TRADE_RETRIEVAL = 'TRADE_RETRIEVAL'; export interface TradeRetrievalPort { /** Extraits du wiki les plus proches de la question, dans sa langue. */ search(question: string, language: string, limit?: number): Promise; } export const TRADE_EMBEDDINGS = 'TRADE_EMBEDDINGS'; export interface TradeEmbeddingPort { isAvailable(): boolean; /** Vecteurs normes, dans l'ordre des textes fournis. */ embed(texts: string[]): Promise; } /* -------------------------------------------------------------------------- */ /* Quota */ /* -------------------------------------------------------------------------- */ export const TRADE_QUOTA = 'TRADE_QUOTA'; export interface TradeUsage { day: string; resetsAt: string; used: number; } export interface TradeQuotaPort { get(userId: string): Promise; reserve(userId: string, day: string, limit: number): Promise; release(userId: string, day: string): Promise; recordTokens(userId: string, day: string, answer: TradeAnswer): Promise; } /* -------------------------------------------------------------------------- */ /* Conversations */ /* -------------------------------------------------------------------------- */ export const TRADE_CONVERSATIONS = 'TRADE_CONVERSATIONS'; /** Source citee sous une reponse, telle qu'elle est persistee. */ export interface TradeSource { title: string; section: string; href: string; } export interface TradeMessage { id: string; role: 'user' | 'assistant'; content: string; sources: TradeSource[]; /** Capacites invoquees pour produire ce message. Vide cote utilisateur. */ actions: TradeAction[]; createdAt: string; } export interface TradeConversationSummary { id: string; title: string; createdAt: string; updatedAt: string; messageCount: number; } export interface TradeConversationRepository { list(userId: string): Promise; create(userId: string, title: string): Promise; /** `null` si la conversation n'existe pas ou n'appartient pas a l'utilisateur. */ find(userId: string, conversationId: string): Promise; messages(userId: string, conversationId: string): Promise; addMessage( conversationId: string, role: 'user' | 'assistant', content: string, sources?: TradeSource[], actions?: TradeAction[] ): Promise; rename(userId: string, conversationId: string, title: string): Promise; remove(userId: string, conversationId: string): Promise; }