@diegoaltoworks/chatter 0.56.0 → 0.58.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/dist/channels/index.js +46 -6
- package/dist/channels/index.mjs +46 -6
- package/dist/channels/pipeline.d.ts +13 -0
- package/dist/channels/pipeline.d.ts.map +1 -1
- package/dist/channels/telegram/channel.d.ts +12 -0
- package/dist/channels/telegram/channel.d.ts.map +1 -1
- package/dist/channels/telegram/index.js +48 -6
- package/dist/channels/telegram/index.mjs +48 -6
- package/dist/channels/whatsapp/inbound.d.ts +13 -0
- package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
- package/dist/channels/whatsapp/index.js +48 -6
- package/dist/channels/whatsapp/index.mjs +48 -6
- package/dist/core/pipeline.d.ts +52 -1
- package/dist/core/pipeline.d.ts.map +1 -1
- package/dist/history/index.d.ts +1 -0
- package/dist/history/index.d.ts.map +1 -1
- package/dist/history/index.js +23 -2
- package/dist/history/index.mjs +23 -2
- package/dist/history/tursoStore.d.ts +13 -1
- package/dist/history/tursoStore.d.ts.map +1 -1
- package/dist/history/types.d.ts +2 -0
- package/dist/history/types.d.ts.map +1 -1
- package/dist/index.d.ts +1 -1
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +74 -9
- package/dist/index.mjs +74 -9
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +31 -4
- package/dist/mcp-server.mjs +31 -4
- 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/server.js +66 -7
- package/dist/server.mjs +66 -7
- package/dist/types.d.ts +25 -0
- package/dist/types.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/channels/index.js
CHANGED
|
@@ -221,14 +221,35 @@ async function prepareChat({
|
|
|
221
221
|
messages,
|
|
222
222
|
personaLayer,
|
|
223
223
|
channelHint,
|
|
224
|
-
buckets
|
|
224
|
+
buckets,
|
|
225
|
+
sender,
|
|
226
|
+
rewriteQuery,
|
|
227
|
+
rerankContext,
|
|
228
|
+
logger
|
|
225
229
|
}) {
|
|
226
230
|
const lastUserMsg = lastUserMessage(messages);
|
|
227
231
|
if (!lastUserMsg) {
|
|
228
232
|
throw new Error("no user message found in conversation");
|
|
229
233
|
}
|
|
230
234
|
const { topK, label } = MODE_SETTINGS[mode];
|
|
231
|
-
|
|
235
|
+
let query = lastUserMsg.content;
|
|
236
|
+
if (rewriteQuery) {
|
|
237
|
+
try {
|
|
238
|
+
const rewritten = await rewriteQuery({ query, mode, sender });
|
|
239
|
+
if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
|
|
240
|
+
} catch (error) {
|
|
241
|
+
logger?.error("rewriteQuery threw; falling back to the original query", error);
|
|
242
|
+
}
|
|
243
|
+
}
|
|
244
|
+
let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
|
|
245
|
+
if (rerankContext) {
|
|
246
|
+
try {
|
|
247
|
+
const reranked = await rerankContext({ query, chunks: ctx });
|
|
248
|
+
if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
|
|
249
|
+
} catch (error) {
|
|
250
|
+
logger?.error("rerankContext threw; falling back to the original chunks", error);
|
|
251
|
+
}
|
|
252
|
+
}
|
|
232
253
|
const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
|
|
233
254
|
const hint = channelHint?.trim();
|
|
234
255
|
const system = [
|
|
@@ -253,6 +274,16 @@ async function resolvePersonaLayer(config, ctx) {
|
|
|
253
274
|
return void 0;
|
|
254
275
|
}
|
|
255
276
|
}
|
|
277
|
+
async function isHistoryEnabled(config, sender) {
|
|
278
|
+
const predicate = config.history?.historyEnabledFor;
|
|
279
|
+
if (!predicate) return true;
|
|
280
|
+
try {
|
|
281
|
+
return await predicate(sender);
|
|
282
|
+
} catch (error) {
|
|
283
|
+
config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
|
|
284
|
+
return false;
|
|
285
|
+
}
|
|
286
|
+
}
|
|
256
287
|
function createInboundPipeline(deps, config) {
|
|
257
288
|
const mutedChats = /* @__PURE__ */ new Set();
|
|
258
289
|
const now = config.now ?? Date.now;
|
|
@@ -292,12 +323,15 @@ function createInboundPipeline(deps, config) {
|
|
|
292
323
|
}
|
|
293
324
|
const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
|
|
294
325
|
const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
|
|
295
|
-
const
|
|
326
|
+
const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
|
|
327
|
+
const priorTurns = config.history && historyEnabled ? await config.history.store.load(
|
|
296
328
|
conversationId,
|
|
297
329
|
config.history.limit ?? DEFAULT_HISTORY_LIMIT
|
|
298
330
|
) : [];
|
|
299
331
|
const messages = [...priorTurns, { role: "user", content: msg.text }];
|
|
300
|
-
|
|
332
|
+
if (historyEnabled) {
|
|
333
|
+
await config.history?.store.append(conversationId, { role: "user", content: msg.text });
|
|
334
|
+
}
|
|
301
335
|
const { system } = await prepareChat({
|
|
302
336
|
store: deps.store,
|
|
303
337
|
prompts: deps.prompts,
|
|
@@ -305,7 +339,11 @@ function createInboundPipeline(deps, config) {
|
|
|
305
339
|
messages,
|
|
306
340
|
personaLayer,
|
|
307
341
|
channelHint: config.channelHint,
|
|
308
|
-
buckets
|
|
342
|
+
buckets,
|
|
343
|
+
sender,
|
|
344
|
+
rewriteQuery: config.rewriteQuery,
|
|
345
|
+
rerankContext: config.rerankContext,
|
|
346
|
+
logger: config.logger
|
|
309
347
|
});
|
|
310
348
|
const { content: produced } = await answerOnce({
|
|
311
349
|
answerFn: config.answerFn,
|
|
@@ -325,7 +363,9 @@ function createInboundPipeline(deps, config) {
|
|
|
325
363
|
if (!content) {
|
|
326
364
|
return { action: "ignore" };
|
|
327
365
|
}
|
|
328
|
-
|
|
366
|
+
if (historyEnabled) {
|
|
367
|
+
await config.history?.store.append(conversationId, { role: "assistant", content });
|
|
368
|
+
}
|
|
329
369
|
await turn.reply.sendAnswer(msg.chatId, content);
|
|
330
370
|
return { action: "reply", content };
|
|
331
371
|
};
|
package/dist/channels/index.mjs
CHANGED
|
@@ -188,14 +188,35 @@ async function prepareChat({
|
|
|
188
188
|
messages,
|
|
189
189
|
personaLayer,
|
|
190
190
|
channelHint,
|
|
191
|
-
buckets
|
|
191
|
+
buckets,
|
|
192
|
+
sender,
|
|
193
|
+
rewriteQuery,
|
|
194
|
+
rerankContext,
|
|
195
|
+
logger
|
|
192
196
|
}) {
|
|
193
197
|
const lastUserMsg = lastUserMessage(messages);
|
|
194
198
|
if (!lastUserMsg) {
|
|
195
199
|
throw new Error("no user message found in conversation");
|
|
196
200
|
}
|
|
197
201
|
const { topK, label } = MODE_SETTINGS[mode];
|
|
198
|
-
|
|
202
|
+
let query = lastUserMsg.content;
|
|
203
|
+
if (rewriteQuery) {
|
|
204
|
+
try {
|
|
205
|
+
const rewritten = await rewriteQuery({ query, mode, sender });
|
|
206
|
+
if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
|
|
207
|
+
} catch (error) {
|
|
208
|
+
logger?.error("rewriteQuery threw; falling back to the original query", error);
|
|
209
|
+
}
|
|
210
|
+
}
|
|
211
|
+
let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
|
|
212
|
+
if (rerankContext) {
|
|
213
|
+
try {
|
|
214
|
+
const reranked = await rerankContext({ query, chunks: ctx });
|
|
215
|
+
if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
|
|
216
|
+
} catch (error) {
|
|
217
|
+
logger?.error("rerankContext threw; falling back to the original chunks", error);
|
|
218
|
+
}
|
|
219
|
+
}
|
|
199
220
|
const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
|
|
200
221
|
const hint = channelHint?.trim();
|
|
201
222
|
const system = [
|
|
@@ -220,6 +241,16 @@ async function resolvePersonaLayer(config, ctx) {
|
|
|
220
241
|
return void 0;
|
|
221
242
|
}
|
|
222
243
|
}
|
|
244
|
+
async function isHistoryEnabled(config, sender) {
|
|
245
|
+
const predicate = config.history?.historyEnabledFor;
|
|
246
|
+
if (!predicate) return true;
|
|
247
|
+
try {
|
|
248
|
+
return await predicate(sender);
|
|
249
|
+
} catch (error) {
|
|
250
|
+
config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
|
|
251
|
+
return false;
|
|
252
|
+
}
|
|
253
|
+
}
|
|
223
254
|
function createInboundPipeline(deps, config) {
|
|
224
255
|
const mutedChats = /* @__PURE__ */ new Set();
|
|
225
256
|
const now = config.now ?? Date.now;
|
|
@@ -259,12 +290,15 @@ function createInboundPipeline(deps, config) {
|
|
|
259
290
|
}
|
|
260
291
|
const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
|
|
261
292
|
const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
|
|
262
|
-
const
|
|
293
|
+
const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
|
|
294
|
+
const priorTurns = config.history && historyEnabled ? await config.history.store.load(
|
|
263
295
|
conversationId,
|
|
264
296
|
config.history.limit ?? DEFAULT_HISTORY_LIMIT
|
|
265
297
|
) : [];
|
|
266
298
|
const messages = [...priorTurns, { role: "user", content: msg.text }];
|
|
267
|
-
|
|
299
|
+
if (historyEnabled) {
|
|
300
|
+
await config.history?.store.append(conversationId, { role: "user", content: msg.text });
|
|
301
|
+
}
|
|
268
302
|
const { system } = await prepareChat({
|
|
269
303
|
store: deps.store,
|
|
270
304
|
prompts: deps.prompts,
|
|
@@ -272,7 +306,11 @@ function createInboundPipeline(deps, config) {
|
|
|
272
306
|
messages,
|
|
273
307
|
personaLayer,
|
|
274
308
|
channelHint: config.channelHint,
|
|
275
|
-
buckets
|
|
309
|
+
buckets,
|
|
310
|
+
sender,
|
|
311
|
+
rewriteQuery: config.rewriteQuery,
|
|
312
|
+
rerankContext: config.rerankContext,
|
|
313
|
+
logger: config.logger
|
|
276
314
|
});
|
|
277
315
|
const { content: produced } = await answerOnce({
|
|
278
316
|
answerFn: config.answerFn,
|
|
@@ -292,7 +330,9 @@ function createInboundPipeline(deps, config) {
|
|
|
292
330
|
if (!content) {
|
|
293
331
|
return { action: "ignore" };
|
|
294
332
|
}
|
|
295
|
-
|
|
333
|
+
if (historyEnabled) {
|
|
334
|
+
await config.history?.store.append(conversationId, { role: "assistant", content });
|
|
335
|
+
}
|
|
296
336
|
await turn.reply.sendAnswer(msg.chatId, content);
|
|
297
337
|
return { action: "reply", content };
|
|
298
338
|
};
|
|
@@ -17,6 +17,7 @@ import type OpenAI from "openai";
|
|
|
17
17
|
import type { AnswerFn, TransformReply } from "../core/answer";
|
|
18
18
|
import type { BucketsFor } from "../core/buckets";
|
|
19
19
|
import type { Logger } from "../core/logger";
|
|
20
|
+
import { type RerankContext, type RewriteQuery } from "../core/pipeline";
|
|
20
21
|
import type { PromptLoader } from "../core/prompts";
|
|
21
22
|
import type { VectorStore } from "../core/retrieval";
|
|
22
23
|
import type { HistoryStore } from "../history/types";
|
|
@@ -41,6 +42,10 @@ export interface InboundPipelineConfig {
|
|
|
41
42
|
channel?: string;
|
|
42
43
|
answerFn?: AnswerFn;
|
|
43
44
|
bucketsFor?: BucketsFor;
|
|
45
|
+
/** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. */
|
|
46
|
+
rewriteQuery?: RewriteQuery;
|
|
47
|
+
/** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. */
|
|
48
|
+
rerankContext?: RerankContext;
|
|
44
49
|
/** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. */
|
|
45
50
|
transformReply?: TransformReply;
|
|
46
51
|
model?: string;
|
|
@@ -56,6 +61,14 @@ export interface InboundPipelineConfig {
|
|
|
56
61
|
store: HistoryStore;
|
|
57
62
|
/** Most recent turns to load per reply. @default 20 */
|
|
58
63
|
limit?: number;
|
|
64
|
+
/**
|
|
65
|
+
* Excludes a sender from history entirely — checked before both `load`
|
|
66
|
+
* and `append`, so an opted-out sender's turns are never read back and
|
|
67
|
+
* never written. A throw/rejection fails closed ("not enabled"): a
|
|
68
|
+
* broken predicate must never start recording a sender who may have
|
|
69
|
+
* opted out. @default every sender is enabled
|
|
70
|
+
*/
|
|
71
|
+
historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
|
|
59
72
|
};
|
|
60
73
|
/** Group chats eligible for a reply. Empty (default) = every group. Has no effect on DMs, which always reply. */
|
|
61
74
|
allowedChats?: string[];
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/channels/pipeline.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;GAcG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AACjC,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,gBAAgB,CAAC;AAE/D,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,iBAAiB,CAAC;AAElD,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,gBAAgB,CAAC;
|
|
1
|
+
{"version":3,"file":"pipeline.d.ts","sourceRoot":"","sources":["../../src/channels/pipeline.ts"],"names":[],"mappings":"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"}
|
|
@@ -20,6 +20,7 @@
|
|
|
20
20
|
import type { AnswerFn, TransformReply } from "../../core/answer";
|
|
21
21
|
import type { BucketsFor } from "../../core/buckets";
|
|
22
22
|
import { type Logger } from "../../core/logger";
|
|
23
|
+
import type { RerankContext, RewriteQuery } from "../../core/pipeline";
|
|
23
24
|
import type { HistoryStore } from "../../history/types";
|
|
24
25
|
import type { Channel } from "../index";
|
|
25
26
|
import { type TelegramApi } from "./api";
|
|
@@ -32,6 +33,10 @@ export interface TelegramChannelConfig {
|
|
|
32
33
|
allowedChats?: string[];
|
|
33
34
|
answerFn?: AnswerFn;
|
|
34
35
|
bucketsFor?: BucketsFor;
|
|
36
|
+
/** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. Falls back to the server's own. */
|
|
37
|
+
rewriteQuery?: RewriteQuery;
|
|
38
|
+
/** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. Falls back to the server's own. */
|
|
39
|
+
rerankContext?: RerankContext;
|
|
35
40
|
/** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. Falls back to the server's own. */
|
|
36
41
|
transformReply?: TransformReply;
|
|
37
42
|
model?: string;
|
|
@@ -45,7 +50,14 @@ export interface TelegramChannelConfig {
|
|
|
45
50
|
/** Off by default — the channel stays single-turn until a store is configured. */
|
|
46
51
|
history?: {
|
|
47
52
|
store: HistoryStore;
|
|
53
|
+
/** Most recent turns to load per reply. @default 20 */
|
|
48
54
|
limit?: number;
|
|
55
|
+
/**
|
|
56
|
+
* Excludes a sender from history entirely — see
|
|
57
|
+
* `InboundPipelineConfig.history.historyEnabledFor` in `./channels`.
|
|
58
|
+
* @default every sender is enabled
|
|
59
|
+
*/
|
|
60
|
+
historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
|
|
49
61
|
};
|
|
50
62
|
muteRegex?: RegExp;
|
|
51
63
|
unmuteRegex?: RegExp;
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAqB,KAAK,WAAW,EAAuB,MAAM,OAAO,CAAC;AAOjF,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,kIAAkI;IAClI,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kIAAkI;IAClI,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kFAAkF;IAClF,OAAO,CAAC,EAAE;
|
|
1
|
+
{"version":3,"file":"channel.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/channel.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAEH,OAAO,KAAK,EAAE,QAAQ,EAAE,cAAc,EAAE,MAAM,mBAAmB,CAAC;AAClE,OAAO,KAAK,EAAE,UAAU,EAAE,MAAM,oBAAoB,CAAC;AACrD,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,KAAK,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AACvE,OAAO,KAAK,EAAE,YAAY,EAAE,MAAM,qBAAqB,CAAC;AAExD,OAAO,KAAK,EAAE,OAAO,EAAE,MAAM,UAAU,CAAC;AAGxC,OAAO,EAAqB,KAAK,WAAW,EAAuB,MAAM,OAAO,CAAC;AAOjF,MAAM,WAAW,qBAAqB;IACpC,qFAAqF;IACrF,QAAQ,EAAE,MAAM,CAAC;IACjB,8GAA8G;IAC9G,IAAI,CAAC,EAAE,MAAM,CAAC;IACd,iHAAiH;IACjH,YAAY,CAAC,EAAE,MAAM,EAAE,CAAC;IACxB,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,UAAU,CAAC,EAAE,UAAU,CAAC;IACxB,0IAA0I;IAC1I,YAAY,CAAC,EAAE,YAAY,CAAC;IAC5B,iJAAiJ;IACjJ,aAAa,CAAC,EAAE,aAAa,CAAC;IAC9B,kIAAkI;IAClI,cAAc,CAAC,EAAE,cAAc,CAAC;IAChC,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,kIAAkI;IAClI,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,4GAA4G;IAC5G,eAAe,CAAC,EAAE,CAAC,GAAG,EAAE;QACtB,MAAM,EAAE,MAAM,CAAC;QACf,IAAI,EAAE,MAAM,CAAC;KACd,KAAK,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAAC;IACvD,kFAAkF;IAClF,OAAO,CAAC,EAAE;QACR,KAAK,EAAE,YAAY,CAAC;QACpB,uDAAuD;QACvD,KAAK,CAAC,EAAE,MAAM,CAAC;QACf;;;;WAIG;QACH,iBAAiB,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,OAAO,GAAG,OAAO,CAAC,OAAO,CAAC,CAAC;KACpE,CAAC;IACF,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,gHAAgH;IAChH,SAAS,CAAC,EAAE,MAAM,CAAC;IACnB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,WAAW,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IAChD,cAAc,CAAC,EAAE;QAAE,GAAG,EAAE,MAAM,CAAC;QAAC,QAAQ,EAAE,MAAM,CAAA;KAAE,CAAC;IACnD,kBAAkB;IAClB,kBAAkB,CAAC,EAAE,MAAM,CAAC;IAC5B,0GAA0G;IAC1G,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,iGAAiG;IACjG,QAAQ,CAAC,EAAE,CAAC,MAAM,EAAE,MAAM,KAAK,IAAI,CAAC;IACpC,mGAAmG;IACnG,KAAK,CAAC,EAAE,OAAO,KAAK,CAAC;IACrB,yCAAyC;IACzC,UAAU,CAAC,EAAE,MAAM,CAAC;IACpB,qJAAqJ;IACrJ,GAAG,CAAC,EAAE,WAAW,CAAC;IAClB,qEAAqE;IACrE,KAAK,CAAC,EAAE,CAAC,EAAE,EAAE,MAAM,KAAK,OAAO,CAAC,IAAI,CAAC,CAAC;IACtC,GAAG,CAAC,EAAE,MAAM,MAAM,CAAC;IACnB,uGAAuG;IACvG,MAAM,CAAC,EAAE,MAAM,CAAC;CACjB;AASD,wBAAgB,qBAAqB,CAAC,MAAM,EAAE,qBAAqB,GAAG,OAAO,CAoI5E"}
|
|
@@ -388,14 +388,35 @@ async function prepareChat({
|
|
|
388
388
|
messages,
|
|
389
389
|
personaLayer,
|
|
390
390
|
channelHint,
|
|
391
|
-
buckets
|
|
391
|
+
buckets,
|
|
392
|
+
sender,
|
|
393
|
+
rewriteQuery,
|
|
394
|
+
rerankContext,
|
|
395
|
+
logger
|
|
392
396
|
}) {
|
|
393
397
|
const lastUserMsg = lastUserMessage(messages);
|
|
394
398
|
if (!lastUserMsg) {
|
|
395
399
|
throw new Error("no user message found in conversation");
|
|
396
400
|
}
|
|
397
401
|
const { topK, label } = MODE_SETTINGS[mode];
|
|
398
|
-
|
|
402
|
+
let query = lastUserMsg.content;
|
|
403
|
+
if (rewriteQuery) {
|
|
404
|
+
try {
|
|
405
|
+
const rewritten = await rewriteQuery({ query, mode, sender });
|
|
406
|
+
if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
|
|
407
|
+
} catch (error) {
|
|
408
|
+
logger?.error("rewriteQuery threw; falling back to the original query", error);
|
|
409
|
+
}
|
|
410
|
+
}
|
|
411
|
+
let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
|
|
412
|
+
if (rerankContext) {
|
|
413
|
+
try {
|
|
414
|
+
const reranked = await rerankContext({ query, chunks: ctx });
|
|
415
|
+
if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
|
|
416
|
+
} catch (error) {
|
|
417
|
+
logger?.error("rerankContext threw; falling back to the original chunks", error);
|
|
418
|
+
}
|
|
419
|
+
}
|
|
399
420
|
const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
|
|
400
421
|
const hint = channelHint?.trim();
|
|
401
422
|
const system = [
|
|
@@ -420,6 +441,16 @@ async function resolvePersonaLayer(config, ctx) {
|
|
|
420
441
|
return void 0;
|
|
421
442
|
}
|
|
422
443
|
}
|
|
444
|
+
async function isHistoryEnabled(config, sender) {
|
|
445
|
+
const predicate = config.history?.historyEnabledFor;
|
|
446
|
+
if (!predicate) return true;
|
|
447
|
+
try {
|
|
448
|
+
return await predicate(sender);
|
|
449
|
+
} catch (error) {
|
|
450
|
+
config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
|
|
451
|
+
return false;
|
|
452
|
+
}
|
|
453
|
+
}
|
|
423
454
|
function createInboundPipeline(deps, config) {
|
|
424
455
|
const mutedChats = /* @__PURE__ */ new Set();
|
|
425
456
|
const now = config.now ?? Date.now;
|
|
@@ -459,12 +490,15 @@ function createInboundPipeline(deps, config) {
|
|
|
459
490
|
}
|
|
460
491
|
const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
|
|
461
492
|
const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
|
|
462
|
-
const
|
|
493
|
+
const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
|
|
494
|
+
const priorTurns = config.history && historyEnabled ? await config.history.store.load(
|
|
463
495
|
conversationId,
|
|
464
496
|
config.history.limit ?? DEFAULT_HISTORY_LIMIT
|
|
465
497
|
) : [];
|
|
466
498
|
const messages = [...priorTurns, { role: "user", content: msg.text }];
|
|
467
|
-
|
|
499
|
+
if (historyEnabled) {
|
|
500
|
+
await config.history?.store.append(conversationId, { role: "user", content: msg.text });
|
|
501
|
+
}
|
|
468
502
|
const { system } = await prepareChat({
|
|
469
503
|
store: deps.store,
|
|
470
504
|
prompts: deps.prompts,
|
|
@@ -472,7 +506,11 @@ function createInboundPipeline(deps, config) {
|
|
|
472
506
|
messages,
|
|
473
507
|
personaLayer,
|
|
474
508
|
channelHint: config.channelHint,
|
|
475
|
-
buckets
|
|
509
|
+
buckets,
|
|
510
|
+
sender,
|
|
511
|
+
rewriteQuery: config.rewriteQuery,
|
|
512
|
+
rerankContext: config.rerankContext,
|
|
513
|
+
logger: config.logger
|
|
476
514
|
});
|
|
477
515
|
const { content: produced } = await answerOnce({
|
|
478
516
|
answerFn: config.answerFn,
|
|
@@ -492,7 +530,9 @@ function createInboundPipeline(deps, config) {
|
|
|
492
530
|
if (!content) {
|
|
493
531
|
return { action: "ignore" };
|
|
494
532
|
}
|
|
495
|
-
|
|
533
|
+
if (historyEnabled) {
|
|
534
|
+
await config.history?.store.append(conversationId, { role: "assistant", content });
|
|
535
|
+
}
|
|
496
536
|
await turn.reply.sendAnswer(msg.chatId, content);
|
|
497
537
|
return { action: "reply", content };
|
|
498
538
|
};
|
|
@@ -625,6 +665,8 @@ function createTelegramChannel(config) {
|
|
|
625
665
|
channel: channelName,
|
|
626
666
|
answerFn: config.answerFn ?? deps.config.answerFn,
|
|
627
667
|
bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
|
|
668
|
+
rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
|
|
669
|
+
rerankContext: config.rerankContext ?? deps.config.rerankContext,
|
|
628
670
|
transformReply: config.transformReply ?? deps.config.transformReply,
|
|
629
671
|
model: config.model,
|
|
630
672
|
channelHint: config.channelHint ?? "Channel: Telegram.",
|
|
@@ -346,14 +346,35 @@ async function prepareChat({
|
|
|
346
346
|
messages,
|
|
347
347
|
personaLayer,
|
|
348
348
|
channelHint,
|
|
349
|
-
buckets
|
|
349
|
+
buckets,
|
|
350
|
+
sender,
|
|
351
|
+
rewriteQuery,
|
|
352
|
+
rerankContext,
|
|
353
|
+
logger
|
|
350
354
|
}) {
|
|
351
355
|
const lastUserMsg = lastUserMessage(messages);
|
|
352
356
|
if (!lastUserMsg) {
|
|
353
357
|
throw new Error("no user message found in conversation");
|
|
354
358
|
}
|
|
355
359
|
const { topK, label } = MODE_SETTINGS[mode];
|
|
356
|
-
|
|
360
|
+
let query = lastUserMsg.content;
|
|
361
|
+
if (rewriteQuery) {
|
|
362
|
+
try {
|
|
363
|
+
const rewritten = await rewriteQuery({ query, mode, sender });
|
|
364
|
+
if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
|
|
365
|
+
} catch (error) {
|
|
366
|
+
logger?.error("rewriteQuery threw; falling back to the original query", error);
|
|
367
|
+
}
|
|
368
|
+
}
|
|
369
|
+
let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
|
|
370
|
+
if (rerankContext) {
|
|
371
|
+
try {
|
|
372
|
+
const reranked = await rerankContext({ query, chunks: ctx });
|
|
373
|
+
if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
|
|
374
|
+
} catch (error) {
|
|
375
|
+
logger?.error("rerankContext threw; falling back to the original chunks", error);
|
|
376
|
+
}
|
|
377
|
+
}
|
|
357
378
|
const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
|
|
358
379
|
const hint = channelHint?.trim();
|
|
359
380
|
const system = [
|
|
@@ -378,6 +399,16 @@ async function resolvePersonaLayer(config, ctx) {
|
|
|
378
399
|
return void 0;
|
|
379
400
|
}
|
|
380
401
|
}
|
|
402
|
+
async function isHistoryEnabled(config, sender) {
|
|
403
|
+
const predicate = config.history?.historyEnabledFor;
|
|
404
|
+
if (!predicate) return true;
|
|
405
|
+
try {
|
|
406
|
+
return await predicate(sender);
|
|
407
|
+
} catch (error) {
|
|
408
|
+
config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
|
|
409
|
+
return false;
|
|
410
|
+
}
|
|
411
|
+
}
|
|
381
412
|
function createInboundPipeline(deps, config) {
|
|
382
413
|
const mutedChats = /* @__PURE__ */ new Set();
|
|
383
414
|
const now = config.now ?? Date.now;
|
|
@@ -417,12 +448,15 @@ function createInboundPipeline(deps, config) {
|
|
|
417
448
|
}
|
|
418
449
|
const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
|
|
419
450
|
const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
|
|
420
|
-
const
|
|
451
|
+
const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
|
|
452
|
+
const priorTurns = config.history && historyEnabled ? await config.history.store.load(
|
|
421
453
|
conversationId,
|
|
422
454
|
config.history.limit ?? DEFAULT_HISTORY_LIMIT
|
|
423
455
|
) : [];
|
|
424
456
|
const messages = [...priorTurns, { role: "user", content: msg.text }];
|
|
425
|
-
|
|
457
|
+
if (historyEnabled) {
|
|
458
|
+
await config.history?.store.append(conversationId, { role: "user", content: msg.text });
|
|
459
|
+
}
|
|
426
460
|
const { system } = await prepareChat({
|
|
427
461
|
store: deps.store,
|
|
428
462
|
prompts: deps.prompts,
|
|
@@ -430,7 +464,11 @@ function createInboundPipeline(deps, config) {
|
|
|
430
464
|
messages,
|
|
431
465
|
personaLayer,
|
|
432
466
|
channelHint: config.channelHint,
|
|
433
|
-
buckets
|
|
467
|
+
buckets,
|
|
468
|
+
sender,
|
|
469
|
+
rewriteQuery: config.rewriteQuery,
|
|
470
|
+
rerankContext: config.rerankContext,
|
|
471
|
+
logger: config.logger
|
|
434
472
|
});
|
|
435
473
|
const { content: produced } = await answerOnce({
|
|
436
474
|
answerFn: config.answerFn,
|
|
@@ -450,7 +488,9 @@ function createInboundPipeline(deps, config) {
|
|
|
450
488
|
if (!content) {
|
|
451
489
|
return { action: "ignore" };
|
|
452
490
|
}
|
|
453
|
-
|
|
491
|
+
if (historyEnabled) {
|
|
492
|
+
await config.history?.store.append(conversationId, { role: "assistant", content });
|
|
493
|
+
}
|
|
454
494
|
await turn.reply.sendAnswer(msg.chatId, content);
|
|
455
495
|
return { action: "reply", content };
|
|
456
496
|
};
|
|
@@ -583,6 +623,8 @@ function createTelegramChannel(config) {
|
|
|
583
623
|
channel: channelName,
|
|
584
624
|
answerFn: config.answerFn ?? deps.config.answerFn,
|
|
585
625
|
bucketsFor: config.bucketsFor ?? deps.config.bucketsFor,
|
|
626
|
+
rewriteQuery: config.rewriteQuery ?? deps.config.rewriteQuery,
|
|
627
|
+
rerankContext: config.rerankContext ?? deps.config.rerankContext,
|
|
586
628
|
transformReply: config.transformReply ?? deps.config.transformReply,
|
|
587
629
|
model: config.model,
|
|
588
630
|
channelHint: config.channelHint ?? "Channel: Telegram.",
|
|
@@ -36,6 +36,8 @@
|
|
|
36
36
|
* prompts: deps.prompts,
|
|
37
37
|
* answerFn: deps.config.answerFn,
|
|
38
38
|
* bucketsFor: deps.config.bucketsFor,
|
|
39
|
+
* rewriteQuery: deps.config.rewriteQuery,
|
|
40
|
+
* rerankContext: deps.config.rerankContext,
|
|
39
41
|
* transformReply: deps.config.transformReply,
|
|
40
42
|
* registry: new Map(),
|
|
41
43
|
* logger: deps.logger,
|
|
@@ -49,6 +51,7 @@ import type OpenAI from "openai";
|
|
|
49
51
|
import type { AnswerFn, TransformReply } from "../../core/answer";
|
|
50
52
|
import type { BucketsFor } from "../../core/buckets";
|
|
51
53
|
import { type Logger } from "../../core/logger";
|
|
54
|
+
import type { RerankContext, RewriteQuery } from "../../core/pipeline";
|
|
52
55
|
import type { PromptLoader } from "../../core/prompts";
|
|
53
56
|
import type { VectorStore } from "../../core/retrieval";
|
|
54
57
|
import type { HistoryStore } from "../../history/types";
|
|
@@ -129,6 +132,10 @@ export interface WhatsAppInboundConfig {
|
|
|
129
132
|
prompts: PromptLoader;
|
|
130
133
|
answerFn?: AnswerFn;
|
|
131
134
|
bucketsFor?: BucketsFor;
|
|
135
|
+
/** Rewrites the retrieval query before it reaches the vector store — see `ChatterConfig.rewriteQuery`. */
|
|
136
|
+
rewriteQuery?: RewriteQuery;
|
|
137
|
+
/** Post-processes retrieved chunks before they're folded into the prompt — see `ChatterConfig.rerankContext`. */
|
|
138
|
+
rerankContext?: RerankContext;
|
|
132
139
|
/** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. */
|
|
133
140
|
transformReply?: TransformReply;
|
|
134
141
|
model?: string;
|
|
@@ -186,6 +193,12 @@ export interface WhatsAppInboundConfig {
|
|
|
186
193
|
store: HistoryStore;
|
|
187
194
|
/** Most recent turns to load per reply. @default 20 */
|
|
188
195
|
limit?: number;
|
|
196
|
+
/**
|
|
197
|
+
* Excludes a sender from history entirely — see
|
|
198
|
+
* `InboundPipelineConfig.history.historyEnabledFor` in `./channels`.
|
|
199
|
+
* @default every sender is enabled
|
|
200
|
+
*/
|
|
201
|
+
historyEnabledFor?: (sender: string) => boolean | Promise<boolean>;
|
|
189
202
|
};
|
|
190
203
|
now?: () => number;
|
|
191
204
|
/** Logger for gate/dispatch diagnostics. Default: a console logger writing to stderr. */
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"AAAA
|
|
1
|
+
{"version":3,"file":"inbound.d.ts","sourceRoot":"","sources":["../../../src/channels/whatsapp/inbound.ts"],"names":[],"mappings":"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"}
|
|
@@ -799,14 +799,35 @@ async function prepareChat({
|
|
|
799
799
|
messages,
|
|
800
800
|
personaLayer,
|
|
801
801
|
channelHint,
|
|
802
|
-
buckets
|
|
802
|
+
buckets,
|
|
803
|
+
sender,
|
|
804
|
+
rewriteQuery,
|
|
805
|
+
rerankContext,
|
|
806
|
+
logger
|
|
803
807
|
}) {
|
|
804
808
|
const lastUserMsg = lastUserMessage(messages);
|
|
805
809
|
if (!lastUserMsg) {
|
|
806
810
|
throw new Error("no user message found in conversation");
|
|
807
811
|
}
|
|
808
812
|
const { topK, label } = MODE_SETTINGS[mode];
|
|
809
|
-
|
|
813
|
+
let query = lastUserMsg.content;
|
|
814
|
+
if (rewriteQuery) {
|
|
815
|
+
try {
|
|
816
|
+
const rewritten = await rewriteQuery({ query, mode, sender });
|
|
817
|
+
if (typeof rewritten === "string" && rewritten.trim()) query = rewritten;
|
|
818
|
+
} catch (error) {
|
|
819
|
+
logger?.error("rewriteQuery threw; falling back to the original query", error);
|
|
820
|
+
}
|
|
821
|
+
}
|
|
822
|
+
let ctx = await store.query(query, topK, buckets ?? defaultBuckets(mode));
|
|
823
|
+
if (rerankContext) {
|
|
824
|
+
try {
|
|
825
|
+
const reranked = await rerankContext({ query, chunks: ctx });
|
|
826
|
+
if (Array.isArray(reranked) && reranked.every((c) => typeof c === "string")) ctx = reranked;
|
|
827
|
+
} catch (error) {
|
|
828
|
+
logger?.error("rerankContext threw; falling back to the original chunks", error);
|
|
829
|
+
}
|
|
830
|
+
}
|
|
810
831
|
const persona = personaLayer?.trim() || (mode === "private" ? prompts.privatePersona : prompts.publicPersona);
|
|
811
832
|
const hint = channelHint?.trim();
|
|
812
833
|
const system = [
|
|
@@ -831,6 +852,16 @@ async function resolvePersonaLayer(config, ctx) {
|
|
|
831
852
|
return void 0;
|
|
832
853
|
}
|
|
833
854
|
}
|
|
855
|
+
async function isHistoryEnabled(config, sender) {
|
|
856
|
+
const predicate = config.history?.historyEnabledFor;
|
|
857
|
+
if (!predicate) return true;
|
|
858
|
+
try {
|
|
859
|
+
return await predicate(sender);
|
|
860
|
+
} catch (error) {
|
|
861
|
+
config.logger?.error("historyEnabledFor threw; disabling history for this turn", error);
|
|
862
|
+
return false;
|
|
863
|
+
}
|
|
864
|
+
}
|
|
834
865
|
function createInboundPipeline(deps, config) {
|
|
835
866
|
const mutedChats = /* @__PURE__ */ new Set();
|
|
836
867
|
const now = config.now ?? Date.now;
|
|
@@ -870,12 +901,15 @@ function createInboundPipeline(deps, config) {
|
|
|
870
901
|
}
|
|
871
902
|
const personaLayer = await resolvePersonaLayer(config, { sender, text: msg.text });
|
|
872
903
|
const buckets = await resolveBuckets({ mode, sender, bucketsFor: config.bucketsFor });
|
|
873
|
-
const
|
|
904
|
+
const historyEnabled = config.history ? await isHistoryEnabled(config, sender) : false;
|
|
905
|
+
const priorTurns = config.history && historyEnabled ? await config.history.store.load(
|
|
874
906
|
conversationId,
|
|
875
907
|
config.history.limit ?? DEFAULT_HISTORY_LIMIT
|
|
876
908
|
) : [];
|
|
877
909
|
const messages = [...priorTurns, { role: "user", content: msg.text }];
|
|
878
|
-
|
|
910
|
+
if (historyEnabled) {
|
|
911
|
+
await config.history?.store.append(conversationId, { role: "user", content: msg.text });
|
|
912
|
+
}
|
|
879
913
|
const { system } = await prepareChat({
|
|
880
914
|
store: deps.store,
|
|
881
915
|
prompts: deps.prompts,
|
|
@@ -883,7 +917,11 @@ function createInboundPipeline(deps, config) {
|
|
|
883
917
|
messages,
|
|
884
918
|
personaLayer,
|
|
885
919
|
channelHint: config.channelHint,
|
|
886
|
-
buckets
|
|
920
|
+
buckets,
|
|
921
|
+
sender,
|
|
922
|
+
rewriteQuery: config.rewriteQuery,
|
|
923
|
+
rerankContext: config.rerankContext,
|
|
924
|
+
logger: config.logger
|
|
887
925
|
});
|
|
888
926
|
const { content: produced } = await answerOnce({
|
|
889
927
|
answerFn: config.answerFn,
|
|
@@ -903,7 +941,9 @@ function createInboundPipeline(deps, config) {
|
|
|
903
941
|
if (!content) {
|
|
904
942
|
return { action: "ignore" };
|
|
905
943
|
}
|
|
906
|
-
|
|
944
|
+
if (historyEnabled) {
|
|
945
|
+
await config.history?.store.append(conversationId, { role: "assistant", content });
|
|
946
|
+
}
|
|
907
947
|
await turn.reply.sendAnswer(msg.chatId, content);
|
|
908
948
|
return { action: "reply", content };
|
|
909
949
|
};
|
|
@@ -1010,6 +1050,8 @@ function createWhatsAppInboundHandler(config) {
|
|
|
1010
1050
|
channel: "whatsapp",
|
|
1011
1051
|
answerFn: config.answerFn,
|
|
1012
1052
|
bucketsFor: config.bucketsFor,
|
|
1053
|
+
rewriteQuery: config.rewriteQuery,
|
|
1054
|
+
rerankContext: config.rerankContext,
|
|
1013
1055
|
transformReply: config.transformReply,
|
|
1014
1056
|
model: config.model,
|
|
1015
1057
|
channelHint: config.channelHint,
|