chatbotlite 0.4.0 → 0.5.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/README.md +35 -2
- package/dist/client/index.cjs +39 -6
- package/dist/client/index.cjs.map +1 -1
- package/dist/client/index.d.cts +5 -0
- package/dist/client/index.d.ts +5 -0
- package/dist/client/index.js +39 -6
- package/dist/client/index.js.map +1 -1
- package/dist/core/index.cjs +56 -54
- package/dist/core/index.cjs.map +1 -1
- package/dist/core/index.d.cts +5 -1
- package/dist/core/index.d.ts +5 -1
- package/dist/core/index.js +56 -54
- package/dist/core/index.js.map +1 -1
- package/dist/embed.global.js +101 -0
- package/dist/embed.global.js.map +1 -0
- package/dist/index.cjs +69 -57
- package/dist/index.cjs.map +1 -1
- package/dist/index.js +69 -57
- package/dist/index.js.map +1 -1
- package/dist/react/index.cjs +72 -39
- package/dist/react/index.cjs.map +1 -1
- package/dist/react/index.js +72 -39
- package/dist/react/index.js.map +1 -1
- package/package.json +3 -2
package/dist/react/index.js
CHANGED
|
@@ -3,8 +3,60 @@ import { jsxs, Fragment, jsx } from 'react/jsx-runtime';
|
|
|
3
3
|
|
|
4
4
|
// src/react/ChatWidget.tsx
|
|
5
5
|
|
|
6
|
+
// src/core/tools.ts
|
|
7
|
+
var MARKER_RE = /\[SKILL:(\w+)((?:\s+\w+=(?:"[^"]*"|[\w./@*+,:-]+))*)\s*\]/g;
|
|
8
|
+
var ARG_RE = /(\w+)=("([^"]*)"|([\w./@*+,:-]+))/g;
|
|
9
|
+
function coerce(value) {
|
|
10
|
+
if (value === "true") return true;
|
|
11
|
+
if (value === "false") return false;
|
|
12
|
+
if (/^-?\d+(?:\.\d+)?$/.test(value)) return Number(value);
|
|
13
|
+
return value;
|
|
14
|
+
}
|
|
15
|
+
function parseToolMarkers(text) {
|
|
16
|
+
const markers = [];
|
|
17
|
+
let m;
|
|
18
|
+
MARKER_RE.lastIndex = 0;
|
|
19
|
+
while ((m = MARKER_RE.exec(text)) !== null) {
|
|
20
|
+
const name = m[1];
|
|
21
|
+
const argsRaw = m[2] ?? "";
|
|
22
|
+
const args = {};
|
|
23
|
+
let a;
|
|
24
|
+
ARG_RE.lastIndex = 0;
|
|
25
|
+
while ((a = ARG_RE.exec(argsRaw)) !== null) {
|
|
26
|
+
const key = a[1];
|
|
27
|
+
const value = a[3] ?? a[4] ?? "";
|
|
28
|
+
args[key] = coerce(value);
|
|
29
|
+
}
|
|
30
|
+
markers.push({ name, args, raw: m[0] });
|
|
31
|
+
}
|
|
32
|
+
return markers;
|
|
33
|
+
}
|
|
34
|
+
function stripToolMarkers(text) {
|
|
35
|
+
return text.replace(MARKER_RE, "").replace(/\s+\n/g, "\n").trim();
|
|
36
|
+
}
|
|
37
|
+
function buildToolsPromptAddendum(enabledTools) {
|
|
38
|
+
if (enabledTools.length === 0) return "";
|
|
39
|
+
const examples = {
|
|
40
|
+
uploadForReview: '[SKILL:uploadForReview purpose="T4 slip" accept="image/*,application/pdf" maxMb=10] \u2014 collect a document for human review (bytes go to webhook, you never see content)',
|
|
41
|
+
scheduleCallback: '[SKILL:scheduleCallback durationMin=15 timezone="America/Vancouver"] \u2014 let the user pick a callback time slot',
|
|
42
|
+
requestPayment: '[SKILL:requestPayment amount=4250 currency="cad" reason="initial deposit"] \u2014 collect payment via inline card'
|
|
43
|
+
};
|
|
44
|
+
const lines = enabledTools.filter((t) => examples[t]).map((t) => `- ${examples[t]}`);
|
|
45
|
+
if (lines.length === 0) return "";
|
|
46
|
+
return [
|
|
47
|
+
"",
|
|
48
|
+
"## Available tools",
|
|
49
|
+
"When you need one of these workflows, emit the marker INLINE in your reply.",
|
|
50
|
+
"Write a short message first, THEN the marker. The marker will be replaced by an interactive card.",
|
|
51
|
+
"Pause the conversation after emitting \u2014 wait for the tool result before continuing.",
|
|
52
|
+
"",
|
|
53
|
+
...lines
|
|
54
|
+
].join("\n");
|
|
55
|
+
}
|
|
56
|
+
|
|
6
57
|
// src/core/prompts.ts
|
|
7
|
-
function buildSystemPrompt(knowledge) {
|
|
58
|
+
function buildSystemPrompt(knowledge, enabledTools = []) {
|
|
59
|
+
const toolsAddendum = buildToolsPromptAddendum(enabledTools);
|
|
8
60
|
return [
|
|
9
61
|
"You are an AI assistant on a business website. Use ONLY the knowledge below to answer.",
|
|
10
62
|
"",
|
|
@@ -17,8 +69,9 @@ function buildSystemPrompt(knowledge) {
|
|
|
17
69
|
"- For anything not covered in the knowledge above, say the owner will follow up \u2014 do NOT guess.",
|
|
18
70
|
'- If the caller is clearly a vendor/sales pitch, say: "This does not look like a customer service request, so we will not continue this thread."',
|
|
19
71
|
`- If wrong number or asked to stop, say: "Sorry about that. We won't text again."`,
|
|
20
|
-
"- Match the caller's language automatically."
|
|
21
|
-
|
|
72
|
+
"- Match the caller's language automatically.",
|
|
73
|
+
toolsAddendum
|
|
74
|
+
].filter(Boolean).join("\n");
|
|
22
75
|
}
|
|
23
76
|
|
|
24
77
|
// src/core/guards.ts
|
|
@@ -141,10 +194,12 @@ var ChatBot = class {
|
|
|
141
194
|
timeoutMs;
|
|
142
195
|
cachedSystemPrompt;
|
|
143
196
|
guards;
|
|
197
|
+
knowledge;
|
|
144
198
|
constructor(init) {
|
|
145
199
|
if (!init.knowledge || typeof init.knowledge !== "string" || init.knowledge.trim().length === 0) {
|
|
146
200
|
throw new Error("chatbotlite: knowledge is required (a non-empty markdown string).");
|
|
147
201
|
}
|
|
202
|
+
this.knowledge = init.knowledge;
|
|
148
203
|
this.keys = init.providers.keys ?? {};
|
|
149
204
|
this.steps = resolveChain(init.providers);
|
|
150
205
|
this.fetcher = init.options?.fetch ?? globalThis.fetch.bind(globalThis);
|
|
@@ -152,6 +207,14 @@ var ChatBot = class {
|
|
|
152
207
|
this.cachedSystemPrompt = buildSystemPrompt(init.knowledge);
|
|
153
208
|
this.guards = init.guards ?? {};
|
|
154
209
|
}
|
|
210
|
+
/** Build system prompt for given opts — uses cached if no enabledTools, else rebuilds. */
|
|
211
|
+
resolveSystemPrompt(opts) {
|
|
212
|
+
if (opts.systemPrompt) return opts.systemPrompt;
|
|
213
|
+
if (opts.enabledTools && opts.enabledTools.length > 0) {
|
|
214
|
+
return buildSystemPrompt(this.knowledge, opts.enabledTools);
|
|
215
|
+
}
|
|
216
|
+
return this.cachedSystemPrompt;
|
|
217
|
+
}
|
|
155
218
|
/** Run an LLM judge against content. Fail-open on errors. */
|
|
156
219
|
async judge(config, content) {
|
|
157
220
|
const endpoint = PROVIDER_ENDPOINTS[config.provider];
|
|
@@ -188,7 +251,7 @@ var ChatBot = class {
|
|
|
188
251
|
* event: error data: {"message":"...","attempts":[...]}
|
|
189
252
|
*/
|
|
190
253
|
async replyStream(message, opts = {}) {
|
|
191
|
-
const systemPrompt =
|
|
254
|
+
const systemPrompt = this.resolveSystemPrompt(opts);
|
|
192
255
|
const messages = [
|
|
193
256
|
{ role: "system", content: systemPrompt },
|
|
194
257
|
...opts.history ?? [],
|
|
@@ -301,7 +364,7 @@ data: ${data}
|
|
|
301
364
|
return this.reply(message, opts);
|
|
302
365
|
}
|
|
303
366
|
const dataUrls = await Promise.all(images.map(fileToDataUrl));
|
|
304
|
-
const systemPrompt =
|
|
367
|
+
const systemPrompt = this.resolveSystemPrompt(opts);
|
|
305
368
|
const userContent = [];
|
|
306
369
|
if (message) userContent.push({ type: "text", text: message });
|
|
307
370
|
for (const url of dataUrls) userContent.push({ type: "image_url", image_url: { url } });
|
|
@@ -381,7 +444,7 @@ data: ${data}
|
|
|
381
444
|
};
|
|
382
445
|
}
|
|
383
446
|
}
|
|
384
|
-
const systemPrompt =
|
|
447
|
+
const systemPrompt = this.resolveSystemPrompt(opts);
|
|
385
448
|
const messages = [
|
|
386
449
|
{ role: "system", content: systemPrompt },
|
|
387
450
|
...opts.history ?? [],
|
|
@@ -494,38 +557,6 @@ function normalizeChainEntry(entry, keys) {
|
|
|
494
557
|
}
|
|
495
558
|
return { provider, model, label: `${provider}/${model}` };
|
|
496
559
|
}
|
|
497
|
-
|
|
498
|
-
// src/core/tools.ts
|
|
499
|
-
var MARKER_RE = /\[SKILL:(\w+)((?:\s+\w+=(?:"[^"]*"|[\w./@*+,:-]+))*)\s*\]/g;
|
|
500
|
-
var ARG_RE = /(\w+)=("([^"]*)"|([\w./@*+,:-]+))/g;
|
|
501
|
-
function coerce(value) {
|
|
502
|
-
if (value === "true") return true;
|
|
503
|
-
if (value === "false") return false;
|
|
504
|
-
if (/^-?\d+(?:\.\d+)?$/.test(value)) return Number(value);
|
|
505
|
-
return value;
|
|
506
|
-
}
|
|
507
|
-
function parseToolMarkers(text) {
|
|
508
|
-
const markers = [];
|
|
509
|
-
let m;
|
|
510
|
-
MARKER_RE.lastIndex = 0;
|
|
511
|
-
while ((m = MARKER_RE.exec(text)) !== null) {
|
|
512
|
-
const name = m[1];
|
|
513
|
-
const argsRaw = m[2] ?? "";
|
|
514
|
-
const args = {};
|
|
515
|
-
let a;
|
|
516
|
-
ARG_RE.lastIndex = 0;
|
|
517
|
-
while ((a = ARG_RE.exec(argsRaw)) !== null) {
|
|
518
|
-
const key = a[1];
|
|
519
|
-
const value = a[3] ?? a[4] ?? "";
|
|
520
|
-
args[key] = coerce(value);
|
|
521
|
-
}
|
|
522
|
-
markers.push({ name, args, raw: m[0] });
|
|
523
|
-
}
|
|
524
|
-
return markers;
|
|
525
|
-
}
|
|
526
|
-
function stripToolMarkers(text) {
|
|
527
|
-
return text.replace(MARKER_RE, "").replace(/\s+\n/g, "\n").trim();
|
|
528
|
-
}
|
|
529
560
|
var CLOUD_ICON = "M7 16a4 4 0 01-.88-7.903A5 5 0 1115.9 6L16 6a5 5 0 011 9.9M15 13l-3-3m0 0l-3 3m3-3v12";
|
|
530
561
|
function UploadForReview(props) {
|
|
531
562
|
const {
|
|
@@ -1156,17 +1187,19 @@ function ChatWidget(props) {
|
|
|
1156
1187
|
return void 0;
|
|
1157
1188
|
}, [open]);
|
|
1158
1189
|
async function fetchReplyFromEndpoint(text, history, attachedFiles, onToken) {
|
|
1190
|
+
const enabledTools = Object.keys(tools);
|
|
1159
1191
|
let body;
|
|
1160
1192
|
const headers = { Accept: "text/event-stream, application/json" };
|
|
1161
1193
|
if (attachedFiles.length > 0) {
|
|
1162
1194
|
const form = new FormData();
|
|
1163
1195
|
form.append("message", text);
|
|
1164
1196
|
form.append("transcript", JSON.stringify(history));
|
|
1197
|
+
form.append("enabledTools", JSON.stringify(enabledTools));
|
|
1165
1198
|
for (const f of attachedFiles) form.append("attachments", f, f.name);
|
|
1166
1199
|
body = form;
|
|
1167
1200
|
} else {
|
|
1168
1201
|
headers["Content-Type"] = "application/json";
|
|
1169
|
-
body = JSON.stringify({ message: text, transcript: history });
|
|
1202
|
+
body = JSON.stringify({ message: text, transcript: history, enabledTools });
|
|
1170
1203
|
}
|
|
1171
1204
|
const res = await fetch(props.endpoint, { method: "POST", headers, body });
|
|
1172
1205
|
if (!res.ok) throw new Error(`Endpoint ${res.status}: ${await res.text().catch(() => "")}`);
|