@diegoaltoworks/chatter 0.54.2 → 0.56.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 +2 -1
- package/dist/channels/index.js +18 -1
- package/dist/channels/index.mjs +18 -1
- package/dist/channels/pipeline.d.ts +11 -1
- package/dist/channels/pipeline.d.ts.map +1 -1
- package/dist/channels/telegram/api.d.ts +122 -0
- package/dist/channels/telegram/api.d.ts.map +1 -0
- package/dist/channels/telegram/channel.d.ts +82 -0
- package/dist/channels/telegram/channel.d.ts.map +1 -0
- package/dist/channels/telegram/index.d.ts +26 -0
- package/dist/channels/telegram/index.d.ts.map +1 -0
- package/dist/channels/telegram/index.js +710 -0
- package/dist/channels/telegram/index.mjs +667 -0
- package/dist/channels/telegram/poll.d.ts +47 -0
- package/dist/channels/telegram/poll.d.ts.map +1 -0
- package/dist/channels/telegram/updates.d.ts +63 -0
- package/dist/channels/telegram/updates.d.ts.map +1 -0
- package/dist/channels/whatsapp/inbound.d.ts +4 -1
- package/dist/channels/whatsapp/inbound.d.ts.map +1 -1
- package/dist/channels/whatsapp/index.js +22 -2
- package/dist/channels/whatsapp/index.mjs +22 -2
- package/dist/core/answer.d.ts +31 -0
- package/dist/core/answer.d.ts.map +1 -1
- package/dist/index.d.ts +2 -2
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +54 -10
- package/dist/index.mjs +53 -10
- package/dist/mcp-server.d.ts.map +1 -1
- package/dist/mcp-server.js +26 -4
- package/dist/mcp-server.mjs +26 -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 +38 -6
- package/dist/server.mjs +38 -6
- package/dist/types.d.ts +17 -1
- package/dist/types.d.ts.map +1 -1
- package/package.json +8 -2
|
@@ -0,0 +1,47 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* The getUpdates long-poll loop, isolated from both the HTTP client and the
|
|
3
|
+
* channel so its failure behaviour is testable without either.
|
|
4
|
+
*
|
|
5
|
+
* The property that matters: a Bot API that is down, rate-limiting, or
|
|
6
|
+
* rejecting the token must never turn into a tight retry loop. Every failure
|
|
7
|
+
* backs off exponentially, and Telegram's own `retry_after` wins over that
|
|
8
|
+
* backoff when it sends one.
|
|
9
|
+
*/
|
|
10
|
+
import { type Logger } from "../../core/logger";
|
|
11
|
+
import { type TelegramUpdate } from "./api";
|
|
12
|
+
/** Exponential backoff: 2s, 4s, 8s ... capped at a minute. `failures` is the count including the one just seen. */
|
|
13
|
+
export declare function pollBackoffMs(failures: number): number;
|
|
14
|
+
/**
|
|
15
|
+
* How long to wait after a failed poll. A `retry_after` from Telegram (HTTP
|
|
16
|
+
* 429 flood control) is an instruction, not a hint — honour it verbatim even
|
|
17
|
+
* when it exceeds the backoff cap, because polling again sooner just extends
|
|
18
|
+
* the flood-wait.
|
|
19
|
+
*/
|
|
20
|
+
export declare function retryDelayMs(error: unknown, failures: number): number;
|
|
21
|
+
export interface LongPollDeps {
|
|
22
|
+
/** One `getUpdates` call. Rejections are backed off; they never end the loop. */
|
|
23
|
+
getUpdates(offset: number | undefined): Promise<TelegramUpdate[]>;
|
|
24
|
+
/** Per-update work. A throw is logged and skipped — one bad update never stops the loop. */
|
|
25
|
+
handleUpdate(update: TelegramUpdate): Promise<void>;
|
|
26
|
+
/** Checked before every poll, and again after a failure, so `stop()` cuts the loop instead of sleeping out a backoff first. */
|
|
27
|
+
isStopped(): boolean;
|
|
28
|
+
/** Overridable for tests; a real channel passes a `setTimeout`-based sleep. */
|
|
29
|
+
sleep(ms: number): Promise<void>;
|
|
30
|
+
/** Where this loop's offset resumes from. Omitted = whatever Telegram has queued. */
|
|
31
|
+
initialOffset?: number;
|
|
32
|
+
/** Called with each newly acknowledged offset, for a host that persists it. */
|
|
33
|
+
onOffset?(offset: number): void;
|
|
34
|
+
logger?: Logger;
|
|
35
|
+
/** Prefix for log lines, e.g. `Telegram[mybot]`. */
|
|
36
|
+
label?: string;
|
|
37
|
+
}
|
|
38
|
+
/**
|
|
39
|
+
* Polls until `isStopped()`, resolving with the last offset reached (what a
|
|
40
|
+
* restart would resume from).
|
|
41
|
+
*
|
|
42
|
+
* The offset advances BEFORE the update is handled: a handler that throws on
|
|
43
|
+
* one specific update must not make Telegram redeliver it forever, which would
|
|
44
|
+
* wedge the loop on a single poison message and starve every later one.
|
|
45
|
+
*/
|
|
46
|
+
export declare function runLongPoll(deps: LongPollDeps): Promise<number | undefined>;
|
|
47
|
+
//# sourceMappingURL=poll.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"poll.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/poll.ts"],"names":[],"mappings":"AAAA;;;;;;;;GAQG;AAEH,OAAO,EAAuB,KAAK,MAAM,EAAE,MAAM,mBAAmB,CAAC;AACrE,OAAO,EAAoB,KAAK,cAAc,EAAE,MAAM,OAAO,CAAC;AAM9D,mHAAmH;AACnH,wBAAgB,aAAa,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAEtD;AAED;;;;;GAKG;AACH,wBAAgB,YAAY,CAAC,KAAK,EAAE,OAAO,EAAE,QAAQ,EAAE,MAAM,GAAG,MAAM,CAGrE;AAED,MAAM,WAAW,YAAY;IAC3B,iFAAiF;IACjF,UAAU,CAAC,MAAM,EAAE,MAAM,GAAG,SAAS,GAAG,OAAO,CAAC,cAAc,EAAE,CAAC,CAAC;IAClE,4FAA4F;IAC5F,YAAY,CAAC,MAAM,EAAE,cAAc,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACpD,+HAA+H;IAC/H,SAAS,IAAI,OAAO,CAAC;IACrB,+EAA+E;IAC/E,KAAK,CAAC,EAAE,EAAE,MAAM,GAAG,OAAO,CAAC,IAAI,CAAC,CAAC;IACjC,qFAAqF;IACrF,aAAa,CAAC,EAAE,MAAM,CAAC;IACvB,+EAA+E;IAC/E,QAAQ,CAAC,CAAC,MAAM,EAAE,MAAM,GAAG,IAAI,CAAC;IAChC,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,oDAAoD;IACpD,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAED;;;;;;;GAOG;AACH,wBAAsB,WAAW,CAAC,IAAI,EAAE,YAAY,GAAG,OAAO,CAAC,MAAM,GAAG,SAAS,CAAC,CAmCjF"}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Telegram update -> {@link ChannelMessage}: the transport's own half of the
|
|
3
|
+
* channel contract (see docs/build-a-channel.md), kept pure so mention and
|
|
4
|
+
* reply-to detection are testable without a network or a running channel.
|
|
5
|
+
*
|
|
6
|
+
* Addressing parity with WhatsApp is the point of this module. WhatsApp
|
|
7
|
+
* resolves "was the bot addressed?" from `contextInfo.mentionedJid` and the
|
|
8
|
+
* quoted participant; Telegram resolves it from message entities and
|
|
9
|
+
* `reply_to_message.from`. The gate policy that consumes those booleans
|
|
10
|
+
* (`decideChannelAction`) is identical for both.
|
|
11
|
+
*/
|
|
12
|
+
import type { ChannelMessage } from "../gates";
|
|
13
|
+
import type { TelegramMessage, TelegramMessageEntity, TelegramUpdate } from "./api";
|
|
14
|
+
/** The bot's own identity, as `getMe` reports it. */
|
|
15
|
+
export interface TelegramBotIdentity {
|
|
16
|
+
id: number;
|
|
17
|
+
username?: string;
|
|
18
|
+
}
|
|
19
|
+
/** The message's text: plain text, or a photo/video/document caption. */
|
|
20
|
+
export declare function messageText(message: TelegramMessage): string;
|
|
21
|
+
/** Entities belong to whichever field carried the text — captions carry theirs on `caption_entities`. */
|
|
22
|
+
export declare function messageEntities(message: TelegramMessage): TelegramMessageEntity[];
|
|
23
|
+
/**
|
|
24
|
+
* Did this message address the bot? Three ways it can, all of which a human
|
|
25
|
+
* reader would call "addressed":
|
|
26
|
+
*
|
|
27
|
+
* - a `mention` entity whose text is the bot's `@username`;
|
|
28
|
+
* - a `text_mention` entity pointing at the bot's user id (how a mention
|
|
29
|
+
* arrives when the mentioned account has no username);
|
|
30
|
+
* - a `bot_command` entity suffixed with the bot's username (`/ask@mybot`),
|
|
31
|
+
* which is how Telegram disambiguates commands between several bots in one
|
|
32
|
+
* group.
|
|
33
|
+
*
|
|
34
|
+
* Username comparison is case-insensitive: Telegram treats `@MyBot` and
|
|
35
|
+
* `@mybot` as the same handle, and clients preserve whatever the sender typed.
|
|
36
|
+
*/
|
|
37
|
+
export declare function mentionsBot(message: TelegramMessage, me: TelegramBotIdentity): boolean;
|
|
38
|
+
/**
|
|
39
|
+
* The {@link ChannelMessage} for an update, or `undefined` when there is
|
|
40
|
+
* nothing for a chat pipeline to look at: a non-message update, a channel post
|
|
41
|
+
* (which has no `from`, only a `sender_chat`), or a service message.
|
|
42
|
+
*
|
|
43
|
+
* A message with no text at all (a sticker, an uncaptioned photo) still maps —
|
|
44
|
+
* its blank text is dropped by `decideChannelAction` like any other, and
|
|
45
|
+
* mapping it keeps this function's rule simple: interpret the wire, let the
|
|
46
|
+
* gates decide policy.
|
|
47
|
+
*/
|
|
48
|
+
export declare function toChannelMessage(update: TelegramUpdate, me: TelegramBotIdentity): ChannelMessage | undefined;
|
|
49
|
+
/**
|
|
50
|
+
* The `offset` to request next so `update` is never redelivered:
|
|
51
|
+
* `update_id + 1`, which is also how the Bot API acknowledges it (there is no
|
|
52
|
+
* separate ack call — see docs/telegram.md on offset persistence).
|
|
53
|
+
*/
|
|
54
|
+
export declare function nextOffset(update: TelegramUpdate): number;
|
|
55
|
+
/**
|
|
56
|
+
* A stable identity key for `bucketsFor`/`personaResolver`/`answerFn`. Namespaced
|
|
57
|
+
* because a bare Telegram user id is a numeral with no channel in it — a host
|
|
58
|
+
* keying persona or spend state across several channels must not have one
|
|
59
|
+
* collide with a phone number or another transport's numeric id (the same
|
|
60
|
+
* reasoning as WhatsApp's explicit `lid:` marker).
|
|
61
|
+
*/
|
|
62
|
+
export declare function telegramSenderKey(senderId: string): string;
|
|
63
|
+
//# sourceMappingURL=updates.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"updates.d.ts","sourceRoot":"","sources":["../../../src/channels/telegram/updates.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;GAUG;AAEH,OAAO,KAAK,EAAE,cAAc,EAAE,MAAM,UAAU,CAAC;AAC/C,OAAO,KAAK,EAAE,eAAe,EAAE,qBAAqB,EAAE,cAAc,EAAE,MAAM,OAAO,CAAC;AAEpF,qDAAqD;AACrD,MAAM,WAAW,mBAAmB;IAClC,EAAE,EAAE,MAAM,CAAC;IACX,QAAQ,CAAC,EAAE,MAAM,CAAC;CACnB;AAED,yEAAyE;AACzE,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,GAAG,MAAM,CAE5D;AAED,yGAAyG;AACzG,wBAAgB,eAAe,CAAC,OAAO,EAAE,eAAe,GAAG,qBAAqB,EAAE,CAEjF;AAED;;;;;;;;;;;;;GAaG;AACH,wBAAgB,WAAW,CAAC,OAAO,EAAE,eAAe,EAAE,EAAE,EAAE,mBAAmB,GAAG,OAAO,CAYtF;AAED;;;;;;;;;GASG;AACH,wBAAgB,gBAAgB,CAC9B,MAAM,EAAE,cAAc,EACtB,EAAE,EAAE,mBAAmB,GACtB,cAAc,GAAG,SAAS,CAiB5B;AAED;;;;GAIG;AACH,wBAAgB,UAAU,CAAC,MAAM,EAAE,cAAc,GAAG,MAAM,CAEzD;AAED;;;;;;GAMG;AACH,wBAAgB,iBAAiB,CAAC,QAAQ,EAAE,MAAM,GAAG,MAAM,CAE1D"}
|
|
@@ -36,6 +36,7 @@
|
|
|
36
36
|
* prompts: deps.prompts,
|
|
37
37
|
* answerFn: deps.config.answerFn,
|
|
38
38
|
* bucketsFor: deps.config.bucketsFor,
|
|
39
|
+
* transformReply: deps.config.transformReply,
|
|
39
40
|
* registry: new Map(),
|
|
40
41
|
* logger: deps.logger,
|
|
41
42
|
* });
|
|
@@ -45,7 +46,7 @@
|
|
|
45
46
|
*/
|
|
46
47
|
import type { WAMessage, WASocket } from "@whiskeysockets/baileys";
|
|
47
48
|
import type OpenAI from "openai";
|
|
48
|
-
import type { AnswerFn } from "../../core/answer";
|
|
49
|
+
import type { AnswerFn, TransformReply } from "../../core/answer";
|
|
49
50
|
import type { BucketsFor } from "../../core/buckets";
|
|
50
51
|
import { type Logger } from "../../core/logger";
|
|
51
52
|
import type { PromptLoader } from "../../core/prompts";
|
|
@@ -128,6 +129,8 @@ export interface WhatsAppInboundConfig {
|
|
|
128
129
|
prompts: PromptLoader;
|
|
129
130
|
answerFn?: AnswerFn;
|
|
130
131
|
bucketsFor?: BucketsFor;
|
|
132
|
+
/** Modifies or vetoes the produced reply before delivery — see `ChatterConfig.transformReply`. */
|
|
133
|
+
transformReply?: TransformReply;
|
|
131
134
|
model?: string;
|
|
132
135
|
/**
|
|
133
136
|
* Own identities per session — share ONE registry across every session
|
|
@@ -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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"}
|
|
@@ -738,6 +738,18 @@ async function answerOnce({
|
|
|
738
738
|
}
|
|
739
739
|
return completeOnce({ client, system, messages, temperature, model });
|
|
740
740
|
}
|
|
741
|
+
async function applyTransformReply(transformReply, input, logger) {
|
|
742
|
+
if (!transformReply) return input.text;
|
|
743
|
+
try {
|
|
744
|
+
return await transformReply(input);
|
|
745
|
+
} catch (error) {
|
|
746
|
+
logger?.error(
|
|
747
|
+
`transformReply threw for channel "${input.channel}"; sending the original reply`,
|
|
748
|
+
error
|
|
749
|
+
);
|
|
750
|
+
return input.text;
|
|
751
|
+
}
|
|
752
|
+
}
|
|
741
753
|
|
|
742
754
|
// src/core/buckets.ts
|
|
743
755
|
function defaultBuckets(mode) {
|
|
@@ -873,7 +885,7 @@ function createInboundPipeline(deps, config) {
|
|
|
873
885
|
channelHint: config.channelHint,
|
|
874
886
|
buckets
|
|
875
887
|
});
|
|
876
|
-
const { content } = await answerOnce({
|
|
888
|
+
const { content: produced } = await answerOnce({
|
|
877
889
|
answerFn: config.answerFn,
|
|
878
890
|
client: deps.client,
|
|
879
891
|
system,
|
|
@@ -883,6 +895,11 @@ function createInboundPipeline(deps, config) {
|
|
|
883
895
|
conversationId,
|
|
884
896
|
model: config.model
|
|
885
897
|
});
|
|
898
|
+
const content = await applyTransformReply(
|
|
899
|
+
config.transformReply,
|
|
900
|
+
{ channel: config.channel ?? "channel", sender, conversationId, text: produced },
|
|
901
|
+
config.logger
|
|
902
|
+
);
|
|
886
903
|
if (!content) {
|
|
887
904
|
return { action: "ignore" };
|
|
888
905
|
}
|
|
@@ -990,8 +1007,10 @@ function createWhatsAppInboundHandler(config) {
|
|
|
990
1007
|
const pipeline = createInboundPipeline(
|
|
991
1008
|
{ client: config.client, store: config.store, prompts: config.prompts },
|
|
992
1009
|
{
|
|
1010
|
+
channel: "whatsapp",
|
|
993
1011
|
answerFn: config.answerFn,
|
|
994
1012
|
bucketsFor: config.bucketsFor,
|
|
1013
|
+
transformReply: config.transformReply,
|
|
995
1014
|
model: config.model,
|
|
996
1015
|
channelHint: config.channelHint,
|
|
997
1016
|
personaResolver: personaResolver ? ({ sender, text }) => personaResolver({ senderPhone: sender, text }) : void 0,
|
|
@@ -1003,7 +1022,8 @@ function createWhatsAppInboundHandler(config) {
|
|
|
1003
1022
|
unmuteReply: config.unmuteReply,
|
|
1004
1023
|
dmRateLimit: config.dmRateLimit,
|
|
1005
1024
|
groupRateLimit: config.groupRateLimit,
|
|
1006
|
-
now: config.now
|
|
1025
|
+
now: config.now,
|
|
1026
|
+
logger
|
|
1007
1027
|
}
|
|
1008
1028
|
);
|
|
1009
1029
|
return async function handleWhatsAppMessage(event) {
|
|
@@ -676,6 +676,18 @@ async function answerOnce({
|
|
|
676
676
|
}
|
|
677
677
|
return completeOnce({ client, system, messages, temperature, model });
|
|
678
678
|
}
|
|
679
|
+
async function applyTransformReply(transformReply, input, logger) {
|
|
680
|
+
if (!transformReply) return input.text;
|
|
681
|
+
try {
|
|
682
|
+
return await transformReply(input);
|
|
683
|
+
} catch (error) {
|
|
684
|
+
logger?.error(
|
|
685
|
+
`transformReply threw for channel "${input.channel}"; sending the original reply`,
|
|
686
|
+
error
|
|
687
|
+
);
|
|
688
|
+
return input.text;
|
|
689
|
+
}
|
|
690
|
+
}
|
|
679
691
|
|
|
680
692
|
// src/core/buckets.ts
|
|
681
693
|
function defaultBuckets(mode) {
|
|
@@ -811,7 +823,7 @@ function createInboundPipeline(deps, config) {
|
|
|
811
823
|
channelHint: config.channelHint,
|
|
812
824
|
buckets
|
|
813
825
|
});
|
|
814
|
-
const { content } = await answerOnce({
|
|
826
|
+
const { content: produced } = await answerOnce({
|
|
815
827
|
answerFn: config.answerFn,
|
|
816
828
|
client: deps.client,
|
|
817
829
|
system,
|
|
@@ -821,6 +833,11 @@ function createInboundPipeline(deps, config) {
|
|
|
821
833
|
conversationId,
|
|
822
834
|
model: config.model
|
|
823
835
|
});
|
|
836
|
+
const content = await applyTransformReply(
|
|
837
|
+
config.transformReply,
|
|
838
|
+
{ channel: config.channel ?? "channel", sender, conversationId, text: produced },
|
|
839
|
+
config.logger
|
|
840
|
+
);
|
|
824
841
|
if (!content) {
|
|
825
842
|
return { action: "ignore" };
|
|
826
843
|
}
|
|
@@ -928,8 +945,10 @@ function createWhatsAppInboundHandler(config) {
|
|
|
928
945
|
const pipeline = createInboundPipeline(
|
|
929
946
|
{ client: config.client, store: config.store, prompts: config.prompts },
|
|
930
947
|
{
|
|
948
|
+
channel: "whatsapp",
|
|
931
949
|
answerFn: config.answerFn,
|
|
932
950
|
bucketsFor: config.bucketsFor,
|
|
951
|
+
transformReply: config.transformReply,
|
|
933
952
|
model: config.model,
|
|
934
953
|
channelHint: config.channelHint,
|
|
935
954
|
personaResolver: personaResolver ? ({ sender, text }) => personaResolver({ senderPhone: sender, text }) : void 0,
|
|
@@ -941,7 +960,8 @@ function createWhatsAppInboundHandler(config) {
|
|
|
941
960
|
unmuteReply: config.unmuteReply,
|
|
942
961
|
dmRateLimit: config.dmRateLimit,
|
|
943
962
|
groupRateLimit: config.groupRateLimit,
|
|
944
|
-
now: config.now
|
|
963
|
+
now: config.now,
|
|
964
|
+
logger
|
|
945
965
|
}
|
|
946
966
|
);
|
|
947
967
|
return async function handleWhatsAppMessage(event) {
|
package/dist/core/answer.d.ts
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
* exactly what it has always been.
|
|
14
14
|
*/
|
|
15
15
|
import type OpenAI from "openai";
|
|
16
|
+
import type { Logger } from "./logger";
|
|
16
17
|
import type { PipelineMessage, PipelineMode } from "./pipeline";
|
|
17
18
|
/** Token accounting in the OpenAI wire shape */
|
|
18
19
|
export interface AnswerUsage {
|
|
@@ -66,6 +67,36 @@ export declare function answerOnce({ answerFn, client, system, messages, mode, s
|
|
|
66
67
|
content: string;
|
|
67
68
|
usage: AnswerUsage;
|
|
68
69
|
}>;
|
|
70
|
+
/** What a `transformReply` hook receives: the produced answer plus who/where it's going. */
|
|
71
|
+
export interface TransformReplyInput {
|
|
72
|
+
/**
|
|
73
|
+
* Identifies the surface delivering this reply — a channel's own name
|
|
74
|
+
* (e.g. "whatsapp", "telegram") or a fixed identifier for an HTTP surface
|
|
75
|
+
* (e.g. "widget-public", "openai-compat-private").
|
|
76
|
+
*/
|
|
77
|
+
channel: string;
|
|
78
|
+
sender?: string;
|
|
79
|
+
conversationId?: string;
|
|
80
|
+
text: string;
|
|
81
|
+
}
|
|
82
|
+
/**
|
|
83
|
+
* Modifies or vetoes an already-produced reply, after `answerFn`/the built-in
|
|
84
|
+
* completion and guardrails have run. A string result replaces the reply;
|
|
85
|
+
* `null` vetoes it — treated the same as an empty answer by every call site
|
|
86
|
+
* (nothing delivered; the channel pipeline never records an assistant turn
|
|
87
|
+
* for it, though the user's own turn — already appended before answering —
|
|
88
|
+
* stays recorded).
|
|
89
|
+
*
|
|
90
|
+
* Non-streaming surfaces only: a streaming response is delivered incrementally
|
|
91
|
+
* as it's produced, before a final answer exists to transform.
|
|
92
|
+
*/
|
|
93
|
+
export type TransformReply = (input: TransformReplyInput) => string | null | Promise<string | null>;
|
|
94
|
+
/**
|
|
95
|
+
* Runs `transformReply` if configured. A throw/rejection is logged and the
|
|
96
|
+
* ORIGINAL text is returned — the delivery invariant: a bug in a plugin hook
|
|
97
|
+
* must never silently swallow an answer the model already produced.
|
|
98
|
+
*/
|
|
99
|
+
export declare function applyTransformReply(transformReply: TransformReply | undefined, input: TransformReplyInput, logger?: Pick<Logger, "error">): Promise<string | null>;
|
|
69
100
|
/**
|
|
70
101
|
* Stream an answer. A brain that returns a plain string cannot stream, so its
|
|
71
102
|
* answer degrades to a single chunk followed by the stream's normal end —
|
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/core/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAKD,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;AAE1F,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC,CAKlE;AAED;;;;;;;;GAQG;AACH,wBAAuB,YAAY,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAaxC"}
|
|
1
|
+
{"version":3,"file":"answer.d.ts","sourceRoot":"","sources":["../../src/core/answer.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;GAaG;AAEH,OAAO,KAAK,MAAM,MAAM,QAAQ,CAAC;AAGjC,OAAO,KAAK,EAAE,MAAM,EAAE,MAAM,UAAU,CAAC;AACvC,OAAO,KAAK,EAAE,eAAe,EAAE,YAAY,EAAE,MAAM,YAAY,CAAC;AAEhE,gDAAgD;AAChD,MAAM,WAAW,WAAW;IAC1B,aAAa,EAAE,MAAM,CAAC;IACtB,iBAAiB,EAAE,MAAM,CAAC;IAC1B,YAAY,EAAE,MAAM,CAAC;CACtB;AAKD,qEAAqE;AACrE,MAAM,WAAW,aAAa;IAC5B,uDAAuD;IACvD,MAAM,EAAE,MAAM,CAAC;IACf,4BAA4B;IAC5B,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,uCAAuC;IACvC,IAAI,EAAE,YAAY,CAAC;IACnB,mEAAmE;IACnE,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,mFAAmF;IACnF,cAAc,CAAC,EAAE,MAAM,CAAC;CACzB;AAED;;;GAGG;AACH,MAAM,MAAM,cAAc,GAAG,MAAM,GAAG;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,CAAC,EAAE,WAAW,CAAA;CAAE,CAAC;AAE/E;;;GAGG;AACH,MAAM,MAAM,QAAQ,GAAG,CAAC,KAAK,EAAE,aAAa,KAAK,OAAO,CAAC,cAAc,CAAC,GAAG,cAAc,CAAC;AAE1F,MAAM,WAAW,aAAa;IAC5B,8EAA8E;IAC9E,QAAQ,CAAC,EAAE,QAAQ,CAAC;IACpB,yEAAyE;IACzE,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,QAAQ,EAAE,eAAe,EAAE,CAAC;IAC5B,IAAI,EAAE,YAAY,CAAC;IACnB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,WAAW,CAAC,EAAE,MAAM,CAAC;IACrB,KAAK,CAAC,EAAE,MAAM,CAAC;CAChB;AAqBD;;GAEG;AACH,wBAAsB,UAAU,CAAC,EAC/B,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,OAAO,CAAC;IAAE,OAAO,EAAE,MAAM,CAAC;IAAC,KAAK,EAAE,WAAW,CAAA;CAAE,CAAC,CAKlE;AAED,4FAA4F;AAC5F,MAAM,WAAW,mBAAmB;IAClC;;;;OAIG;IACH,OAAO,EAAE,MAAM,CAAC;IAChB,MAAM,CAAC,EAAE,MAAM,CAAC;IAChB,cAAc,CAAC,EAAE,MAAM,CAAC;IACxB,IAAI,EAAE,MAAM,CAAC;CACd;AAED;;;;;;;;;;GAUG;AACH,MAAM,MAAM,cAAc,GAAG,CAAC,KAAK,EAAE,mBAAmB,KAAK,MAAM,GAAG,IAAI,GAAG,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAAC;AAEpG;;;;GAIG;AACH,wBAAsB,mBAAmB,CACvC,cAAc,EAAE,cAAc,GAAG,SAAS,EAC1C,KAAK,EAAE,mBAAmB,EAC1B,MAAM,CAAC,EAAE,IAAI,CAAC,MAAM,EAAE,OAAO,CAAC,GAC7B,OAAO,CAAC,MAAM,GAAG,IAAI,CAAC,CAWxB;AAED;;;;;;;;GAQG;AACH,wBAAuB,YAAY,CAAC,EAClC,QAAQ,EACR,MAAM,EACN,MAAM,EACN,QAAQ,EACR,IAAI,EACJ,MAAM,EACN,cAAc,EACd,WAAW,EACX,KAAK,GACN,EAAE,aAAa,GAAG,cAAc,CAAC,MAAM,CAAC,CAaxC"}
|
package/dist/index.d.ts
CHANGED
|
@@ -14,8 +14,8 @@ export type { ApiKeyOptions, ApiKeyPayload, VerifyResult } from "./auth";
|
|
|
14
14
|
export { ApiKeyManager } from "./auth";
|
|
15
15
|
export type { Channel } from "./channels";
|
|
16
16
|
export type { ChatBotConfig, ChatButtonConfig, ChatConfig, ChatMessage, ChatMode, StreamCallbacks, } from "./client";
|
|
17
|
-
export type { AnswerFn, AnswerFnInput, AnswerFnResult, AnswerOptions, AnswerUsage, } from "./core/answer";
|
|
18
|
-
export { answerOnce, answerStream } from "./core/answer";
|
|
17
|
+
export type { AnswerFn, AnswerFnInput, AnswerFnResult, AnswerOptions, AnswerUsage, TransformReply, TransformReplyInput, } from "./core/answer";
|
|
18
|
+
export { answerOnce, answerStream, applyTransformReply } from "./core/answer";
|
|
19
19
|
export type { BucketsFor, BucketsForContext } from "./core/buckets";
|
|
20
20
|
export { defaultBuckets, resolveBuckets } from "./core/buckets";
|
|
21
21
|
export { detectLeakage, guardOutput, scrubOutput } from "./core/guardrails";
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;GAWG;AAEH,YAAY,EAAE,aAAa,EAAE,aAAa,EAAE,YAAY,EAAE,MAAM,QAAQ,CAAC;AAEzE,OAAO,EAAE,aAAa,EAAE,MAAM,QAAQ,CAAC;AAIvC,YAAY,EAAE,OAAO,EAAE,MAAM,YAAY,CAAC;AAG1C,YAAY,EACV,aAAa,EACb,gBAAgB,EAChB,UAAU,EACV,WAAW,EACX,QAAQ,EACR,eAAe,GAChB,MAAM,UAAU,CAAC;AAClB,YAAY,EACV,QAAQ,EACR,aAAa,EACb,cAAc,EACd,aAAa,EACb,WAAW,
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -36,6 +36,7 @@ __export(index_exports, {
|
|
|
36
36
|
VectorStore: () => VectorStore,
|
|
37
37
|
answerOnce: () => answerOnce,
|
|
38
38
|
answerStream: () => answerStream,
|
|
39
|
+
applyTransformReply: () => applyTransformReply,
|
|
39
40
|
chatBodyLimit: () => chatBodyLimit,
|
|
40
41
|
completeOnce: () => completeOnce,
|
|
41
42
|
completeStream: () => completeStream,
|
|
@@ -282,6 +283,18 @@ async function answerOnce({
|
|
|
282
283
|
}
|
|
283
284
|
return completeOnce({ client, system, messages, temperature, model });
|
|
284
285
|
}
|
|
286
|
+
async function applyTransformReply(transformReply, input, logger) {
|
|
287
|
+
if (!transformReply) return input.text;
|
|
288
|
+
try {
|
|
289
|
+
return await transformReply(input);
|
|
290
|
+
} catch (error) {
|
|
291
|
+
logger?.error(
|
|
292
|
+
`transformReply threw for channel "${input.channel}"; sending the original reply`,
|
|
293
|
+
error
|
|
294
|
+
);
|
|
295
|
+
return input.text;
|
|
296
|
+
}
|
|
297
|
+
}
|
|
285
298
|
async function* answerStream({
|
|
286
299
|
answerFn,
|
|
287
300
|
client,
|
|
@@ -1002,6 +1015,11 @@ async function createMCPServer(config) {
|
|
|
1002
1015
|
conversationId: convId,
|
|
1003
1016
|
model
|
|
1004
1017
|
});
|
|
1018
|
+
const replyText = await applyTransformReply(
|
|
1019
|
+
config.transformReply,
|
|
1020
|
+
{ channel: "mcp-public", conversationId: convId, text: result.content },
|
|
1021
|
+
log
|
|
1022
|
+
);
|
|
1005
1023
|
const cost = calculateCost(result.usage, config.openai.pricing);
|
|
1006
1024
|
const duration = Date.now() - startTime;
|
|
1007
1025
|
await logger.logChatInteraction(
|
|
@@ -1009,7 +1027,7 @@ async function createMCPServer(config) {
|
|
|
1009
1027
|
convId,
|
|
1010
1028
|
conversationMessages,
|
|
1011
1029
|
ctx,
|
|
1012
|
-
|
|
1030
|
+
replyText ?? "",
|
|
1013
1031
|
duration,
|
|
1014
1032
|
cost
|
|
1015
1033
|
).catch((err) => log.error("Logging error:", err));
|
|
@@ -1017,7 +1035,7 @@ async function createMCPServer(config) {
|
|
|
1017
1035
|
content: [
|
|
1018
1036
|
{
|
|
1019
1037
|
type: "text",
|
|
1020
|
-
text:
|
|
1038
|
+
text: replyText ?? ""
|
|
1021
1039
|
}
|
|
1022
1040
|
],
|
|
1023
1041
|
_meta: {
|
|
@@ -1083,6 +1101,11 @@ async function createMCPServer(config) {
|
|
|
1083
1101
|
conversationId: convId,
|
|
1084
1102
|
model
|
|
1085
1103
|
});
|
|
1104
|
+
const replyText = await applyTransformReply(
|
|
1105
|
+
config.transformReply,
|
|
1106
|
+
{ channel: "mcp-private", conversationId: convId, text: result.content },
|
|
1107
|
+
log
|
|
1108
|
+
);
|
|
1086
1109
|
const cost = calculateCost(result.usage, config.openai.pricing);
|
|
1087
1110
|
const duration = Date.now() - startTime;
|
|
1088
1111
|
await logger.logChatInteraction(
|
|
@@ -1090,7 +1113,7 @@ async function createMCPServer(config) {
|
|
|
1090
1113
|
convId,
|
|
1091
1114
|
conversationMessages,
|
|
1092
1115
|
ctx,
|
|
1093
|
-
|
|
1116
|
+
replyText ?? "",
|
|
1094
1117
|
duration,
|
|
1095
1118
|
cost
|
|
1096
1119
|
).catch((err) => log.error("Logging error:", err));
|
|
@@ -1098,7 +1121,7 @@ async function createMCPServer(config) {
|
|
|
1098
1121
|
content: [
|
|
1099
1122
|
{
|
|
1100
1123
|
type: "text",
|
|
1101
|
-
text:
|
|
1124
|
+
text: replyText ?? ""
|
|
1102
1125
|
}
|
|
1103
1126
|
],
|
|
1104
1127
|
_meta: {
|
|
@@ -1571,7 +1594,12 @@ function demoRoutes(deps) {
|
|
|
1571
1594
|
mode: "public",
|
|
1572
1595
|
model
|
|
1573
1596
|
});
|
|
1574
|
-
|
|
1597
|
+
const reply = await applyTransformReply(
|
|
1598
|
+
config.transformReply,
|
|
1599
|
+
{ channel: "widget-demo", text: out.content },
|
|
1600
|
+
logger
|
|
1601
|
+
);
|
|
1602
|
+
return c.json({ reply: reply ?? "" });
|
|
1575
1603
|
} catch (error) {
|
|
1576
1604
|
const err = error;
|
|
1577
1605
|
logger.error("Demo chat error:", err);
|
|
@@ -1614,7 +1642,7 @@ function oversizedBodyError(message) {
|
|
|
1614
1642
|
return { error: { message, type: "invalid_request_error" } };
|
|
1615
1643
|
}
|
|
1616
1644
|
function openaiRoutes(deps) {
|
|
1617
|
-
const { client, store, config, prompts, apiKeyManager } = deps;
|
|
1645
|
+
const { client, store, config, prompts, apiKeyManager, logger } = deps;
|
|
1618
1646
|
const app = new import_hono2.Hono();
|
|
1619
1647
|
const { limitPublic, limitPrivate } = createRateLimiter2(config);
|
|
1620
1648
|
const requirePrivateJWT = createJWTMiddleware(config);
|
|
@@ -1709,13 +1737,18 @@ function openaiRoutes(deps) {
|
|
|
1709
1737
|
temperature,
|
|
1710
1738
|
model: serverModel
|
|
1711
1739
|
});
|
|
1740
|
+
const reply = await applyTransformReply(
|
|
1741
|
+
config.transformReply,
|
|
1742
|
+
{ channel: `openai-compat-${mode}`, sender, conversationId, text: out.content },
|
|
1743
|
+
logger
|
|
1744
|
+
);
|
|
1712
1745
|
return c.json({
|
|
1713
1746
|
id,
|
|
1714
1747
|
object: "chat.completion",
|
|
1715
1748
|
created,
|
|
1716
1749
|
model: serverModel,
|
|
1717
1750
|
choices: [
|
|
1718
|
-
{ index: 0, message: { role: "assistant", content:
|
|
1751
|
+
{ index: 0, message: { role: "assistant", content: reply ?? "" }, finish_reason: "stop" }
|
|
1719
1752
|
],
|
|
1720
1753
|
usage: out.usage
|
|
1721
1754
|
});
|
|
@@ -1745,7 +1778,7 @@ function wantsStream(c) {
|
|
|
1745
1778
|
return acc.includes("text/event-stream");
|
|
1746
1779
|
}
|
|
1747
1780
|
function privateRoutes(deps) {
|
|
1748
|
-
const { client, store, config, prompts } = deps;
|
|
1781
|
+
const { client, store, config, prompts, logger } = deps;
|
|
1749
1782
|
const app = new import_hono3.Hono();
|
|
1750
1783
|
const requirePrivateJWT = createJWTMiddleware(config);
|
|
1751
1784
|
const { limitPrivate } = createRateLimiter2(config);
|
|
@@ -1800,7 +1833,12 @@ function privateRoutes(deps) {
|
|
|
1800
1833
|
sender,
|
|
1801
1834
|
model
|
|
1802
1835
|
});
|
|
1803
|
-
|
|
1836
|
+
const reply = await applyTransformReply(
|
|
1837
|
+
config.transformReply,
|
|
1838
|
+
{ channel: "widget-private", sender, text: out.content },
|
|
1839
|
+
logger
|
|
1840
|
+
);
|
|
1841
|
+
return c.json({ reply: reply ?? "" });
|
|
1804
1842
|
});
|
|
1805
1843
|
return app;
|
|
1806
1844
|
}
|
|
@@ -1862,7 +1900,12 @@ function publicRoutes(deps) {
|
|
|
1862
1900
|
});
|
|
1863
1901
|
}
|
|
1864
1902
|
const out = await answerOnce({ answerFn, client, system, messages, mode: "public", model });
|
|
1865
|
-
|
|
1903
|
+
const reply = await applyTransformReply(
|
|
1904
|
+
config.transformReply,
|
|
1905
|
+
{ channel: "widget-public", text: out.content },
|
|
1906
|
+
logger
|
|
1907
|
+
);
|
|
1908
|
+
return c.json({ reply: reply ?? "" });
|
|
1866
1909
|
});
|
|
1867
1910
|
return app;
|
|
1868
1911
|
}
|
|
@@ -2201,6 +2244,7 @@ function createShortener(config, fetchImpl = fetch) {
|
|
|
2201
2244
|
VectorStore,
|
|
2202
2245
|
answerOnce,
|
|
2203
2246
|
answerStream,
|
|
2247
|
+
applyTransformReply,
|
|
2204
2248
|
chatBodyLimit,
|
|
2205
2249
|
completeOnce,
|
|
2206
2250
|
completeStream,
|