@mostajs/chatbot 0.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/CHANGELOG.md +51 -0
- package/LICENSE +21 -0
- package/README.md +50 -0
- package/dist/backends/ai.d.ts +12 -0
- package/dist/backends/ai.js +71 -0
- package/dist/backends/router.d.ts +11 -0
- package/dist/backends/router.js +39 -0
- package/dist/backends/scripted.d.ts +8 -0
- package/dist/backends/scripted.js +25 -0
- package/dist/client.d.ts +6 -0
- package/dist/client.js +34 -0
- package/dist/config.d.ts +32 -0
- package/dist/config.js +49 -0
- package/dist/index.d.ts +36 -0
- package/dist/index.js +45 -0
- package/dist/registry.d.ts +14 -0
- package/dist/registry.js +10 -0
- package/dist/server.d.ts +11 -0
- package/dist/server.js +12 -0
- package/dist/types.d.ts +51 -0
- package/dist/types.js +2 -0
- package/llms.txt +59 -0
- package/package.json +65 -0
package/CHANGELOG.md
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
# Changelog — @mostajs/chatbot
|
|
2
|
+
|
|
3
|
+
**Auteur** : Dr Hamid MADANI <drmdh@msn.com>
|
|
4
|
+
|
|
5
|
+
## 0.2.0 — 2026-06-10
|
|
6
|
+
- **Extraction en modules (façon @mostajs/orm)** : la couche LLM et la boucle/routeur quittent le chatbot.
|
|
7
|
+
- **`@mostajs/llm`** : dialectes (1 par IA) + registre + types function-calling (déplacés depuis `src/dialects/`).
|
|
8
|
+
- **`@mostajs/btool-use`** : boucle tool-use `runToolLoop` (extraite de `backends/ai.ts`).
|
|
9
|
+
- **`@mostajs/intent-router`** : routeur déterministe intents→outils (extrait de `backends/router.ts`).
|
|
10
|
+
- Le chatbot **compose** ces 3 modules ; backends `ai` (→ `runToolLoop`) et `router` (→ `intent-router`) recâblés.
|
|
11
|
+
- **Compat préservée** : `types.ts` réexporte sous les noms historiques (`ChatActor`/`ChatTool`/`ChatChunk`/`ChatIntent`),
|
|
12
|
+
`index.ts`/`server.ts` réexportent dialectes + `getLlmProvider`/`registerDialect`/`runToolLoop`/`matchIntent`… → **code app inchangé**.
|
|
13
|
+
- `src/dialects/` et `src/providers/` supprimés. Regroupement physique dans `mostajs/mosta-chat-stack/`.
|
|
14
|
+
- Vérifié : build vert ; app typecheck clean ; tool-use live DeepSeek **6/6** ; routeur cross-modules **5/5 (zéro IA)**.
|
|
15
|
+
|
|
16
|
+
## 0.1.0 — 2026-06-10
|
|
17
|
+
- **Backend `router` (routeur hybride)** : résout d'abord les **intents déterministes** fournis par
|
|
18
|
+
l'app (`ChatIntent` : slash-commande `/cmd` + motifs regex → outil + extraction d'arguments) en
|
|
19
|
+
appelant `tool.run(args, actor)` **DIRECTEMENT, sans aucun appel IA** (coût zéro, fiable, pas
|
|
20
|
+
d'hallucination) ; bascule sur l'IA uniquement en **repli** (`CHATBOT_ROUTER_FALLBACK=ai|scripted|none`,
|
|
21
|
+
défaut `ai`). Mêmes garde-fous d'écriture (autorisation + `needsConfirmation`). → « moins d'IA, plus
|
|
22
|
+
d'actions par outils maison » ; le chatbot **fonctionne même sans clé / hors-ligne**. Test `test-scripts/run-router-test.sh` **5/5**.
|
|
23
|
+
- **Boucle tool-use (function-calling)** dans le backend `ai` : `complete()` → `tool_calls` →
|
|
24
|
+
`tool.run(args, actor)` → résultat renvoyé → reboucle → texte final (`MAX_TOOL_ROUNDS=6`). Débloque
|
|
25
|
+
le **niveau A** (exécution réelle d'outils). Écritures gardées : autorisation wildcard-aware
|
|
26
|
+
(`actorCanUse`) **et** confirmation humaine (les outils renvoient un aperçu `needsConfirmation` tant
|
|
27
|
+
que `confirm:true` n'est pas fourni). Streaming texte conservé en repli (pas d'outils / provider sans `complete`).
|
|
28
|
+
- **Architecture dialectes** (calquée sur les *dialects* de `@mostajs/orm` : un dialecte par IA, DRY par
|
|
29
|
+
héritage). Interface `ILlmDialect` + registre `registerDialect`/`getDialect`/`getSupportedDialects`.
|
|
30
|
+
- Familles : **`OpenAiDialect`** (format OpenAI-compatible, `fetch`, zéro dép) et **`AnthropicDialect`**
|
|
31
|
+
(SDK, `tool_use`/`tool_result`, blocs bruts préservés pour le thinking adaptatif).
|
|
32
|
+
- Dialectes concrets (≈8 lignes, façon `CockroachDBDialect extends PostgresDialect`) : **`deepseek`**,
|
|
33
|
+
**`openai`**, **`mistral`**, **`groq`**, **`ollama`** (local, sans clé), **`anthropic`**.
|
|
34
|
+
- `providers/{deepseek,anthropic}` conservés en **alias rétrocompatibles** vers les dialectes.
|
|
35
|
+
- Métadonnées driver (clé/endpoint/modèle/dépendance + `available()`) façon `installHint` de l'ORM.
|
|
36
|
+
- Test live (vrais appels DeepSeek) **6/6** : `order_status` réellement invoqué + valeur réelle restituée
|
|
37
|
+
(anti-hallucination) ; action d'écriture non exécutée sans autorisation, exécutée après confirmation.
|
|
38
|
+
|
|
39
|
+
## 0.0.2 — 2026-06-08
|
|
40
|
+
- **Règles de conduite du copilote** (gouvernance, à la DEVRULES) : champ `DomainProfile.rules?: string[]` + `CHATBOT_RULES` (`.env`, séparées par `|`), **injectés dans le prompt système** du backend `ai` sous un bloc « RÈGLES À RESPECTER IMPÉRATIVEMENT ». L'app peut alimenter `rules` depuis un **fichier** lu au runtime (ex. `COPILOT-RULES.md`). Helper `chatbotRules()`.
|
|
41
|
+
|
|
42
|
+
## 0.0.1 — 2026-06-08
|
|
43
|
+
- Scaffold (DEVRULES §5) après livrables #1-#3 (`docs/`). Assistant/copilote/agent **générique réutilisable**.
|
|
44
|
+
- **Interrupteur maître** `CHATBOT_ENABLED` + rôles `CHATBOT_ROLES` + mode `CHATBOT_MODE=assistant|agent` (`.env`).
|
|
45
|
+
- Registres pluggables (style dialectes) : **backends** (`scripted` défaut, `ai`), **providers LLM**, **outils** métier.
|
|
46
|
+
- Providers LLM : **`anthropic`** (`@anthropic-ai/sdk`, `claude-opus-4-8`, adaptive thinking + streaming + option
|
|
47
|
+
web search) et **`deepseek`** (API compatible OpenAI, `fetch` SSE, sans dépendance).
|
|
48
|
+
- **Multi-provider `CHATBOT_PROVIDERS=deepseek,anthropic,…`** avec **fallback** automatique.
|
|
49
|
+
- API : `createChatbot({domain,tools})` → `enabled()/stream()/ask()` ; `registerBackend/LlmProvider/Tool`.
|
|
50
|
+
- Client : widget React minimal `<Chatbot send=… />` (`./client`). Backend `scripted` minimal.
|
|
51
|
+
- **Tout piloté par `.env`** (@mostajs/config, cascade MOSTA_ENV). Build `tsc` vert.
|
package/LICENSE
ADDED
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
@mostajs/i18n
|
|
2
|
+
Copyright (C) 2026 Dr Hamid MADANI <drmdh@msn.com>
|
|
3
|
+
|
|
4
|
+
SPDX-License-Identifier: AGPL-3.0-or-later
|
|
5
|
+
|
|
6
|
+
This program is free software: you can redistribute it and/or modify
|
|
7
|
+
it under the terms of the GNU Affero General Public License as published by
|
|
8
|
+
the Free Software Foundation, either version 3 of the License, or
|
|
9
|
+
(at your option) any later version.
|
|
10
|
+
|
|
11
|
+
This program is distributed in the hope that it will be useful,
|
|
12
|
+
but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
13
|
+
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
14
|
+
GNU Affero General Public License for more details.
|
|
15
|
+
|
|
16
|
+
You should have received a copy of the GNU Affero General Public License
|
|
17
|
+
along with this program. If not, see <https://www.gnu.org/licenses/>.
|
|
18
|
+
|
|
19
|
+
The complete text of the GNU Affero General Public License version 3 is
|
|
20
|
+
available at the URL above and must accompany any distribution of this
|
|
21
|
+
software.
|
package/README.md
ADDED
|
@@ -0,0 +1,50 @@
|
|
|
1
|
+
# @mostajs/chatbot
|
|
2
|
+
|
|
3
|
+
**Auteur** : Dr Hamid MADANI <drmdh@msn.com> · **Licence** : AGPL-3.0-or-later
|
|
4
|
+
|
|
5
|
+
Assistant / copilote / agent conversationnel **générique et réutilisable** pour les apps `@mostajs/*`.
|
|
6
|
+
**Activable/désactivable** et **entièrement piloté par `.env`**. Backends pluggables (`scripted` sans IA,
|
|
7
|
+
`ai` avec LLM), **providers LLM pluggables avec fallback** (`anthropic`/`claude-opus-4-8`, `deepseek`),
|
|
8
|
+
ancrage **tool-use** sur les données de l'app, **recherche web** optionnelle. Registres façon dialectes ORM.
|
|
9
|
+
|
|
10
|
+
> Statut : **0.0.1 (scaffold)**. Livrables DEVRULES #1-#3 dans `docs/`. Voir `llms.txt` pour l'API complète.
|
|
11
|
+
|
|
12
|
+
## Configuration (`.env` — tout par config)
|
|
13
|
+
```
|
|
14
|
+
CHATBOT_ENABLED=1 # interrupteur maître (défaut 0)
|
|
15
|
+
CHATBOT_ROLES=admin,employe # vide = tous
|
|
16
|
+
CHATBOT_MODE=assistant # assistant | agent
|
|
17
|
+
CHATBOT_BACKEND=ai # scripted (défaut) | ai
|
|
18
|
+
CHATBOT_PROVIDERS=deepseek,anthropic # liste priorité + fallback
|
|
19
|
+
CHATBOT_MODEL= # vide ⇒ défaut du provider
|
|
20
|
+
ANTHROPIC_API_KEY=... # provider anthropic
|
|
21
|
+
DEEPSEEK_API_KEY=... # provider deepseek (API compatible OpenAI)
|
|
22
|
+
DEEPSEEK_BASE_URL=https://api.deepseek.com
|
|
23
|
+
CHATBOT_WEB_SEARCH=0 # recherche web (anthropic)
|
|
24
|
+
CHATBOT_EFFORT=high
|
|
25
|
+
```
|
|
26
|
+
|
|
27
|
+
## Usage (Next.js)
|
|
28
|
+
```ts
|
|
29
|
+
// serveur
|
|
30
|
+
import { createChatbot, registerTool } from "@mostajs/chatbot";
|
|
31
|
+
registerTool({ name:"orders_by_state", description:"Compter les commandes par état",
|
|
32
|
+
schema:{ type:"object", properties:{ state:{type:"string"} }, required:["state"] },
|
|
33
|
+
permission:"order.read", run: async ({state}, actor) => /* requête dépôt */ 0 });
|
|
34
|
+
const bot = createChatbot({ domain:{ appName:"CRM TRADING",
|
|
35
|
+
description:"Société de négoce : commandes, fournisseurs, proformats.", locale:"fr" } });
|
|
36
|
+
if (bot.enabled()) { const answer = await bot.ask([{role:"user",content:"aide"}], actor); }
|
|
37
|
+
```
|
|
38
|
+
```tsx
|
|
39
|
+
// client (widget)
|
|
40
|
+
import { Chatbot } from "@mostajs/chatbot/client";
|
|
41
|
+
<Chatbot send={askServerAction} title="Assistant TRADING" />
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
## Ajouter un provider LLM (comme un dialecte)
|
|
45
|
+
```ts
|
|
46
|
+
import { registerLlmProvider } from "@mostajs/chatbot";
|
|
47
|
+
registerLlmProvider({ key:"openai", async *stream(messages, tools, opts){ /* SSE */ } });
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
Peers : `@mostajs/config`, `@anthropic-ai/sdk` (optionnel), `react` (optionnel). DeepSeek : sans dépendance.
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend `ai` — orchestre un dialecte LLM (@mostajs/llm). Construit le system prompt à partir du
|
|
3
|
+
* profil métier + des outils, puis :
|
|
4
|
+
* - si des **outils** sont fournis ET que le dialecte sait faire `complete` → **boucle tool-use**
|
|
5
|
+
* (déléguée à `@mostajs/btool-use` : `runToolLoop`) → exécution réelle des outils (gardée + confirmée) ;
|
|
6
|
+
* - sinon → **streaming texte** (réponse ancrée, sans exécution d'outil).
|
|
7
|
+
* Multi-providers `.env` (CHATBOT_PROVIDERS) avec FALLBACK si le 1er chunk est une erreur.
|
|
8
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
9
|
+
*/
|
|
10
|
+
import type { IChatBackend } from "../types.js";
|
|
11
|
+
export declare const aiBackend: IChatBackend;
|
|
12
|
+
//# sourceMappingURL=ai.d.ts.map
|
|
@@ -0,0 +1,71 @@
|
|
|
1
|
+
import { getLlmProvider } from "@mostajs/llm";
|
|
2
|
+
import { runToolLoop, actorCanUse } from "@mostajs/btool-use";
|
|
3
|
+
import { registerBackend } from "../registry.js";
|
|
4
|
+
import { chatbotProviders, chatbotModel, chatbotEffort, chatbotMaxTokens, chatbotWebSearch, chatbotRules } from "../config.js";
|
|
5
|
+
/** Outils exposables à l'acteur (filtre RBAC ; l'autorisation fine reste faite par l'outil lui-même). */
|
|
6
|
+
function visibleTools(ctx) {
|
|
7
|
+
return (ctx.tools ?? []).filter((t) => actorCanUse(ctx.actor, t.permission));
|
|
8
|
+
}
|
|
9
|
+
function systemPrompt(ctx) {
|
|
10
|
+
const tools = visibleTools(ctx);
|
|
11
|
+
const toolList = tools.map((t) => `- ${t.name}: ${t.description}`).join("\n");
|
|
12
|
+
// Règles impératives (gouvernance, à la DEVRULES) : profil métier (`domain.rules`) + ops (`CHATBOT_RULES`).
|
|
13
|
+
const rules = [...(ctx.domain.rules ?? []), ...chatbotRules()].map((r) => r.trim()).filter(Boolean);
|
|
14
|
+
const rulesBlock = rules.length ? `RÈGLES À RESPECTER IMPÉRATIVEMENT :\n${rules.join("\n")}` : "";
|
|
15
|
+
return [
|
|
16
|
+
`Tu es l'assistant de « ${ctx.domain.appName} ». ${ctx.domain.description}`,
|
|
17
|
+
ctx.domain.locale ? `Réponds en langue: ${ctx.domain.locale}.` : "",
|
|
18
|
+
rulesBlock,
|
|
19
|
+
toolList
|
|
20
|
+
? `Tu disposes d'outils que tu peux APPELER directement pour obtenir des données réelles ou agir :\n${toolList}\n` +
|
|
21
|
+
`Appelle l'outil pertinent plutôt que d'inventer une donnée. Pour toute action d'ÉCRITURE, l'outil renvoie ` +
|
|
22
|
+
`d'abord un aperçu (champ « needsConfirmation ») SANS rien modifier : présente cet aperçu à l'utilisateur et ` +
|
|
23
|
+
`attends sa confirmation EXPLICITE avant de rappeler l'outil avec confirm=true.`
|
|
24
|
+
: "",
|
|
25
|
+
`N'invente jamais de données : si une information n'est pas disponible, dis-le.`,
|
|
26
|
+
].filter(Boolean).join("\n\n");
|
|
27
|
+
}
|
|
28
|
+
export const aiBackend = {
|
|
29
|
+
key: "ai",
|
|
30
|
+
async *reply(ctx) {
|
|
31
|
+
const opts = {
|
|
32
|
+
model: chatbotModel() || undefined,
|
|
33
|
+
system: systemPrompt(ctx),
|
|
34
|
+
effort: chatbotEffort(),
|
|
35
|
+
maxTokens: chatbotMaxTokens(),
|
|
36
|
+
webSearch: chatbotWebSearch(),
|
|
37
|
+
};
|
|
38
|
+
const tools = visibleTools(ctx);
|
|
39
|
+
const hasTools = tools.length > 0;
|
|
40
|
+
const keys = chatbotProviders();
|
|
41
|
+
let lastErr = "aucun provider";
|
|
42
|
+
for (const key of keys) {
|
|
43
|
+
const provider = getLlmProvider(key);
|
|
44
|
+
if (!provider) {
|
|
45
|
+
lastErr = `provider « ${key} » inconnu`;
|
|
46
|
+
continue;
|
|
47
|
+
}
|
|
48
|
+
// Boucle tool-use (déléguée à @mostajs/btool-use) si outils + complete ; sinon streaming.
|
|
49
|
+
const it = (hasTools && typeof provider.complete === "function")
|
|
50
|
+
? runToolLoop({ provider, messages: ctx.messages, tools, actor: ctx.actor, opts })[Symbol.asyncIterator]()
|
|
51
|
+
: provider.stream(ctx.messages, ctx.tools ?? [], opts)[Symbol.asyncIterator]();
|
|
52
|
+
const first = await it.next();
|
|
53
|
+
if (!first.done && first.value.type === "error") {
|
|
54
|
+
lastErr = first.value.text ?? "erreur";
|
|
55
|
+
continue;
|
|
56
|
+
}
|
|
57
|
+
if (!first.done)
|
|
58
|
+
yield first.value;
|
|
59
|
+
while (true) {
|
|
60
|
+
const n = await it.next();
|
|
61
|
+
if (n.done)
|
|
62
|
+
break;
|
|
63
|
+
yield n.value;
|
|
64
|
+
}
|
|
65
|
+
return; // provider opérationnel : terminé
|
|
66
|
+
}
|
|
67
|
+
yield { type: "error", text: `Aucun provider LLM disponible (${keys.join(", ") || "—"}). Dernière erreur : ${lastErr}` };
|
|
68
|
+
},
|
|
69
|
+
};
|
|
70
|
+
registerBackend(aiBackend);
|
|
71
|
+
//# sourceMappingURL=ai.js.map
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend `router` — **routeur hybride** : délègue à `@mostajs/intent-router` la résolution des
|
|
3
|
+
* **intents déterministes** (slash-commandes + motifs regex fournis par l'app) → exécution directe
|
|
4
|
+
* de l'outil **sans aucun appel IA** (coût zéro, fiable) ; ne bascule sur l'IA
|
|
5
|
+
* (`CHATBOT_ROUTER_FALLBACK=ai`, défaut) qu'en **repli** pour le langage libre. → « moins d'IA, plus
|
|
6
|
+
* d'actions par outils maison », et le chatbot **n'est pas dépendant de l'IA** (intents OK sans clé / hors-ligne).
|
|
7
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
8
|
+
*/
|
|
9
|
+
import type { IChatBackend } from "../types.js";
|
|
10
|
+
export declare const routerBackend: IChatBackend;
|
|
11
|
+
//# sourceMappingURL=router.d.ts.map
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { matchIntent, runIntent } from "@mostajs/intent-router";
|
|
2
|
+
import { registerBackend, getBackend } from "../registry.js";
|
|
3
|
+
import { chatbotRouterFallback } from "../config.js";
|
|
4
|
+
/** Dernier message utilisateur (le texte à router). */
|
|
5
|
+
function lastUserText(ctx) {
|
|
6
|
+
for (let i = ctx.messages.length - 1; i >= 0; i--)
|
|
7
|
+
if (ctx.messages[i].role === "user")
|
|
8
|
+
return ctx.messages[i].content ?? "";
|
|
9
|
+
return "";
|
|
10
|
+
}
|
|
11
|
+
export const routerBackend = {
|
|
12
|
+
key: "router",
|
|
13
|
+
async *reply(ctx) {
|
|
14
|
+
const intents = ctx.intents ?? [];
|
|
15
|
+
const text = lastUserText(ctx);
|
|
16
|
+
// 1) Intent déterministe → exécution directe, ZÉRO appel IA.
|
|
17
|
+
const hit = intents.length ? matchIntent(text, intents) : null;
|
|
18
|
+
if (hit) {
|
|
19
|
+
yield* runIntent({ intent: hit.intent, args: hit.args, tools: ctx.tools ?? [], actor: ctx.actor });
|
|
20
|
+
return;
|
|
21
|
+
}
|
|
22
|
+
// 2) Repli configurable (langage libre).
|
|
23
|
+
const fb = chatbotRouterFallback();
|
|
24
|
+
if (fb === "none") {
|
|
25
|
+
const cmds = intents.filter((i) => i.command).map((i) => `/${i.command}`).join(" ");
|
|
26
|
+
yield { type: "text", text: cmds ? `Je n'ai pas compris. Commandes disponibles : ${cmds}` : "Je n'ai pas compris la demande." };
|
|
27
|
+
yield { type: "done" };
|
|
28
|
+
return;
|
|
29
|
+
}
|
|
30
|
+
const backend = getBackend(fb) ?? getBackend("ai") ?? getBackend("scripted");
|
|
31
|
+
if (!backend || backend.key === "router") {
|
|
32
|
+
yield { type: "error", text: `Repli « ${fb} » introuvable.` };
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
yield* backend.reply(ctx);
|
|
36
|
+
},
|
|
37
|
+
};
|
|
38
|
+
registerBackend(routerBackend);
|
|
39
|
+
//# sourceMappingURL=router.js.map
|
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Backend `scripted` — réponses à intentions/FAQ, sans IA, sans clé, sans coût (défaut).
|
|
3
|
+
* Minimal en 0.0.1 : aiguillage par mots-clés sur le profil métier ; à enrichir (intents) en 0.1.
|
|
4
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
import type { IChatBackend } from "../types.js";
|
|
7
|
+
export declare const scriptedBackend: IChatBackend;
|
|
8
|
+
//# sourceMappingURL=scripted.d.ts.map
|
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
import { registerBackend } from "../registry.js";
|
|
2
|
+
export const scriptedBackend = {
|
|
3
|
+
key: "scripted",
|
|
4
|
+
async *reply(ctx) {
|
|
5
|
+
const last = [...ctx.messages].reverse().find((m) => m.role === "user")?.content?.toLowerCase() ?? "";
|
|
6
|
+
const tools = (ctx.tools ?? []).map((t) => `• ${t.name} — ${t.description}`).join("\n");
|
|
7
|
+
let text;
|
|
8
|
+
if (/aide|help|mode d'emploi|comment/.test(last)) {
|
|
9
|
+
text = `Assistant ${ctx.domain.appName}. ${ctx.domain.description}\n` +
|
|
10
|
+
(tools ? `Je peux mobiliser :\n${tools}` : "Posez votre question.") +
|
|
11
|
+
`\n\n(Backend « scripted » — activez l'IA via CHATBOT_BACKEND=ai pour des réponses en langage naturel.)`;
|
|
12
|
+
}
|
|
13
|
+
else if (!last) {
|
|
14
|
+
text = `Bonjour 👋 — assistant ${ctx.domain.appName}. Tapez « aide » pour commencer.`;
|
|
15
|
+
}
|
|
16
|
+
else {
|
|
17
|
+
text = `Je suis en mode « scripted » (sans IA) et ne sais pas répondre librement à « ${last} ». ` +
|
|
18
|
+
`Activez le backend IA (CHATBOT_BACKEND=ai) pour une réponse en langage naturel.`;
|
|
19
|
+
}
|
|
20
|
+
yield { type: "text", text };
|
|
21
|
+
yield { type: "done" };
|
|
22
|
+
},
|
|
23
|
+
};
|
|
24
|
+
registerBackend(scriptedBackend);
|
|
25
|
+
//# sourceMappingURL=scripted.js.map
|
package/dist/client.d.ts
ADDED
package/dist/client.js
ADDED
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot/client — widget React minimal (bulle + panneau). L'app fournit `send(text)`
|
|
3
|
+
* (qui appelle son endpoint serveur → createChatbot). Styles : classes `cbot-*` côté hôte.
|
|
4
|
+
* (0.0.1 : non-streaming. Streaming SSE en 0.3.)
|
|
5
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
6
|
+
*/
|
|
7
|
+
"use client";
|
|
8
|
+
import { jsx as _jsx, jsxs as _jsxs } from "react/jsx-runtime";
|
|
9
|
+
import { useState } from "react";
|
|
10
|
+
export function Chatbot({ send, title = "Assistant", greeting = "Bonjour 👋" }) {
|
|
11
|
+
const [open, setOpen] = useState(false);
|
|
12
|
+
const [msgs, setMsgs] = useState([{ role: "assistant", content: greeting }]);
|
|
13
|
+
const [val, setVal] = useState("");
|
|
14
|
+
const [busy, setBusy] = useState(false);
|
|
15
|
+
const submit = async (e) => {
|
|
16
|
+
e.preventDefault();
|
|
17
|
+
const t = val.trim();
|
|
18
|
+
if (!t || busy)
|
|
19
|
+
return;
|
|
20
|
+
setVal("");
|
|
21
|
+
setMsgs((m) => [...m, { role: "user", content: t }]);
|
|
22
|
+
setBusy(true);
|
|
23
|
+
try {
|
|
24
|
+
const r = await send(t);
|
|
25
|
+
setMsgs((m) => [...m, { role: "assistant", content: r }]);
|
|
26
|
+
}
|
|
27
|
+
catch {
|
|
28
|
+
setMsgs((m) => [...m, { role: "assistant", content: "Erreur de l'assistant." }]);
|
|
29
|
+
}
|
|
30
|
+
setBusy(false);
|
|
31
|
+
};
|
|
32
|
+
return (_jsxs("div", { className: "cbot", children: [open ? (_jsxs("div", { className: "cbot-panel", role: "dialog", children: [_jsxs("div", { className: "cbot-head", children: [_jsx("span", { children: title }), _jsx("button", { className: "cbot-x", onClick: () => setOpen(false), "aria-label": "Fermer", children: "\u00D7" })] }), _jsxs("div", { className: "cbot-msgs", children: [msgs.map((m, i) => _jsx("div", { className: `cbot-msg ${m.role}`, children: m.content }, i)), busy ? _jsx("div", { className: "cbot-msg assistant cbot-typing", children: "\u2026" }) : null] }), _jsxs("form", { className: "cbot-form", onSubmit: submit, children: [_jsx("input", { value: val, onChange: (e) => setVal(e.target.value), placeholder: "Votre question\u2026" }), _jsx("button", { className: "cbot-send", type: "submit", disabled: busy, children: "\u27A4" })] })] })) : null, _jsx("button", { className: "cbot-fab", onClick: () => setOpen((v) => !v), "aria-label": title, children: "\uD83E\uDD16" })] }));
|
|
33
|
+
}
|
|
34
|
+
//# sourceMappingURL=client.js.map
|
package/dist/config.d.ts
ADDED
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
import type { ChatbotMode } from "./types.js";
|
|
2
|
+
/** Interrupteur MAÎTRE — désactivé par défaut (opt-in explicite). */
|
|
3
|
+
export declare function chatbotEnabled(): boolean;
|
|
4
|
+
/** Mode : `assistant` (Q&A/copilote) ou `agent` (boucle but→outils→action). */
|
|
5
|
+
export declare function chatbotMode(): ChatbotMode;
|
|
6
|
+
/** Backend de réponse : `scripted` (défaut, sans IA) ou `ai` (LLM). */
|
|
7
|
+
export declare function chatbotBackend(): string;
|
|
8
|
+
/**
|
|
9
|
+
* Providers LLM actifs (backend `ai`), en **ordre de priorité / fallback**.
|
|
10
|
+
* `CHATBOT_PROVIDERS=deepseek,anthropic,…` (liste) ; sinon `CHATBOT_PROVIDER` (single) ; défaut `anthropic`.
|
|
11
|
+
*/
|
|
12
|
+
export declare function chatbotProviders(): string[];
|
|
13
|
+
/** Premier provider (compat). */
|
|
14
|
+
export declare function chatbotProvider(): string;
|
|
15
|
+
/** Modèle LLM (jamais en dur — §10). Vide ⇒ défaut spécifique au provider (anthropic→claude-opus-4-8, deepseek→deepseek-chat). */
|
|
16
|
+
export declare function chatbotModel(): string;
|
|
17
|
+
/** Recherche web (outils serveur `web_search`/`web_fetch`). */
|
|
18
|
+
export declare function chatbotWebSearch(): boolean;
|
|
19
|
+
export declare function chatbotEffort(): string;
|
|
20
|
+
export declare function chatbotMaxTokens(): number;
|
|
21
|
+
/** Rôles autorisés (vide = tous les utilisateurs authentifiés). */
|
|
22
|
+
export declare function chatbotAllowedRoles(): string[];
|
|
23
|
+
/** Règles impératives supplémentaires (ops) injectées dans le prompt système. `CHATBOT_RULES=r1|r2|…`. */
|
|
24
|
+
export declare function chatbotRules(): string[];
|
|
25
|
+
/**
|
|
26
|
+
* Backend de **repli** du routeur hybride quand aucun intent déterministe ne matche.
|
|
27
|
+
* `CHATBOT_ROUTER_FALLBACK=ai|scripted|none` (défaut `ai`). `none` ⇒ message d'aide, aucun appel IA.
|
|
28
|
+
*/
|
|
29
|
+
export declare function chatbotRouterFallback(): string;
|
|
30
|
+
/** L'acteur a-t-il le droit d'utiliser l'assistant ? (maître + rôles) */
|
|
31
|
+
export declare function chatbotAllowsRole(role: string): boolean;
|
|
32
|
+
//# sourceMappingURL=config.d.ts.map
|
package/dist/config.js
ADDED
|
@@ -0,0 +1,49 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot — configuration `.env` (@mostajs/config, cascade MOSTA_ENV).
|
|
3
|
+
* Interrupteur maître + mode + backend + provider + modèle + recherche web + quotas/rôles.
|
|
4
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
import { getEnv } from "@mostajs/config";
|
|
7
|
+
/** Interrupteur MAÎTRE — désactivé par défaut (opt-in explicite). */
|
|
8
|
+
export function chatbotEnabled() { return getEnv("CHATBOT_ENABLED", "0") === "1"; }
|
|
9
|
+
/** Mode : `assistant` (Q&A/copilote) ou `agent` (boucle but→outils→action). */
|
|
10
|
+
export function chatbotMode() { return getEnv("CHATBOT_MODE", "assistant") === "agent" ? "agent" : "assistant"; }
|
|
11
|
+
/** Backend de réponse : `scripted` (défaut, sans IA) ou `ai` (LLM). */
|
|
12
|
+
export function chatbotBackend() { return getEnv("CHATBOT_BACKEND", "scripted"); }
|
|
13
|
+
/**
|
|
14
|
+
* Providers LLM actifs (backend `ai`), en **ordre de priorité / fallback**.
|
|
15
|
+
* `CHATBOT_PROVIDERS=deepseek,anthropic,…` (liste) ; sinon `CHATBOT_PROVIDER` (single) ; défaut `anthropic`.
|
|
16
|
+
*/
|
|
17
|
+
export function chatbotProviders() {
|
|
18
|
+
const raw = getEnv("CHATBOT_PROVIDERS", "") || getEnv("CHATBOT_PROVIDER", "anthropic");
|
|
19
|
+
return raw.split(",").map((s) => s.trim().toLowerCase()).filter(Boolean);
|
|
20
|
+
}
|
|
21
|
+
/** Premier provider (compat). */
|
|
22
|
+
export function chatbotProvider() { return chatbotProviders()[0] ?? "anthropic"; }
|
|
23
|
+
/** Modèle LLM (jamais en dur — §10). Vide ⇒ défaut spécifique au provider (anthropic→claude-opus-4-8, deepseek→deepseek-chat). */
|
|
24
|
+
export function chatbotModel() { return getEnv("CHATBOT_MODEL", ""); }
|
|
25
|
+
/** Recherche web (outils serveur `web_search`/`web_fetch`). */
|
|
26
|
+
export function chatbotWebSearch() { return getEnv("CHATBOT_WEB_SEARCH", "0") === "1"; }
|
|
27
|
+
export function chatbotEffort() { return getEnv("CHATBOT_EFFORT", "high"); }
|
|
28
|
+
export function chatbotMaxTokens() { return Number(getEnv("CHATBOT_MAX_TOKENS", "4096")) || 4096; }
|
|
29
|
+
/** Rôles autorisés (vide = tous les utilisateurs authentifiés). */
|
|
30
|
+
export function chatbotAllowedRoles() {
|
|
31
|
+
return getEnv("CHATBOT_ROLES", "").split(",").map((s) => s.trim()).filter(Boolean);
|
|
32
|
+
}
|
|
33
|
+
/** Règles impératives supplémentaires (ops) injectées dans le prompt système. `CHATBOT_RULES=r1|r2|…`. */
|
|
34
|
+
export function chatbotRules() {
|
|
35
|
+
return getEnv("CHATBOT_RULES", "").split("|").map((s) => s.trim()).filter(Boolean);
|
|
36
|
+
}
|
|
37
|
+
/**
|
|
38
|
+
* Backend de **repli** du routeur hybride quand aucun intent déterministe ne matche.
|
|
39
|
+
* `CHATBOT_ROUTER_FALLBACK=ai|scripted|none` (défaut `ai`). `none` ⇒ message d'aide, aucun appel IA.
|
|
40
|
+
*/
|
|
41
|
+
export function chatbotRouterFallback() { return getEnv("CHATBOT_ROUTER_FALLBACK", "ai"); }
|
|
42
|
+
/** L'acteur a-t-il le droit d'utiliser l'assistant ? (maître + rôles) */
|
|
43
|
+
export function chatbotAllowsRole(role) {
|
|
44
|
+
if (!chatbotEnabled())
|
|
45
|
+
return false;
|
|
46
|
+
const allowed = chatbotAllowedRoles();
|
|
47
|
+
return allowed.length === 0 || allowed.includes(role);
|
|
48
|
+
}
|
|
49
|
+
//# sourceMappingURL=config.js.map
|
package/dist/index.d.ts
ADDED
|
@@ -0,0 +1,36 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot — point d'entrée serveur-safe. Assistant/copilote/agent générique, réutilisable
|
|
3
|
+
* par toute app `@mostajs/*` : l'app fournit son **profil métier** + ses **outils** (+ **intents**) ;
|
|
4
|
+
* le module route vers le backend configuré (`scripted` | `ai` | `router`). Activable/désactivable (`.env`).
|
|
5
|
+
*
|
|
6
|
+
* Architecture en couches (façon ORM) :
|
|
7
|
+
* @mostajs/llm — dialectes (1 par IA) + registre + types function-calling
|
|
8
|
+
* @mostajs/btool-use — boucle tool-use (runToolLoop), compose llm
|
|
9
|
+
* @mostajs/intent-router — routeur déterministe intents→outils, compose llm
|
|
10
|
+
* @mostajs/chatbot — backends (scripted/ai/router) + createChatbot + widget ; compose les 3 ci-dessus
|
|
11
|
+
*
|
|
12
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
13
|
+
*/
|
|
14
|
+
import "@mostajs/llm";
|
|
15
|
+
import "./backends/scripted.js";
|
|
16
|
+
import "./backends/ai.js";
|
|
17
|
+
import "./backends/router.js";
|
|
18
|
+
import type { ChatChunk, ChatMessage, ChatActor, DomainProfile, ChatTool, ChatIntent } from "./types.js";
|
|
19
|
+
export interface ChatbotOptions {
|
|
20
|
+
domain: DomainProfile;
|
|
21
|
+
tools?: ChatTool[];
|
|
22
|
+
intents?: ChatIntent[];
|
|
23
|
+
}
|
|
24
|
+
export declare function createChatbot(options: ChatbotOptions): {
|
|
25
|
+
enabled: () => boolean;
|
|
26
|
+
stream: (messages: ChatMessage[], actor: ChatActor) => AsyncIterable<ChatChunk>;
|
|
27
|
+
ask: (messages: ChatMessage[], actor: ChatActor) => Promise<string>;
|
|
28
|
+
};
|
|
29
|
+
export type { ChatbotOptions as Options };
|
|
30
|
+
export type { AskContext, ChatChunk, ChatMessage, ChatActor, DomainProfile, ChatTool, ChatIntent, IChatBackend, ILlmProvider, ILlmDialect, ToolCall, LlmMessage, LlmTurn, ChatbotMode, LlmStreamOpts, } from "./types.js";
|
|
31
|
+
export { registerBackend, getBackend, listBackends, registerTool, getTool, listTools } from "./registry.js";
|
|
32
|
+
export { registerLlmProvider, getLlmProvider, listLlmProviders, registerDialect, getDialect, listDialects, getSupportedDialects, AbstractLlmDialect, OpenAiDialect, DeepSeekDialect, MistralDialect, GroqDialect, OllamaDialect, AnthropicDialect, } from "@mostajs/llm";
|
|
33
|
+
export { runToolLoop } from "@mostajs/btool-use";
|
|
34
|
+
export { matchIntent, runIntent } from "@mostajs/intent-router";
|
|
35
|
+
export { chatbotEnabled, chatbotMode, chatbotBackend, chatbotProvider, chatbotProviders, chatbotModel, chatbotWebSearch, chatbotEffort, chatbotMaxTokens, chatbotAllowedRoles, chatbotAllowsRole, chatbotRouterFallback, } from "./config.js";
|
|
36
|
+
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.js
ADDED
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot — point d'entrée serveur-safe. Assistant/copilote/agent générique, réutilisable
|
|
3
|
+
* par toute app `@mostajs/*` : l'app fournit son **profil métier** + ses **outils** (+ **intents**) ;
|
|
4
|
+
* le module route vers le backend configuré (`scripted` | `ai` | `router`). Activable/désactivable (`.env`).
|
|
5
|
+
*
|
|
6
|
+
* Architecture en couches (façon ORM) :
|
|
7
|
+
* @mostajs/llm — dialectes (1 par IA) + registre + types function-calling
|
|
8
|
+
* @mostajs/btool-use — boucle tool-use (runToolLoop), compose llm
|
|
9
|
+
* @mostajs/intent-router — routeur déterministe intents→outils, compose llm
|
|
10
|
+
* @mostajs/chatbot — backends (scripted/ai/router) + createChatbot + widget ; compose les 3 ci-dessus
|
|
11
|
+
*
|
|
12
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
13
|
+
*/
|
|
14
|
+
import "@mostajs/llm"; // enregistre les dialectes LLM (deepseek, anthropic, openai, mistral, groq, ollama)
|
|
15
|
+
import "./backends/scripted.js"; // backends built-in (effet de bord)
|
|
16
|
+
import "./backends/ai.js";
|
|
17
|
+
import "./backends/router.js";
|
|
18
|
+
import { getBackend } from "./registry.js";
|
|
19
|
+
import { chatbotEnabled, chatbotBackend, chatbotAllowsRole, chatbotMode } from "./config.js";
|
|
20
|
+
export function createChatbot(options) {
|
|
21
|
+
async function* stream(messages, actor) {
|
|
22
|
+
if (!chatbotAllowsRole(actor.role)) {
|
|
23
|
+
yield { type: "error", text: "Assistant désactivé pour votre profil." };
|
|
24
|
+
return;
|
|
25
|
+
}
|
|
26
|
+
const backend = getBackend(chatbotBackend()) ?? getBackend("scripted");
|
|
27
|
+
const ctx = { messages, actor, domain: options.domain, tools: options.tools, intents: options.intents, mode: chatbotMode() };
|
|
28
|
+
yield* backend.reply(ctx);
|
|
29
|
+
}
|
|
30
|
+
async function ask(messages, actor) {
|
|
31
|
+
let out = "";
|
|
32
|
+
for await (const c of stream(messages, actor))
|
|
33
|
+
if (c.type === "text" && c.text)
|
|
34
|
+
out += c.text;
|
|
35
|
+
return out;
|
|
36
|
+
}
|
|
37
|
+
return { enabled: () => chatbotEnabled(), stream, ask };
|
|
38
|
+
}
|
|
39
|
+
export { registerBackend, getBackend, listBackends, registerTool, getTool, listTools } from "./registry.js";
|
|
40
|
+
// Réexports de la couche LLM (compat : ces symboles vivaient dans @mostajs/chatbot avant l'extraction).
|
|
41
|
+
export { registerLlmProvider, getLlmProvider, listLlmProviders, registerDialect, getDialect, listDialects, getSupportedDialects, AbstractLlmDialect, OpenAiDialect, DeepSeekDialect, MistralDialect, GroqDialect, OllamaDialect, AnthropicDialect, } from "@mostajs/llm";
|
|
42
|
+
export { runToolLoop } from "@mostajs/btool-use";
|
|
43
|
+
export { matchIntent, runIntent } from "@mostajs/intent-router";
|
|
44
|
+
export { chatbotEnabled, chatbotMode, chatbotBackend, chatbotProvider, chatbotProviders, chatbotModel, chatbotWebSearch, chatbotEffort, chatbotMaxTokens, chatbotAllowedRoles, chatbotAllowsRole, chatbotRouterFallback, } from "./config.js";
|
|
45
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1,14 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot — registres de la couche conversation : **backends** de réponse + **outils** métier.
|
|
3
|
+
* Le registre des **dialectes/providers LLM** vit dans `@mostajs/llm` (réexporté par l'index pour compat).
|
|
4
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
import type { IChatBackend, ChatTool } from "./types.js";
|
|
7
|
+
export declare function registerBackend(b: IChatBackend): void;
|
|
8
|
+
export declare function getBackend(key: string): IChatBackend | null;
|
|
9
|
+
export declare function listBackends(): IChatBackend[];
|
|
10
|
+
/** Outils métier : l'app enregistre les siens (lecture seule par défaut). */
|
|
11
|
+
export declare function registerTool(t: ChatTool): void;
|
|
12
|
+
export declare function listTools(): ChatTool[];
|
|
13
|
+
export declare function getTool(name: string): ChatTool | null;
|
|
14
|
+
//# sourceMappingURL=registry.d.ts.map
|
package/dist/registry.js
ADDED
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
const BACKENDS = new Map();
|
|
2
|
+
const TOOLS = new Map();
|
|
3
|
+
export function registerBackend(b) { BACKENDS.set(b.key, b); }
|
|
4
|
+
export function getBackend(key) { return BACKENDS.get(key) ?? null; }
|
|
5
|
+
export function listBackends() { return Array.from(BACKENDS.values()); }
|
|
6
|
+
/** Outils métier : l'app enregistre les siens (lecture seule par défaut). */
|
|
7
|
+
export function registerTool(t) { TOOLS.set(t.name, t); }
|
|
8
|
+
export function listTools() { return Array.from(TOOLS.values()); }
|
|
9
|
+
export function getTool(name) { return TOOLS.get(name) ?? null; }
|
|
10
|
+
//# sourceMappingURL=registry.js.map
|
package/dist/server.d.ts
ADDED
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot/server — backends/providers concrets (utile pour enregistrer des extensions
|
|
3
|
+
* ou composer côté serveur). Réexporte l'API publique.
|
|
4
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
export * from "./index.js";
|
|
7
|
+
export { scriptedBackend } from "./backends/scripted.js";
|
|
8
|
+
export { aiBackend } from "./backends/ai.js";
|
|
9
|
+
export { routerBackend } from "./backends/router.js";
|
|
10
|
+
export { AnthropicDialect, AnthropicDialect as anthropicProvider } from "@mostajs/llm";
|
|
11
|
+
//# sourceMappingURL=server.d.ts.map
|
package/dist/server.js
ADDED
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot/server — backends/providers concrets (utile pour enregistrer des extensions
|
|
3
|
+
* ou composer côté serveur). Réexporte l'API publique.
|
|
4
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
5
|
+
*/
|
|
6
|
+
export * from "./index.js";
|
|
7
|
+
export { scriptedBackend } from "./backends/scripted.js";
|
|
8
|
+
export { aiBackend } from "./backends/ai.js";
|
|
9
|
+
export { routerBackend } from "./backends/router.js";
|
|
10
|
+
// Le dialecte Anthropic vit désormais dans @mostajs/llm (réexporté ici pour compat).
|
|
11
|
+
export { AnthropicDialect, AnthropicDialect as anthropicProvider } from "@mostajs/llm";
|
|
12
|
+
//# sourceMappingURL=server.js.map
|
package/dist/types.d.ts
ADDED
|
@@ -0,0 +1,51 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* @mostajs/chatbot — types de la couche **conversation**. La couche **modèle** (dialectes, tool-use,
|
|
3
|
+
* function-calling) vit dans `@mostajs/llm` (composé) ; les intents déterministes dans
|
|
4
|
+
* `@mostajs/intent-router`. On réexporte ces types sous les **noms historiques** du chatbot (compat).
|
|
5
|
+
* @author Dr Hamid MADANI <drmdh@msn.com> · AGPL-3.0-or-later
|
|
6
|
+
*/
|
|
7
|
+
import type { LlmActor, LlmTool, LlmChunk } from "@mostajs/llm";
|
|
8
|
+
import type { Intent } from "@mostajs/intent-router";
|
|
9
|
+
export type ChatbotMode = "assistant" | "agent";
|
|
10
|
+
export type ChatRole = "user" | "assistant" | "system";
|
|
11
|
+
export interface ChatMessage {
|
|
12
|
+
role: ChatRole;
|
|
13
|
+
content: string;
|
|
14
|
+
}
|
|
15
|
+
/** Acteur — alias de `LlmActor`. */
|
|
16
|
+
export type ChatActor = LlmActor;
|
|
17
|
+
/** Outil métier (tool-use) — alias de `LlmTool`. */
|
|
18
|
+
export type ChatTool = LlmTool;
|
|
19
|
+
/** Fragment streamé — alias de `LlmChunk`. */
|
|
20
|
+
export type ChatChunk = LlmChunk;
|
|
21
|
+
/** Intent déterministe (routeur hybride) — alias de `Intent` (@mostajs/intent-router). */
|
|
22
|
+
export type ChatIntent = Intent;
|
|
23
|
+
export type { LlmActor, LlmTool, LlmChunk, LlmMessage, LlmStreamOpts, LlmTurn, ToolCall, ILlmProvider, ILlmDialect } from "@mostajs/llm";
|
|
24
|
+
/** Profil métier injecté par l'app hôte — rend le module réutilisable partout. */
|
|
25
|
+
export interface DomainProfile {
|
|
26
|
+
appName: string;
|
|
27
|
+
description: string;
|
|
28
|
+
locale?: string;
|
|
29
|
+
/**
|
|
30
|
+
* Règles de conduite **impératives** du copilote (gouvernance, à la DEVRULES) — injectées dans le
|
|
31
|
+
* prompt système comme contraintes à respecter (ex. « demander confirmation avant toute action »,
|
|
32
|
+
* « ancrer chaque offre sur une source », « ne jamais exposer de secret »). Cumulées avec `CHATBOT_RULES`.
|
|
33
|
+
*/
|
|
34
|
+
rules?: string[];
|
|
35
|
+
}
|
|
36
|
+
/** Contexte d'une requête à l'assistant. */
|
|
37
|
+
export interface AskContext {
|
|
38
|
+
messages: ChatMessage[];
|
|
39
|
+
actor: ChatActor;
|
|
40
|
+
domain: DomainProfile;
|
|
41
|
+
tools?: ChatTool[];
|
|
42
|
+
/** Intents déterministes (routeur hybride) — fournis par l'app. */
|
|
43
|
+
intents?: ChatIntent[];
|
|
44
|
+
mode?: ChatbotMode;
|
|
45
|
+
}
|
|
46
|
+
/** Backend de réponse : `scripted` (règles/FAQ), `ai` (LLM + tool-use) ou `router` (hybride). */
|
|
47
|
+
export interface IChatBackend {
|
|
48
|
+
key: string;
|
|
49
|
+
reply(ctx: AskContext): AsyncIterable<ChatChunk>;
|
|
50
|
+
}
|
|
51
|
+
//# sourceMappingURL=types.d.ts.map
|
package/dist/types.js
ADDED
package/llms.txt
ADDED
|
@@ -0,0 +1,59 @@
|
|
|
1
|
+
# @mostajs/chatbot — fiche LLM
|
|
2
|
+
> Assistant / copilote / agent conversationnel GÉNÉRIQUE et réutilisable pour apps @mostajs/*. Activable/désactivable et **entièrement piloté par `.env`**. Backends pluggables (scripted | ai) ; providers LLM pluggables (anthropic/claude-opus-4-8, deepseek) avec **fallback** ; ancrage tool-use ; recherche web optionnelle. Registres façon dialectes ORM.
|
|
3
|
+
|
|
4
|
+
- Version: 0.0.1 (scaffold) · Licence: AGPL-3.0-or-later · Auteur: Dr Hamid MADANI <drmdh@msn.com>
|
|
5
|
+
- Chemin: mostajs/mosta-chatbot · Livrables #1-#3: `docs/01..03` + `docs/scripts-recherche-chatbot-08062026.sh`
|
|
6
|
+
|
|
7
|
+
## RÔLE
|
|
8
|
+
L'app hôte fournit son **profil métier** (`DomainProfile`) + ses **outils** (`ChatTool`, lecture seule par
|
|
9
|
+
défaut, RBAC) ; le module route vers le **backend** configuré et, en mode `ai`, vers un **provider LLM**
|
|
10
|
+
(avec fallback). Aucune logique d'une app particulière dans le module (réutilisable partout : CRM, santé, …).
|
|
11
|
+
|
|
12
|
+
## EXPORTS
|
|
13
|
+
- `.` (serveur-safe) : `createChatbot({domain,tools})` → `{enabled(), stream(messages,actor), ask(messages,actor)}` ;
|
|
14
|
+
`registerBackend/getBackend/listBackends`, `registerLlmProvider/getLlmProvider/listLlmProviders`,
|
|
15
|
+
`registerTool/getTool/listTools` ; toute la config (`chatbot*`) ; types.
|
|
16
|
+
- `./server` : built-ins concrets (`scriptedBackend`, `aiBackend`, `anthropicProvider`, `deepseekProvider`).
|
|
17
|
+
- `./client` (React) : `<Chatbot send=… title? greeting? />` (widget bulle+panneau ; SSE en 0.3).
|
|
18
|
+
|
|
19
|
+
## CONFIG (.env — @mostajs/config, cascade MOSTA_ENV) — TOUT par .env
|
|
20
|
+
- `CHATBOT_ENABLED=0|1` (défaut 0, opt-in) · `CHATBOT_ROLES=admin,employe` (vide=tous) · `CHATBOT_MODE=assistant|agent`
|
|
21
|
+
- `CHATBOT_BACKEND=scripted|ai` (défaut scripted)
|
|
22
|
+
- `CHATBOT_PROVIDERS=deepseek,anthropic` (liste priorité+fallback ; `CHATBOT_PROVIDER` = single, compat)
|
|
23
|
+
- `CHATBOT_MODEL=` (vide ⇒ défaut provider : anthropic→claude-opus-4-8, deepseek→deepseek-chat)
|
|
24
|
+
- Anthropic : `ANTHROPIC_API_KEY` · `CHATBOT_WEB_SEARCH=0|1` · `CHATBOT_EFFORT=low|medium|high|xhigh|max`
|
|
25
|
+
- DeepSeek : `DEEPSEEK_API_KEY` · `DEEPSEEK_BASE_URL=https://api.deepseek.com` (compatible OpenAI)
|
|
26
|
+
- `CHATBOT_MAX_TOKENS` · `CHATBOT_QUOTA_PER_ROLE` (à venir)
|
|
27
|
+
|
|
28
|
+
## PROVIDERS LLM (registre — ajouter = un fichier + register)
|
|
29
|
+
- `anthropic` : `@anthropic-ai/sdk`, modèle `claude-opus-4-8`, `thinking:{type:"adaptive"}`, streaming, outils
|
|
30
|
+
serveur `web_search`/`web_fetch` si `CHATBOT_WEB_SEARCH=1`.
|
|
31
|
+
- `deepseek` : endpoint `/chat/completions` (OpenAI-compatible), SSE en `fetch`, modèles `deepseek-chat` /
|
|
32
|
+
`deepseek-v4-flash` / `deepseek-v4-pro`. Sans dépendance.
|
|
33
|
+
- Extension : `registerLlmProvider({ key:"openai", stream(messages,tools,opts){…} })`.
|
|
34
|
+
|
|
35
|
+
## TYPES CLÉS
|
|
36
|
+
DomainProfile { appName, description, locale? } · ChatTool { name, description, schema, permission?, run(input,actor) }
|
|
37
|
+
ChatActor { id, role, permissions? } · ChatChunk { type:'text'|'tool'|'done'|'error', text?, tool? }
|
|
38
|
+
IChatBackend { key, reply(ctx): AsyncIterable<ChatChunk> } · ILlmProvider { key, stream(msgs,tools,opts) }
|
|
39
|
+
|
|
40
|
+
## PATTERN (app Next.js)
|
|
41
|
+
```ts
|
|
42
|
+
// serveur
|
|
43
|
+
import { createChatbot, registerTool } from "@mostajs/chatbot";
|
|
44
|
+
registerTool({ name:"orders_by_state", description:"Compter les commandes par état", schema:{...},
|
|
45
|
+
permission:"order.read", run: async ({state}) => crm.repositories.orders.find(o=>o.state===state).length });
|
|
46
|
+
const bot = createChatbot({ domain:{ appName:"CRM TRADING", description:"Négoce: commandes/fournisseurs/proformats", locale:"fr" } });
|
|
47
|
+
if (bot.enabled()) for await (const c of bot.stream(messages, actor)) { /* stream */ }
|
|
48
|
+
```
|
|
49
|
+
|
|
50
|
+
## DÉPEND DE (peers)
|
|
51
|
+
- `@mostajs/config` (env). `@anthropic-ai/sdk` (optionnel — provider anthropic). `react` (optionnel — `./client`).
|
|
52
|
+
- DeepSeek : aucune dépendance (fetch). Composition prévue : `@mostajs/repository` (outils/historique),
|
|
53
|
+
`@mostajs/auth`/`rbac` (accès+outils), `@mostajs/audit` (traçabilité), `@mostajs/i18n`, `@mostajs/net` (SSE).
|
|
54
|
+
|
|
55
|
+
## PIÈGES
|
|
56
|
+
- `CHATBOT_ENABLED=0` par défaut : rien ne s'affiche/ne coûte tant qu'on n'a pas opt-in.
|
|
57
|
+
- `./client` est 'use client' : importer `createChatbot` (serveur) à part, passer un `send()` au widget.
|
|
58
|
+
- Multi-provider : laisser `CHATBOT_MODEL` vide pour que chaque provider applique son défaut.
|
|
59
|
+
- Backend `ai` envoie le contexte à un tiers (Anthropic/DeepSeek) : opt-in, pas de secret au prompt, audit, RGPD.
|
package/package.json
ADDED
|
@@ -0,0 +1,65 @@
|
|
|
1
|
+
{
|
|
2
|
+
"name": "@mostajs/chatbot",
|
|
3
|
+
"version": "0.2.0",
|
|
4
|
+
"description": "Assistant / copilote / agent conversationnel générique et réutilisable pour apps @mostajs/* — backends pluggables (scripted | ai), providers LLM pluggables (anthropic/claude-opus-4-8 par défaut), ancrage tool-use, recherche web optionnelle. Activable/désactivable par .env.",
|
|
5
|
+
"author": "Dr Hamid MADANI <drmdh@msn.com>",
|
|
6
|
+
"license": "AGPL-3.0-or-later",
|
|
7
|
+
"type": "module",
|
|
8
|
+
"main": "dist/index.js",
|
|
9
|
+
"types": "dist/index.d.ts",
|
|
10
|
+
"exports": {
|
|
11
|
+
".": {
|
|
12
|
+
"types": "./dist/index.d.ts",
|
|
13
|
+
"import": "./dist/index.js",
|
|
14
|
+
"default": "./dist/index.js"
|
|
15
|
+
},
|
|
16
|
+
"./server": {
|
|
17
|
+
"types": "./dist/server.d.ts",
|
|
18
|
+
"import": "./dist/server.js",
|
|
19
|
+
"default": "./dist/server.js"
|
|
20
|
+
},
|
|
21
|
+
"./client": {
|
|
22
|
+
"types": "./dist/client.d.ts",
|
|
23
|
+
"import": "./dist/client.js",
|
|
24
|
+
"default": "./dist/client.js"
|
|
25
|
+
}
|
|
26
|
+
},
|
|
27
|
+
"files": [
|
|
28
|
+
"dist/**/*.js",
|
|
29
|
+
"dist/**/*.d.ts",
|
|
30
|
+
"llms.txt",
|
|
31
|
+
"CHANGELOG.md",
|
|
32
|
+
"README.md",
|
|
33
|
+
"LICENSE"
|
|
34
|
+
],
|
|
35
|
+
"scripts": {
|
|
36
|
+
"build": "tsc -p tsconfig.json"
|
|
37
|
+
},
|
|
38
|
+
"peerDependencies": {
|
|
39
|
+
"@mostajs/config": "*",
|
|
40
|
+
"@mostajs/llm": "*",
|
|
41
|
+
"@mostajs/btool-use": "*",
|
|
42
|
+
"@mostajs/intent-router": "*",
|
|
43
|
+
"react": ">=18"
|
|
44
|
+
},
|
|
45
|
+
"peerDependenciesMeta": {
|
|
46
|
+
"react": {
|
|
47
|
+
"optional": true
|
|
48
|
+
}
|
|
49
|
+
},
|
|
50
|
+
"devDependencies": {
|
|
51
|
+
"typescript": "^5.6.0",
|
|
52
|
+
"@types/react": "^19.0.0"
|
|
53
|
+
},
|
|
54
|
+
"keywords": [
|
|
55
|
+
"chatbot",
|
|
56
|
+
"assistant",
|
|
57
|
+
"copilot",
|
|
58
|
+
"agent",
|
|
59
|
+
"llm",
|
|
60
|
+
"anthropic",
|
|
61
|
+
"claude",
|
|
62
|
+
"tool-use",
|
|
63
|
+
"mostajs"
|
|
64
|
+
]
|
|
65
|
+
}
|