@diegoaltoworks/chatter 1.0.0 → 2.1.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 +3 -1
- package/dist/channels/gates.d.ts +21 -2
- package/dist/channels/gates.d.ts.map +1 -1
- package/dist/channels/index.d.ts +5 -3
- package/dist/channels/index.d.ts.map +1 -1
- package/dist/channels/index.js +31 -13
- package/dist/channels/index.mjs +30 -13
- package/dist/channels/matrix/channel.d.ts +2 -0
- package/dist/channels/matrix/channel.d.ts.map +1 -1
- package/dist/channels/matrix/handler.d.ts +9 -0
- package/dist/channels/matrix/handler.d.ts.map +1 -1
- package/dist/channels/matrix/index.d.ts +1 -1
- package/dist/channels/matrix/index.d.ts.map +1 -1
- package/dist/channels/matrix/index.js +75 -20
- package/dist/channels/matrix/index.mjs +74 -20
- package/dist/channels/matrix/updates.d.ts +8 -2
- package/dist/channels/matrix/updates.d.ts.map +1 -1
- package/dist/channels/pipeline.d.ts +34 -4
- package/dist/channels/pipeline.d.ts.map +1 -1
- package/dist/channels/polling.d.ts +11 -0
- package/dist/channels/polling.d.ts.map +1 -1
- package/dist/channels/telegram/channel.d.ts +2 -0
- package/dist/channels/telegram/channel.d.ts.map +1 -1
- package/dist/channels/telegram/handler.d.ts +9 -0
- package/dist/channels/telegram/handler.d.ts.map +1 -1
- package/dist/channels/telegram/index.d.ts +1 -1
- package/dist/channels/telegram/index.d.ts.map +1 -1
- package/dist/channels/telegram/index.js +73 -24
- package/dist/channels/telegram/index.mjs +72 -24
- package/dist/channels/telegram/updates.d.ts +8 -2
- package/dist/channels/telegram/updates.d.ts.map +1 -1
- package/dist/channels/telegram/webhook.d.ts +10 -0
- package/dist/channels/telegram/webhook.d.ts.map +1 -1
- package/dist/channels/whatsapp/channel.d.ts +8 -0
- package/dist/channels/whatsapp/channel.d.ts.map +1 -1
- package/dist/channels/whatsapp/inbound.d.ts +2 -0
- package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
- package/dist/channels/whatsapp/index.js +45 -18
- package/dist/channels/whatsapp/index.mjs +45 -18
- package/dist/core/answer.d.ts +9 -2
- package/dist/core/answer.d.ts.map +1 -1
- package/dist/core/chunking.d.ts +31 -0
- package/dist/core/chunking.d.ts.map +1 -0
- package/dist/core/guardrails.d.ts +11 -1
- package/dist/core/guardrails.d.ts.map +1 -1
- package/dist/core/llm.d.ts +3 -1
- package/dist/core/llm.d.ts.map +1 -1
- package/dist/core/retrieval.d.ts +39 -0
- package/dist/core/retrieval.d.ts.map +1 -1
- package/dist/history/index.js +12 -9
- package/dist/history/index.mjs +12 -9
- package/dist/index.d.ts +4 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +261 -52
- package/dist/index.mjs +254 -47
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +217 -30
- package/dist/mcp-server.mjs +216 -29
- package/dist/personas/index.d.ts +1 -1
- package/dist/personas/index.d.ts.map +1 -1
- package/dist/personas/index.js +21 -7
- package/dist/personas/index.mjs +21 -7
- package/dist/personas/resolver.d.ts +35 -8
- package/dist/personas/resolver.d.ts.map +1 -1
- package/dist/personas/timeContext.d.ts.map +1 -1
- package/dist/routes/demo.d.ts.map +1 -1
- package/dist/routes/openai.d.ts.map +1 -1
- package/dist/routes/private.d.ts.map +1 -1
- package/dist/routes/public.d.ts.map +1 -1
- package/dist/scheduler/index.js +12 -9
- package/dist/scheduler/index.mjs +12 -9
- package/dist/server.d.ts.map +1 -1
- package/dist/server.js +251 -50
- package/dist/server.mjs +246 -45
- package/dist/types.d.ts +39 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +13 -13
package/dist/scheduler/index.js
CHANGED
|
@@ -116,9 +116,10 @@ function detectLeakage(text) {
|
|
|
116
116
|
function scrubOutput(text) {
|
|
117
117
|
return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
|
|
118
118
|
}
|
|
119
|
-
|
|
119
|
+
var DEFAULT_REFUSAL = "Sorry, I can't share internal instructions. How else can I help?";
|
|
120
|
+
function guardOutput(text, refusal) {
|
|
120
121
|
if (detectLeakage(text)) {
|
|
121
|
-
return
|
|
122
|
+
return typeof refusal === "string" && refusal.trim() ? refusal : DEFAULT_REFUSAL;
|
|
122
123
|
}
|
|
123
124
|
return scrubOutput(text);
|
|
124
125
|
}
|
|
@@ -130,7 +131,8 @@ async function completeOnce({
|
|
|
130
131
|
system,
|
|
131
132
|
messages,
|
|
132
133
|
temperature = 0.2,
|
|
133
|
-
model = DEFAULT_MODEL
|
|
134
|
+
model = DEFAULT_MODEL,
|
|
135
|
+
refusal
|
|
134
136
|
}) {
|
|
135
137
|
const res = await client.chat.completions.create({
|
|
136
138
|
model,
|
|
@@ -139,19 +141,19 @@ async function completeOnce({
|
|
|
139
141
|
});
|
|
140
142
|
const text = res.choices[0]?.message?.content ?? "";
|
|
141
143
|
return {
|
|
142
|
-
content: guardOutput(text),
|
|
144
|
+
content: guardOutput(text, refusal),
|
|
143
145
|
usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
144
146
|
};
|
|
145
147
|
}
|
|
146
148
|
|
|
147
149
|
// src/core/answer.ts
|
|
148
150
|
var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
|
|
149
|
-
async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
|
|
151
|
+
async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal) {
|
|
150
152
|
const result = await answerFn({ system, messages, mode, sender, conversationId });
|
|
151
153
|
const raw = typeof result === "string" ? result : result?.content;
|
|
152
154
|
const usage = typeof result === "string" ? void 0 : result?.usage;
|
|
153
155
|
return {
|
|
154
|
-
content: guardOutput(typeof raw === "string" ? raw : ""),
|
|
156
|
+
content: guardOutput(typeof raw === "string" ? raw : "", refusal),
|
|
155
157
|
usage: usage ?? noUsage()
|
|
156
158
|
};
|
|
157
159
|
}
|
|
@@ -164,12 +166,13 @@ async function answerOnce({
|
|
|
164
166
|
sender,
|
|
165
167
|
conversationId,
|
|
166
168
|
temperature,
|
|
167
|
-
model
|
|
169
|
+
model,
|
|
170
|
+
refusal
|
|
168
171
|
}) {
|
|
169
172
|
if (answerFn) {
|
|
170
|
-
return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
|
|
173
|
+
return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal);
|
|
171
174
|
}
|
|
172
|
-
return completeOnce({ client, system, messages, temperature, model });
|
|
175
|
+
return completeOnce({ client, system, messages, temperature, model, refusal });
|
|
173
176
|
}
|
|
174
177
|
|
|
175
178
|
// src/core/logger.ts
|
package/dist/scheduler/index.mjs
CHANGED
|
@@ -75,9 +75,10 @@ function detectLeakage(text) {
|
|
|
75
75
|
function scrubOutput(text) {
|
|
76
76
|
return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
|
|
77
77
|
}
|
|
78
|
-
|
|
78
|
+
var DEFAULT_REFUSAL = "Sorry, I can't share internal instructions. How else can I help?";
|
|
79
|
+
function guardOutput(text, refusal) {
|
|
79
80
|
if (detectLeakage(text)) {
|
|
80
|
-
return
|
|
81
|
+
return typeof refusal === "string" && refusal.trim() ? refusal : DEFAULT_REFUSAL;
|
|
81
82
|
}
|
|
82
83
|
return scrubOutput(text);
|
|
83
84
|
}
|
|
@@ -89,7 +90,8 @@ async function completeOnce({
|
|
|
89
90
|
system,
|
|
90
91
|
messages,
|
|
91
92
|
temperature = 0.2,
|
|
92
|
-
model = DEFAULT_MODEL
|
|
93
|
+
model = DEFAULT_MODEL,
|
|
94
|
+
refusal
|
|
93
95
|
}) {
|
|
94
96
|
const res = await client.chat.completions.create({
|
|
95
97
|
model,
|
|
@@ -98,19 +100,19 @@ async function completeOnce({
|
|
|
98
100
|
});
|
|
99
101
|
const text = res.choices[0]?.message?.content ?? "";
|
|
100
102
|
return {
|
|
101
|
-
content: guardOutput(text),
|
|
103
|
+
content: guardOutput(text, refusal),
|
|
102
104
|
usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
103
105
|
};
|
|
104
106
|
}
|
|
105
107
|
|
|
106
108
|
// src/core/answer.ts
|
|
107
109
|
var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
|
|
108
|
-
async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
|
|
110
|
+
async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal) {
|
|
109
111
|
const result = await answerFn({ system, messages, mode, sender, conversationId });
|
|
110
112
|
const raw = typeof result === "string" ? result : result?.content;
|
|
111
113
|
const usage = typeof result === "string" ? void 0 : result?.usage;
|
|
112
114
|
return {
|
|
113
|
-
content: guardOutput(typeof raw === "string" ? raw : ""),
|
|
115
|
+
content: guardOutput(typeof raw === "string" ? raw : "", refusal),
|
|
114
116
|
usage: usage ?? noUsage()
|
|
115
117
|
};
|
|
116
118
|
}
|
|
@@ -123,12 +125,13 @@ async function answerOnce({
|
|
|
123
125
|
sender,
|
|
124
126
|
conversationId,
|
|
125
127
|
temperature,
|
|
126
|
-
model
|
|
128
|
+
model,
|
|
129
|
+
refusal
|
|
127
130
|
}) {
|
|
128
131
|
if (answerFn) {
|
|
129
|
-
return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
|
|
132
|
+
return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal);
|
|
130
133
|
}
|
|
131
|
-
return completeOnce({ client, system, messages, temperature, model });
|
|
134
|
+
return completeOnce({ client, system, messages, temperature, model, refusal });
|
|
132
135
|
}
|
|
133
136
|
|
|
134
137
|
// src/core/logger.ts
|
package/dist/server.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAiB5B,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG;IAAE,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAEtE;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,
|
|
1
|
+
{"version":3,"file":"server.d.ts","sourceRoot":"","sources":["../src/server.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAIH,OAAO,EAAE,IAAI,EAAE,MAAM,MAAM,CAAC;AAiB5B,OAAO,KAAK,EAAE,aAAa,EAAsB,MAAM,SAAS,CAAC;AAEjE;;;;;;;;;;;;;;;GAeG;AACH,MAAM,MAAM,UAAU,GAAG,IAAI,GAAG;IAAE,YAAY,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAA;CAAE,CAAC;AAEtE;;;;;GAKG;AACH,wBAAsB,YAAY,CAAC,MAAM,EAAE,aAAa,GAAG,OAAO,CAAC,UAAU,CAAC,CAsQ7E"}
|
package/dist/server.js
CHANGED
|
@@ -114,6 +114,126 @@ var init_buildLock = __esm({
|
|
|
114
114
|
}
|
|
115
115
|
});
|
|
116
116
|
|
|
117
|
+
// src/core/chunking.ts
|
|
118
|
+
function headingOf(line) {
|
|
119
|
+
const match = HEADING.exec(line);
|
|
120
|
+
if (!match) return void 0;
|
|
121
|
+
const title = (match[2] ?? "").replace(/[ \t]+#+$/, "").replace(/^#+$/, "").trim();
|
|
122
|
+
return { level: match[1].length, title };
|
|
123
|
+
}
|
|
124
|
+
function startsTable(lines, i) {
|
|
125
|
+
return lines[i].includes("|") && i + 1 < lines.length && TABLE_DELIMITER.test(lines[i + 1]);
|
|
126
|
+
}
|
|
127
|
+
function fenceEnd(lines, start) {
|
|
128
|
+
const opener = FENCE.exec(lines[start]);
|
|
129
|
+
if (!opener) return start + 1;
|
|
130
|
+
const marker = opener[1];
|
|
131
|
+
const closer = new RegExp(`^ {0,3}${marker[0] === "`" ? "`" : "~"}{${marker.length},}[ \\t]*$`);
|
|
132
|
+
for (let i = start + 1; i < lines.length; i++) if (closer.test(lines[i])) return i + 1;
|
|
133
|
+
return lines.length;
|
|
134
|
+
}
|
|
135
|
+
function listEnd(lines, start) {
|
|
136
|
+
let end = start + 1;
|
|
137
|
+
for (let i = start + 1; i < lines.length; i++) {
|
|
138
|
+
const line = lines[i];
|
|
139
|
+
if (isBlank(line)) {
|
|
140
|
+
let next = i + 1;
|
|
141
|
+
while (next < lines.length && isBlank(lines[next])) next++;
|
|
142
|
+
if (next < lines.length && (LIST_ITEM.test(lines[next]) || isIndented(lines[next]))) {
|
|
143
|
+
i = next - 1;
|
|
144
|
+
continue;
|
|
145
|
+
}
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
if (headingOf(line) && !isIndented(line)) break;
|
|
149
|
+
if (FENCE.test(line) && !isIndented(line)) break;
|
|
150
|
+
end = i + 1;
|
|
151
|
+
}
|
|
152
|
+
return end;
|
|
153
|
+
}
|
|
154
|
+
function tableEnd(lines, start) {
|
|
155
|
+
let end = start + 2;
|
|
156
|
+
while (end < lines.length && !isBlank(lines[end]) && lines[end].includes("|")) end++;
|
|
157
|
+
return end;
|
|
158
|
+
}
|
|
159
|
+
function paragraphEnd(lines, start) {
|
|
160
|
+
let end = start + 1;
|
|
161
|
+
while (end < lines.length && !isBlank(lines[end]) && !headingOf(lines[end]) && !FENCE.test(lines[end]) && !LIST_ITEM.test(lines[end])) {
|
|
162
|
+
end++;
|
|
163
|
+
}
|
|
164
|
+
return end;
|
|
165
|
+
}
|
|
166
|
+
function scan(text) {
|
|
167
|
+
const lines = text.replace(/\r\n?/g, "\n").split("\n");
|
|
168
|
+
const sections = [{ trail: [], blocks: [] }];
|
|
169
|
+
const stack = [];
|
|
170
|
+
let i = 0;
|
|
171
|
+
while (i < lines.length) {
|
|
172
|
+
const line = lines[i];
|
|
173
|
+
if (isBlank(line)) {
|
|
174
|
+
i++;
|
|
175
|
+
continue;
|
|
176
|
+
}
|
|
177
|
+
const heading = FENCE.test(line) ? void 0 : headingOf(line);
|
|
178
|
+
if (heading) {
|
|
179
|
+
while (stack.length && stack[stack.length - 1].level >= heading.level) stack.pop();
|
|
180
|
+
stack.push(heading);
|
|
181
|
+
sections.push({ trail: stack.map((h) => h.title), heading: line, blocks: [] });
|
|
182
|
+
i++;
|
|
183
|
+
continue;
|
|
184
|
+
}
|
|
185
|
+
let end;
|
|
186
|
+
if (FENCE.test(line)) end = fenceEnd(lines, i);
|
|
187
|
+
else if (LIST_ITEM.test(line)) end = listEnd(lines, i);
|
|
188
|
+
else if (startsTable(lines, i)) end = tableEnd(lines, i);
|
|
189
|
+
else end = paragraphEnd(lines, i);
|
|
190
|
+
sections[sections.length - 1].blocks.push({ lines: lines.slice(i, end) });
|
|
191
|
+
i = end;
|
|
192
|
+
}
|
|
193
|
+
return sections;
|
|
194
|
+
}
|
|
195
|
+
function chunkSections(text, max = 900) {
|
|
196
|
+
const cap = Math.max(1, max);
|
|
197
|
+
const out = [];
|
|
198
|
+
const emit = (parts, section) => {
|
|
199
|
+
const body = parts.join("\n\n");
|
|
200
|
+
if (body.trim() === "") return;
|
|
201
|
+
out.push({ text: body, section: [...section], position: out.length });
|
|
202
|
+
};
|
|
203
|
+
for (const section of scan(text)) {
|
|
204
|
+
let parts = section.heading ? [section.heading.trim()] : [];
|
|
205
|
+
let size = parts.join("\n\n").length;
|
|
206
|
+
let hasBlock = false;
|
|
207
|
+
for (const block of section.blocks) {
|
|
208
|
+
const piece = block.lines.join("\n");
|
|
209
|
+
const added = piece.length + (parts.length ? 2 : 0);
|
|
210
|
+
if (hasBlock && size + added > cap) {
|
|
211
|
+
emit(parts, section.trail);
|
|
212
|
+
parts = [];
|
|
213
|
+
size = 0;
|
|
214
|
+
hasBlock = false;
|
|
215
|
+
}
|
|
216
|
+
size += piece.length + (parts.length ? 2 : 0);
|
|
217
|
+
parts.push(piece);
|
|
218
|
+
hasBlock = true;
|
|
219
|
+
}
|
|
220
|
+
emit(parts, section.trail);
|
|
221
|
+
}
|
|
222
|
+
return out;
|
|
223
|
+
}
|
|
224
|
+
var FENCE, HEADING, LIST_ITEM, TABLE_DELIMITER, isBlank, isIndented;
|
|
225
|
+
var init_chunking = __esm({
|
|
226
|
+
"src/core/chunking.ts"() {
|
|
227
|
+
"use strict";
|
|
228
|
+
FENCE = /^ {0,3}(`{3,}|~{3,})/;
|
|
229
|
+
HEADING = /^ {0,3}(#{1,6})(?:[ \t]+(.*?))?[ \t]*$/;
|
|
230
|
+
LIST_ITEM = /^[ \t]*(?:[-*+]|\d{1,9}[.)])[ \t]+\S/;
|
|
231
|
+
TABLE_DELIMITER = /^[ \t]*\|?[ \t]*:?-+:?[ \t]*(?:\|[ \t]*:?-+:?[ \t]*)*\|?[ \t]*$/;
|
|
232
|
+
isBlank = (line) => line.trim() === "";
|
|
233
|
+
isIndented = (line) => /^(?: {2,}|\t)/.test(line);
|
|
234
|
+
}
|
|
235
|
+
});
|
|
236
|
+
|
|
117
237
|
// src/core/loaders.ts
|
|
118
238
|
function walk(dir) {
|
|
119
239
|
const items = [];
|
|
@@ -194,11 +314,13 @@ async function sha256(input) {
|
|
|
194
314
|
const hash = await crypto.subtle.digest("SHA-256", data);
|
|
195
315
|
return Array.from(new Uint8Array(hash)).map((b) => b.toString(16).padStart(2, "0")).join("");
|
|
196
316
|
}
|
|
197
|
-
var EMB_MODEL, DEFAULT_KNOWLEDGE_DIR, DATABASE_CONFIG_REQUIRED_MESSAGE, VectorStore;
|
|
317
|
+
var import_node_path3, EMB_MODEL, DEFAULT_KNOWLEDGE_DIR, DATABASE_CONFIG_REQUIRED_MESSAGE, VectorStore;
|
|
198
318
|
var init_retrieval = __esm({
|
|
199
319
|
"src/core/retrieval.ts"() {
|
|
200
320
|
"use strict";
|
|
321
|
+
import_node_path3 = require("node:path");
|
|
201
322
|
init_buildLock();
|
|
323
|
+
init_chunking();
|
|
202
324
|
init_loaders();
|
|
203
325
|
init_logger();
|
|
204
326
|
EMB_MODEL = "text-embedding-3-large";
|
|
@@ -209,6 +331,7 @@ var init_retrieval = __esm({
|
|
|
209
331
|
this.embed = embed;
|
|
210
332
|
this.db = options.databaseClient;
|
|
211
333
|
this.knowledgeDir = options.knowledgeDir || DEFAULT_KNOWLEDGE_DIR;
|
|
334
|
+
this.chunking = options.chunking ?? "lines";
|
|
212
335
|
this.logger = options.logger ?? createConsoleLogger();
|
|
213
336
|
this.buildLock = options.buildLock ?? createTursoBuildLock(this.db);
|
|
214
337
|
this.instanceId = options.instanceId ?? crypto.randomUUID();
|
|
@@ -222,6 +345,7 @@ var init_retrieval = __esm({
|
|
|
222
345
|
*/
|
|
223
346
|
db;
|
|
224
347
|
knowledgeDir;
|
|
348
|
+
chunking;
|
|
225
349
|
logger;
|
|
226
350
|
buildLock;
|
|
227
351
|
instanceId;
|
|
@@ -282,11 +406,41 @@ var init_retrieval = __esm({
|
|
|
282
406
|
this.logger.info("No knowledge documents and no existing chunks - nothing to build");
|
|
283
407
|
return;
|
|
284
408
|
}
|
|
409
|
+
this.logger.info(`Chunking mode: ${this.chunking}`);
|
|
410
|
+
if (this.chunking === "sections") await this.ensureSectionColumns();
|
|
285
411
|
const rows = [];
|
|
286
412
|
for (const d of docs) {
|
|
287
|
-
|
|
288
|
-
const
|
|
289
|
-
|
|
413
|
+
if (this.chunking === "sections") {
|
|
414
|
+
const source = (0, import_node_path3.relative)(this.knowledgeDir, d.source).split(import_node_path3.sep).join("/");
|
|
415
|
+
for (const c of chunkSections(d.text)) {
|
|
416
|
+
const section = c.section.join(" > ");
|
|
417
|
+
const id = await sha256(`${d.bucket}|${source}|${section}|${c.text}`);
|
|
418
|
+
const context = section ? `${source} > ${section}` : source;
|
|
419
|
+
rows.push({
|
|
420
|
+
id,
|
|
421
|
+
bucket: d.bucket,
|
|
422
|
+
source,
|
|
423
|
+
text: c.text,
|
|
424
|
+
embedText: `${context}
|
|
425
|
+
|
|
426
|
+
${c.text}`,
|
|
427
|
+
section,
|
|
428
|
+
position: c.position
|
|
429
|
+
});
|
|
430
|
+
}
|
|
431
|
+
} else {
|
|
432
|
+
for (const part of chunk(d.text)) {
|
|
433
|
+
const id = await sha256(`${d.bucket}|${d.source}|${part}`);
|
|
434
|
+
rows.push({
|
|
435
|
+
id,
|
|
436
|
+
bucket: d.bucket,
|
|
437
|
+
source: d.source,
|
|
438
|
+
text: part,
|
|
439
|
+
embedText: part,
|
|
440
|
+
section: null,
|
|
441
|
+
position: null
|
|
442
|
+
});
|
|
443
|
+
}
|
|
290
444
|
}
|
|
291
445
|
}
|
|
292
446
|
this.logger.info(`Created ${rows.length} chunks from knowledge documents`);
|
|
@@ -295,11 +449,17 @@ var init_retrieval = __esm({
|
|
|
295
449
|
for (let i = 0; i < rows.length; i += UPSERT_BATCH) {
|
|
296
450
|
const batch = rows.slice(i, i + UPSERT_BATCH);
|
|
297
451
|
await this.db.batch(
|
|
298
|
-
batch.map(
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
452
|
+
batch.map(
|
|
453
|
+
(r) => this.chunking === "sections" ? {
|
|
454
|
+
sql: `INSERT INTO chunks(id,bucket,source,text,section,position) VALUES(?,?,?,?,?,?)
|
|
455
|
+
ON CONFLICT(id) DO NOTHING`,
|
|
456
|
+
args: [r.id, r.bucket, r.source, r.text, r.section, r.position]
|
|
457
|
+
} : {
|
|
458
|
+
sql: `INSERT INTO chunks(id,bucket,source,text) VALUES(?,?,?,?)
|
|
459
|
+
ON CONFLICT(id) DO NOTHING`,
|
|
460
|
+
args: [r.id, r.bucket, r.source, r.text]
|
|
461
|
+
}
|
|
462
|
+
),
|
|
303
463
|
"write"
|
|
304
464
|
);
|
|
305
465
|
}
|
|
@@ -320,7 +480,7 @@ var init_retrieval = __esm({
|
|
|
320
480
|
return;
|
|
321
481
|
}
|
|
322
482
|
this.logger.info(`Embedding ${missing.length} new/updated chunks with ${EMB_MODEL}...`);
|
|
323
|
-
const textById = new Map(rows.map((r) => [r.id, r.
|
|
483
|
+
const textById = new Map(rows.map((r) => [r.id, r.embedText]));
|
|
324
484
|
const BATCH = 96;
|
|
325
485
|
for (let i = 0; i < missing.length; i += BATCH) {
|
|
326
486
|
const batchIds = missing.slice(i, i + BATCH);
|
|
@@ -341,6 +501,18 @@ var init_retrieval = __esm({
|
|
|
341
501
|
}
|
|
342
502
|
this.logger.info(`Successfully embedded ${missing.length} new chunks`);
|
|
343
503
|
}
|
|
504
|
+
/**
|
|
505
|
+
* Adds the nullable `section`/`position` columns to a `chunks` table that
|
|
506
|
+
* every released version created without them. Checked via table_info so it
|
|
507
|
+
* is a no-op once present; only `'sections'` mode ever calls it.
|
|
508
|
+
*/
|
|
509
|
+
async ensureSectionColumns() {
|
|
510
|
+
const info = await this.db.execute("PRAGMA table_info(chunks)");
|
|
511
|
+
const have = new Set(info.rows.map((r) => String(r.name)));
|
|
512
|
+
if (!have.has("section")) await this.db.execute("ALTER TABLE chunks ADD COLUMN section TEXT");
|
|
513
|
+
if (!have.has("position"))
|
|
514
|
+
await this.db.execute("ALTER TABLE chunks ADD COLUMN position INTEGER");
|
|
515
|
+
}
|
|
344
516
|
// Remove chunks from database that no longer exist in markdown files
|
|
345
517
|
async cleanupStaleChunks(currentIds) {
|
|
346
518
|
const result = await this.db.execute("SELECT id FROM chunks");
|
|
@@ -387,25 +559,34 @@ var init_retrieval = __esm({
|
|
|
387
559
|
* interpolated. An empty list retrieves nothing, and short-circuits before
|
|
388
560
|
* the embedding call.
|
|
389
561
|
*/
|
|
390
|
-
async
|
|
562
|
+
async queryChunks(q, k = 6, allowed = ["base"]) {
|
|
391
563
|
if (allowed.length === 0) return [];
|
|
392
564
|
const [qv] = await this.embed([q]);
|
|
393
565
|
const placeholders = allowed.map(() => "?").join(",");
|
|
394
566
|
const res = await this.db.execute({
|
|
395
|
-
sql: `SELECT c
|
|
567
|
+
sql: `SELECT c.*, e.embedding
|
|
396
568
|
FROM chunks c
|
|
397
569
|
JOIN embeddings e ON e.id = c.id
|
|
398
570
|
WHERE c.bucket IN (${placeholders})`,
|
|
399
571
|
args: allowed
|
|
400
572
|
});
|
|
401
|
-
const scored =
|
|
402
|
-
for (const row of res.rows) {
|
|
573
|
+
const scored = res.rows.map((row) => {
|
|
403
574
|
const emb = JSON.parse(String(row.embedding));
|
|
404
|
-
const
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
|
|
408
|
-
|
|
575
|
+
const trail = row.section == null ? "" : String(row.section);
|
|
576
|
+
return {
|
|
577
|
+
text: String(row.text),
|
|
578
|
+
bucket: String(row.bucket ?? ""),
|
|
579
|
+
source: String(row.source ?? ""),
|
|
580
|
+
section: trail ? trail.split(" > ") : [],
|
|
581
|
+
score: _VectorStore.cosine(qv, emb)
|
|
582
|
+
};
|
|
583
|
+
});
|
|
584
|
+
scored.sort((a, b) => b.score - a.score);
|
|
585
|
+
return scored.slice(0, k);
|
|
586
|
+
}
|
|
587
|
+
/** Same results as {@link queryChunks}, as bare text. */
|
|
588
|
+
async query(q, k = 6, allowed = ["base"]) {
|
|
589
|
+
return (await this.queryChunks(q, k, allowed)).map((c) => c.text);
|
|
409
590
|
}
|
|
410
591
|
};
|
|
411
592
|
}
|
|
@@ -530,7 +711,7 @@ __export(server_exports, {
|
|
|
530
711
|
createServer: () => createServer
|
|
531
712
|
});
|
|
532
713
|
module.exports = __toCommonJS(server_exports);
|
|
533
|
-
var
|
|
714
|
+
var import_node_path5 = require("node:path");
|
|
534
715
|
var import_hono5 = require("hono");
|
|
535
716
|
var import_openai = __toESM(require("openai"));
|
|
536
717
|
|
|
@@ -672,9 +853,10 @@ function detectLeakage(text) {
|
|
|
672
853
|
function scrubOutput(text) {
|
|
673
854
|
return text.replace(/sk-[A-Za-z0-9]{15,}/g, "[REDACTED]").replace(/AIza[0-9A-Za-z_-]{30,}/g, "[REDACTED]");
|
|
674
855
|
}
|
|
675
|
-
|
|
856
|
+
var DEFAULT_REFUSAL = "Sorry, I can't share internal instructions. How else can I help?";
|
|
857
|
+
function guardOutput(text, refusal) {
|
|
676
858
|
if (detectLeakage(text)) {
|
|
677
|
-
return
|
|
859
|
+
return typeof refusal === "string" && refusal.trim() ? refusal : DEFAULT_REFUSAL;
|
|
678
860
|
}
|
|
679
861
|
return scrubOutput(text);
|
|
680
862
|
}
|
|
@@ -686,7 +868,8 @@ async function completeOnce({
|
|
|
686
868
|
system,
|
|
687
869
|
messages,
|
|
688
870
|
temperature = 0.2,
|
|
689
|
-
model = DEFAULT_MODEL
|
|
871
|
+
model = DEFAULT_MODEL,
|
|
872
|
+
refusal
|
|
690
873
|
}) {
|
|
691
874
|
const res = await client.chat.completions.create({
|
|
692
875
|
model,
|
|
@@ -695,7 +878,7 @@ async function completeOnce({
|
|
|
695
878
|
});
|
|
696
879
|
const text = res.choices[0]?.message?.content ?? "";
|
|
697
880
|
return {
|
|
698
|
-
content: guardOutput(text),
|
|
881
|
+
content: guardOutput(text, refusal),
|
|
699
882
|
usage: res.usage || { prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 }
|
|
700
883
|
};
|
|
701
884
|
}
|
|
@@ -720,12 +903,12 @@ async function* completeStream({
|
|
|
720
903
|
|
|
721
904
|
// src/core/answer.ts
|
|
722
905
|
var noUsage = () => ({ prompt_tokens: 0, completion_tokens: 0, total_tokens: 0 });
|
|
723
|
-
async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }) {
|
|
906
|
+
async function runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal) {
|
|
724
907
|
const result = await answerFn({ system, messages, mode, sender, conversationId });
|
|
725
908
|
const raw = typeof result === "string" ? result : result?.content;
|
|
726
909
|
const usage = typeof result === "string" ? void 0 : result?.usage;
|
|
727
910
|
return {
|
|
728
|
-
content: guardOutput(typeof raw === "string" ? raw : ""),
|
|
911
|
+
content: guardOutput(typeof raw === "string" ? raw : "", refusal),
|
|
729
912
|
usage: usage ?? noUsage()
|
|
730
913
|
};
|
|
731
914
|
}
|
|
@@ -738,12 +921,13 @@ async function answerOnce({
|
|
|
738
921
|
sender,
|
|
739
922
|
conversationId,
|
|
740
923
|
temperature,
|
|
741
|
-
model
|
|
924
|
+
model,
|
|
925
|
+
refusal
|
|
742
926
|
}) {
|
|
743
927
|
if (answerFn) {
|
|
744
|
-
return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId });
|
|
928
|
+
return runAnswerFn(answerFn, { system, messages, mode, sender, conversationId }, refusal);
|
|
745
929
|
}
|
|
746
|
-
return completeOnce({ client, system, messages, temperature, model });
|
|
930
|
+
return completeOnce({ client, system, messages, temperature, model, refusal });
|
|
747
931
|
}
|
|
748
932
|
async function applyTransformReply(transformReply, input, logger) {
|
|
749
933
|
if (!transformReply) return input.text;
|
|
@@ -767,16 +951,15 @@ async function* answerStream({
|
|
|
767
951
|
sender,
|
|
768
952
|
conversationId,
|
|
769
953
|
temperature,
|
|
770
|
-
model
|
|
954
|
+
model,
|
|
955
|
+
refusal
|
|
771
956
|
}) {
|
|
772
957
|
if (answerFn) {
|
|
773
|
-
const { content } = await runAnswerFn(
|
|
774
|
-
|
|
775
|
-
messages,
|
|
776
|
-
|
|
777
|
-
|
|
778
|
-
conversationId
|
|
779
|
-
});
|
|
958
|
+
const { content } = await runAnswerFn(
|
|
959
|
+
answerFn,
|
|
960
|
+
{ system, messages, mode, sender, conversationId },
|
|
961
|
+
refusal
|
|
962
|
+
);
|
|
780
963
|
if (content) yield content;
|
|
781
964
|
return;
|
|
782
965
|
}
|
|
@@ -1076,7 +1259,7 @@ function loadServeStatic(runtime = detectRuntime()) {
|
|
|
1076
1259
|
|
|
1077
1260
|
// src/core/widgets.ts
|
|
1078
1261
|
var import_node_fs3 = require("node:fs");
|
|
1079
|
-
var
|
|
1262
|
+
var import_node_path4 = require("node:path");
|
|
1080
1263
|
var import_node_url = require("node:url");
|
|
1081
1264
|
var import_meta = {};
|
|
1082
1265
|
function resolveStatic(configStaticDir) {
|
|
@@ -1085,15 +1268,15 @@ function resolveStatic(configStaticDir) {
|
|
|
1085
1268
|
}
|
|
1086
1269
|
try {
|
|
1087
1270
|
if (import_meta.url) {
|
|
1088
|
-
const moduleDir = (0,
|
|
1089
|
-
const staticDir = (0,
|
|
1271
|
+
const moduleDir = (0, import_node_path4.dirname)((0, import_node_url.fileURLToPath)(import_meta.url));
|
|
1272
|
+
const staticDir = (0, import_node_path4.join)(moduleDir, "static");
|
|
1090
1273
|
if ((0, import_node_fs3.existsSync)(staticDir)) return { staticDir };
|
|
1091
1274
|
}
|
|
1092
1275
|
} catch {
|
|
1093
1276
|
}
|
|
1094
1277
|
try {
|
|
1095
1278
|
if (typeof __dirname !== "undefined") {
|
|
1096
|
-
const staticDir = (0,
|
|
1279
|
+
const staticDir = (0, import_node_path4.join)(__dirname, "static");
|
|
1097
1280
|
if ((0, import_node_fs3.existsSync)(staticDir)) return { staticDir };
|
|
1098
1281
|
}
|
|
1099
1282
|
} catch {
|
|
@@ -1425,7 +1608,8 @@ function demoRoutes(deps) {
|
|
|
1425
1608
|
system,
|
|
1426
1609
|
messages,
|
|
1427
1610
|
mode: "public",
|
|
1428
|
-
model
|
|
1611
|
+
model,
|
|
1612
|
+
refusal: config.refusal
|
|
1429
1613
|
})) {
|
|
1430
1614
|
await s.write(`data: ${JSON.stringify({ delta })}
|
|
1431
1615
|
|
|
@@ -1440,7 +1624,8 @@ function demoRoutes(deps) {
|
|
|
1440
1624
|
system,
|
|
1441
1625
|
messages,
|
|
1442
1626
|
mode: "public",
|
|
1443
|
-
model
|
|
1627
|
+
model,
|
|
1628
|
+
refusal: config.refusal
|
|
1444
1629
|
});
|
|
1445
1630
|
const reply = await applyTransformReply(
|
|
1446
1631
|
config.transformReply,
|
|
@@ -1662,7 +1847,8 @@ function openaiRoutes(deps) {
|
|
|
1662
1847
|
sender,
|
|
1663
1848
|
conversationId,
|
|
1664
1849
|
temperature,
|
|
1665
|
-
model: serverModel
|
|
1850
|
+
model: serverModel,
|
|
1851
|
+
refusal: config.refusal
|
|
1666
1852
|
})) {
|
|
1667
1853
|
await s.write(chunk2({ content: delta }, null));
|
|
1668
1854
|
}
|
|
@@ -1679,7 +1865,8 @@ function openaiRoutes(deps) {
|
|
|
1679
1865
|
sender,
|
|
1680
1866
|
conversationId,
|
|
1681
1867
|
temperature,
|
|
1682
|
-
model: serverModel
|
|
1868
|
+
model: serverModel,
|
|
1869
|
+
refusal: config.refusal
|
|
1683
1870
|
});
|
|
1684
1871
|
const reply = await applyTransformReply(
|
|
1685
1872
|
config.transformReply,
|
|
@@ -1770,7 +1957,8 @@ function privateRoutes(deps) {
|
|
|
1770
1957
|
messages,
|
|
1771
1958
|
mode: "private",
|
|
1772
1959
|
sender,
|
|
1773
|
-
model
|
|
1960
|
+
model,
|
|
1961
|
+
refusal: config.refusal
|
|
1774
1962
|
})) {
|
|
1775
1963
|
await s.write(`data: ${JSON.stringify({ delta })}
|
|
1776
1964
|
|
|
@@ -1786,7 +1974,8 @@ function privateRoutes(deps) {
|
|
|
1786
1974
|
messages,
|
|
1787
1975
|
mode: "private",
|
|
1788
1976
|
sender,
|
|
1789
|
-
model
|
|
1977
|
+
model,
|
|
1978
|
+
refusal: config.refusal
|
|
1790
1979
|
});
|
|
1791
1980
|
const reply = await applyTransformReply(
|
|
1792
1981
|
config.transformReply,
|
|
@@ -1953,7 +2142,8 @@ function publicRoutes(deps) {
|
|
|
1953
2142
|
system,
|
|
1954
2143
|
messages,
|
|
1955
2144
|
mode: "public",
|
|
1956
|
-
model
|
|
2145
|
+
model,
|
|
2146
|
+
refusal: config.refusal
|
|
1957
2147
|
})) {
|
|
1958
2148
|
await s.write(`data: ${JSON.stringify({ delta })}
|
|
1959
2149
|
|
|
@@ -1962,7 +2152,15 @@ function publicRoutes(deps) {
|
|
|
1962
2152
|
await s.write("event: end\ndata: {}\n\n");
|
|
1963
2153
|
});
|
|
1964
2154
|
}
|
|
1965
|
-
const out = await answerOnce({
|
|
2155
|
+
const out = await answerOnce({
|
|
2156
|
+
answerFn,
|
|
2157
|
+
client,
|
|
2158
|
+
system,
|
|
2159
|
+
messages,
|
|
2160
|
+
mode: "public",
|
|
2161
|
+
model,
|
|
2162
|
+
refusal: config.refusal
|
|
2163
|
+
});
|
|
1966
2164
|
const reply = await applyTransformReply(
|
|
1967
2165
|
config.transformReply,
|
|
1968
2166
|
{ channel: "widget-public", text: out.content },
|
|
@@ -2004,6 +2202,7 @@ async function createServer(config) {
|
|
|
2004
2202
|
store = new VectorStore2(createOpenAIEmbedder2(client), {
|
|
2005
2203
|
databaseClient: db,
|
|
2006
2204
|
knowledgeDir: config.knowledgeDir || DEFAULT_KNOWLEDGE_DIR2,
|
|
2205
|
+
chunking: config.chunking,
|
|
2007
2206
|
logger
|
|
2008
2207
|
});
|
|
2009
2208
|
}
|
|
@@ -2054,7 +2253,7 @@ async function createServer(config) {
|
|
|
2054
2253
|
}
|
|
2055
2254
|
}
|
|
2056
2255
|
if (serveStatic && staticDir) {
|
|
2057
|
-
const relativePath = (0,
|
|
2256
|
+
const relativePath = (0, import_node_path5.relative)(process.cwd(), staticDir);
|
|
2058
2257
|
app.get("/chatter.js", serveStatic({ path: `${relativePath}/chatter.js` }));
|
|
2059
2258
|
app.get("/chatter.css", serveStatic({ path: `${relativePath}/chatter.css` }));
|
|
2060
2259
|
}
|
|
@@ -2101,6 +2300,7 @@ async function createServer(config) {
|
|
|
2101
2300
|
app.get("/demo/react", serveStatic({ path: `${publicDir}/react-demo.html` }));
|
|
2102
2301
|
}
|
|
2103
2302
|
const senders = createSenderRegistry(logger);
|
|
2303
|
+
const identities = /* @__PURE__ */ new Map();
|
|
2104
2304
|
const deps = {
|
|
2105
2305
|
client,
|
|
2106
2306
|
store,
|
|
@@ -2112,6 +2312,7 @@ async function createServer(config) {
|
|
|
2112
2312
|
prompts,
|
|
2113
2313
|
apiKeyManager,
|
|
2114
2314
|
senders,
|
|
2315
|
+
identities,
|
|
2115
2316
|
logger
|
|
2116
2317
|
};
|
|
2117
2318
|
if (enablePublic) {
|