lightclawbot 1.0.7 → 1.0.9-beta.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 +47 -213
- package/dist/index.d.ts +3 -3
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -2
- package/dist/index.js.map +1 -1
- package/dist/src/channel.d.ts +1 -1
- package/dist/src/channel.d.ts.map +1 -1
- package/dist/src/channel.js +1 -1
- package/dist/src/channel.js.map +1 -1
- package/dist/src/config.d.ts +16 -6
- package/dist/src/config.d.ts.map +1 -1
- package/dist/src/config.js +18 -5
- package/dist/src/config.js.map +1 -1
- package/dist/src/download-tool.d.ts +2 -2
- package/dist/src/download-tool.d.ts.map +1 -1
- package/dist/src/download-tool.js +9 -9
- package/dist/src/download-tool.js.map +1 -1
- package/dist/src/file-storage.d.ts +9 -9
- package/dist/src/file-storage.d.ts.map +1 -1
- package/dist/src/file-storage.js +10 -10
- package/dist/src/file-storage.js.map +1 -1
- package/dist/src/format-urls.d.ts +5 -5
- package/dist/src/format-urls.js +8 -8
- package/dist/src/format-urls.js.map +1 -1
- package/dist/src/gateway.d.ts.map +1 -1
- package/dist/src/gateway.js +18 -14
- package/dist/src/gateway.js.map +1 -1
- package/dist/src/inbound.d.ts.map +1 -1
- package/dist/src/inbound.js +20 -21
- package/dist/src/inbound.js.map +1 -1
- package/dist/src/outbound.d.ts +1 -1
- package/dist/src/outbound.d.ts.map +1 -1
- package/dist/src/outbound.js +15 -3
- package/dist/src/outbound.js.map +1 -1
- package/dist/src/runtime.d.ts +1 -1
- package/dist/src/runtime.d.ts.map +1 -1
- package/dist/src/socket/handlers.d.ts +26 -0
- package/dist/src/socket/handlers.d.ts.map +1 -0
- package/dist/src/socket/handlers.js +130 -0
- package/dist/src/socket/handlers.js.map +1 -0
- package/dist/src/socket/index.d.ts +11 -0
- package/dist/src/socket/index.d.ts.map +1 -0
- package/dist/src/socket/index.js +9 -0
- package/dist/src/socket/index.js.map +1 -0
- package/dist/src/socket/registry.d.ts +59 -0
- package/dist/src/socket/registry.d.ts.map +1 -0
- package/dist/src/socket/registry.js +125 -0
- package/dist/src/socket/registry.js.map +1 -0
- package/dist/src/socket/reliable-emitter.d.ts +79 -0
- package/dist/src/socket/reliable-emitter.d.ts.map +1 -0
- package/dist/src/socket/reliable-emitter.js +217 -0
- package/dist/src/socket/reliable-emitter.js.map +1 -0
- package/dist/src/types.d.ts +7 -2
- package/dist/src/types.d.ts.map +1 -1
- package/dist/src/upload-tool.d.ts +2 -2
- package/dist/src/upload-tool.d.ts.map +1 -1
- package/dist/src/upload-tool.js +3 -3
- package/dist/src/upload-tool.js.map +1 -1
- package/node_modules/socket.io-parser/build/cjs/index.d.ts +14 -4
- package/node_modules/socket.io-parser/build/cjs/index.js +14 -6
- package/node_modules/socket.io-parser/build/esm/index.d.ts +14 -4
- package/node_modules/socket.io-parser/build/esm/index.js +14 -6
- package/node_modules/socket.io-parser/build/esm-debug/index.d.ts +14 -4
- package/node_modules/socket.io-parser/build/esm-debug/index.js +14 -6
- package/node_modules/socket.io-parser/package.json +1 -1
- package/package.json +6 -11
- package/skills/lightclaw-media/SKILL.md +22 -102
- package/dist/public/data/scripts/manifest.json +0 -11
- package/dist/public/data/scripts/upgrade.211d7e4c.sh +0 -266
- package/dist/public/data/scripts/upgrade.sh +0 -266
- package/dist/src/session-history.d.ts +0 -88
- package/dist/src/session-history.d.ts.map +0 -1
- package/dist/src/session-history.js +0 -598
- package/dist/src/session-history.js.map +0 -1
- package/dist/src/streaming/delta-tracker.d.ts +0 -34
- package/dist/src/streaming/delta-tracker.d.ts.map +0 -1
- package/dist/src/streaming/delta-tracker.js +0 -145
- package/dist/src/streaming/delta-tracker.js.map +0 -1
- package/dist/src/streaming/index.d.ts +0 -12
- package/dist/src/streaming/index.d.ts.map +0 -1
- package/dist/src/streaming/index.js +0 -13
- package/dist/src/streaming/index.js.map +0 -1
- package/dist/src/streaming/stream-reply-sink.d.ts +0 -59
- package/dist/src/streaming/stream-reply-sink.d.ts.map +0 -1
- package/dist/src/streaming/stream-reply-sink.js +0 -293
- package/dist/src/streaming/stream-reply-sink.js.map +0 -1
- package/dist/src/streaming/types.d.ts +0 -45
- package/dist/src/streaming/types.d.ts.map +0 -1
- package/dist/src/streaming/types.js +0 -7
- package/dist/src/streaming/types.js.map +0 -1
|
@@ -1,145 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LightClaw — 文本增量计算器 (DeltaTracker)
|
|
3
|
-
*
|
|
4
|
-
* 参考 kimi-claw `utils/text.js` 中的 `toStreamDeltaText` 算法。
|
|
5
|
-
*
|
|
6
|
-
* 核心作用:
|
|
7
|
-
* AI 引擎的 `onPartialReply` 回调输出的文本格式不确定 —— 有的模型输出全量快照(snapshot),
|
|
8
|
-
* 有的输出纯增量(delta),甚至可能在同一次对话中混合使用。
|
|
9
|
-
* DeltaTracker 会自动检测模式,始终输出 **纯增量文本**,方便直接追加到流式推送中。
|
|
10
|
-
*
|
|
11
|
-
* 算法概述:
|
|
12
|
-
* 1. 维护 `{ latest, mode, lastEmitted, localSnapshot }` 状态
|
|
13
|
-
* 2. 新文本到达时,判断其与 `latest` 的关系:
|
|
14
|
-
* - 如果新文本以 latest 为前缀 → snapshot 模式,截取增量
|
|
15
|
-
* - 如果新文本与 latest 有较长公共前缀 → snapshot 模式(可能是局部重传)
|
|
16
|
-
* - 如果上述均不满足 → delta 模式,直接追加
|
|
17
|
-
* 3. 各种边界处理:重复文本、前缀回退、模式切换等
|
|
18
|
-
*/
|
|
19
|
-
// ============================================================
|
|
20
|
-
// 公共工具
|
|
21
|
-
// ============================================================
|
|
22
|
-
/** 计算两个字符串的最长公共前缀长度 */
|
|
23
|
-
function longestCommonPrefixLength(a, b) {
|
|
24
|
-
const len = Math.min(a.length, b.length);
|
|
25
|
-
let i = 0;
|
|
26
|
-
while (i < len && a[i] === b[i]) {
|
|
27
|
-
i += 1;
|
|
28
|
-
}
|
|
29
|
-
return i;
|
|
30
|
-
}
|
|
31
|
-
// ============================================================
|
|
32
|
-
// 核心 API
|
|
33
|
-
// ============================================================
|
|
34
|
-
/**
|
|
35
|
-
* 创建一个新的增量追踪器状态。
|
|
36
|
-
*/
|
|
37
|
-
export function createDeltaTrackerState() {
|
|
38
|
-
return {
|
|
39
|
-
latest: "",
|
|
40
|
-
mode: "unknown",
|
|
41
|
-
lastEmitted: "",
|
|
42
|
-
localSnapshot: undefined,
|
|
43
|
-
};
|
|
44
|
-
}
|
|
45
|
-
/**
|
|
46
|
-
* 将传入的文本(可能是快照或增量)转换为纯增量文本。
|
|
47
|
-
*
|
|
48
|
-
* @param state - 增量追踪器状态(会被就地修改)
|
|
49
|
-
* @param text - AI 引擎传入的文本
|
|
50
|
-
* @returns 需要追加发送的增量文本;如果无新增量则返回 `undefined`
|
|
51
|
-
*
|
|
52
|
-
* 直接移植自 kimi-claw 的 `toStreamDeltaText`,逻辑完全对齐。
|
|
53
|
-
*/
|
|
54
|
-
export function toStreamDeltaText(state, text) {
|
|
55
|
-
if (typeof text !== "string" || text.length === 0)
|
|
56
|
-
return undefined;
|
|
57
|
-
const incoming = text;
|
|
58
|
-
const previous = state.latest;
|
|
59
|
-
const currentMode = state.mode ?? "unknown";
|
|
60
|
-
// ── 首次接收 ──
|
|
61
|
-
if (!previous) {
|
|
62
|
-
state.latest = incoming;
|
|
63
|
-
state.mode = "unknown";
|
|
64
|
-
state.lastEmitted = incoming;
|
|
65
|
-
state.localSnapshot = undefined;
|
|
66
|
-
return incoming;
|
|
67
|
-
}
|
|
68
|
-
// ── 完全相同 → 无增量 ──
|
|
69
|
-
if (incoming === previous) {
|
|
70
|
-
state.localSnapshot = undefined;
|
|
71
|
-
return undefined;
|
|
72
|
-
}
|
|
73
|
-
// ── 新文本以 previous 为前缀 → snapshot 模式 ──
|
|
74
|
-
if (incoming.startsWith(previous)) {
|
|
75
|
-
state.latest = incoming;
|
|
76
|
-
state.mode = "snapshot";
|
|
77
|
-
const delta = incoming.slice(previous.length);
|
|
78
|
-
state.localSnapshot = undefined;
|
|
79
|
-
if (delta.length > 0) {
|
|
80
|
-
state.lastEmitted = delta;
|
|
81
|
-
return delta;
|
|
82
|
-
}
|
|
83
|
-
return undefined;
|
|
84
|
-
}
|
|
85
|
-
// ── 部分公共前缀分析 ──
|
|
86
|
-
const commonLen = longestCommonPrefixLength(previous, incoming);
|
|
87
|
-
if (incoming.length - previous.length > 0 &&
|
|
88
|
-
commonLen > 0 &&
|
|
89
|
-
(currentMode === "snapshot" ||
|
|
90
|
-
commonLen >= Math.max(8, Math.floor(0.75 * previous.length)) ||
|
|
91
|
-
previous.length >= 64 ||
|
|
92
|
-
incoming.length >= 128 ||
|
|
93
|
-
incoming.length >= 2 * previous.length)) {
|
|
94
|
-
state.latest = incoming;
|
|
95
|
-
state.mode = "snapshot";
|
|
96
|
-
const delta = incoming.slice(commonLen);
|
|
97
|
-
state.localSnapshot = undefined;
|
|
98
|
-
if (delta.length > 0) {
|
|
99
|
-
state.lastEmitted = delta;
|
|
100
|
-
return delta;
|
|
101
|
-
}
|
|
102
|
-
return undefined;
|
|
103
|
-
}
|
|
104
|
-
// ── localSnapshot 追踪(delta 模式下的累计快照校验) ──
|
|
105
|
-
if (typeof state.localSnapshot === "string" && state.localSnapshot.length > 0) {
|
|
106
|
-
if (incoming === state.localSnapshot) {
|
|
107
|
-
return undefined;
|
|
108
|
-
}
|
|
109
|
-
if (incoming.startsWith(state.localSnapshot)) {
|
|
110
|
-
const delta = incoming.slice(state.localSnapshot.length);
|
|
111
|
-
state.latest = `${previous}${delta}`;
|
|
112
|
-
state.mode = "snapshot";
|
|
113
|
-
state.localSnapshot = incoming;
|
|
114
|
-
if (delta.length > 0) {
|
|
115
|
-
state.lastEmitted = delta;
|
|
116
|
-
return delta;
|
|
117
|
-
}
|
|
118
|
-
return undefined;
|
|
119
|
-
}
|
|
120
|
-
state.localSnapshot = undefined;
|
|
121
|
-
}
|
|
122
|
-
// ── 前缀回退(新文本是旧文本的前缀)→ 忽略 ──
|
|
123
|
-
if (previous.startsWith(incoming)) {
|
|
124
|
-
if (currentMode === "snapshot") {
|
|
125
|
-
state.latest = incoming;
|
|
126
|
-
}
|
|
127
|
-
state.localSnapshot = undefined;
|
|
128
|
-
return undefined;
|
|
129
|
-
}
|
|
130
|
-
// ── snapshot 模式下的重复增量检测 ──
|
|
131
|
-
if (currentMode === "snapshot" &&
|
|
132
|
-
typeof state.lastEmitted === "string" &&
|
|
133
|
-
incoming === state.lastEmitted &&
|
|
134
|
-
previous.endsWith(incoming)) {
|
|
135
|
-
state.localSnapshot = undefined;
|
|
136
|
-
return undefined;
|
|
137
|
-
}
|
|
138
|
-
// ── 兜底:视为 delta 模式,直接追加 ──
|
|
139
|
-
state.latest = `${previous}${incoming}`;
|
|
140
|
-
state.mode = "delta";
|
|
141
|
-
state.lastEmitted = incoming;
|
|
142
|
-
state.localSnapshot = incoming;
|
|
143
|
-
return incoming;
|
|
144
|
-
}
|
|
145
|
-
//# sourceMappingURL=delta-tracker.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"delta-tracker.js","sourceRoot":"","sources":["../../../src/streaming/delta-tracker.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,12 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LightClaw — 流式输出模块
|
|
3
|
-
*
|
|
4
|
-
* 统一导出流式输出相关的类型、工具和分发器。
|
|
5
|
-
*
|
|
6
|
-
* 使用方式:
|
|
7
|
-
* import { createStreamReplyConfig } from "./streaming/index.js";
|
|
8
|
-
*/
|
|
9
|
-
export type { StreamFrameKind, DeltaTrackerState, StreamReplySinkOptions } from "./types.js";
|
|
10
|
-
export { createDeltaTrackerState, toStreamDeltaText } from "./delta-tracker.js";
|
|
11
|
-
export { createStreamReplyConfig } from "./stream-reply-sink.js";
|
|
12
|
-
//# sourceMappingURL=index.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../../src/streaming/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,YAAY,EAAE,eAAe,EAAE,iBAAiB,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AAG7F,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAGhF,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -1,13 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LightClaw — 流式输出模块
|
|
3
|
-
*
|
|
4
|
-
* 统一导出流式输出相关的类型、工具和分发器。
|
|
5
|
-
*
|
|
6
|
-
* 使用方式:
|
|
7
|
-
* import { createStreamReplyConfig } from "./streaming/index.js";
|
|
8
|
-
*/
|
|
9
|
-
// 增量计算器
|
|
10
|
-
export { createDeltaTrackerState, toStreamDeltaText } from "./delta-tracker.js";
|
|
11
|
-
// 真流式回复配置(dispatcher + replyOptions,用于 dispatchReplyFromConfig)
|
|
12
|
-
export { createStreamReplyConfig } from "./stream-reply-sink.js";
|
|
13
|
-
//# sourceMappingURL=index.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../../src/streaming/index.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAKH,QAAQ;AACR,OAAO,EAAE,uBAAuB,EAAE,iBAAiB,EAAE,MAAM,oBAAoB,CAAC;AAEhF,gEAAgE;AAChE,OAAO,EAAE,uBAAuB,EAAE,MAAM,wBAAwB,CAAC"}
|
|
@@ -1,59 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LightClaw — 真流式回复分发 (StreamReplySink)
|
|
3
|
-
*
|
|
4
|
-
* 参考 kimi-claw 的 `handleSessionPrompt` (ACP 通路) 中的流式分发架构。
|
|
5
|
-
*
|
|
6
|
-
* 核心改造:
|
|
7
|
-
* 从 `dispatchReplyWithBufferedBlockDispatcher` (Buffered Block 模式) 切换到
|
|
8
|
-
* `dispatchReplyFromConfig` (直接回调模式),实现逐 token 的真流式输出。
|
|
9
|
-
*
|
|
10
|
-
* 原理对比:
|
|
11
|
-
* - 旧模式(Buffered Block):框架内部缓冲 → 累积一个 block → deliver(payload, {kind})
|
|
12
|
-
* 用户体验:等一会儿突然蹦出一大段文字
|
|
13
|
-
* - 新模式(dispatchReplyFromConfig):AI 每个 token → onPartialReply 实时回调
|
|
14
|
-
* 用户体验:逐字/逐词流畅输出
|
|
15
|
-
*
|
|
16
|
-
* dispatchReplyFromConfig 需要两个参数:
|
|
17
|
-
* 1. dispatcher: ReplyDispatcher — 包含 sendToolResult / sendBlockReply / sendFinalReply 等
|
|
18
|
-
* 2. replyOptions: GetReplyOptions — 包含 onPartialReply / onReasoningStream / onToolStart 等
|
|
19
|
-
*/
|
|
20
|
-
import type { StreamReplySinkOptions } from "./types.js";
|
|
21
|
-
import type { SignalContext } from "../types.js";
|
|
22
|
-
/**
|
|
23
|
-
* 创建真流式回复的 dispatcher 和 replyOptions。
|
|
24
|
-
*
|
|
25
|
-
* 返回 `{ dispatcher, replyOptions }` 对象,直接用于
|
|
26
|
-
* `dispatchReplyFromConfig` 的参数。
|
|
27
|
-
*
|
|
28
|
-
* 对齐 kimi-claw `handleSessionPrompt` 中的实现:
|
|
29
|
-
* - onPartialReply: 每个 token 增量 → 实时 emit stream_chunk
|
|
30
|
-
* - onReasoningStream: 思考过程增量 → 实时 emit reasoning_chunk
|
|
31
|
-
* - onToolStart: 工具调用开始通知
|
|
32
|
-
* - sendFinalReply: 去重后发送最终增量
|
|
33
|
-
* - sendBlockReply: 不处理(已通过 onPartialReply 实时推送)
|
|
34
|
-
*/
|
|
35
|
-
export declare function createStreamReplyConfig(opts: StreamReplySinkOptions, prefixOptions: Record<string, unknown>, signalCtx: SignalContext): {
|
|
36
|
-
dispatcher: {
|
|
37
|
-
sendToolResult: (payload: {
|
|
38
|
-
text?: string;
|
|
39
|
-
mediaUrls?: string[];
|
|
40
|
-
mediaUrl?: string;
|
|
41
|
-
isError?: boolean;
|
|
42
|
-
}) => boolean;
|
|
43
|
-
sendBlockReply: (payload: {
|
|
44
|
-
text?: string;
|
|
45
|
-
mediaUrls?: string[];
|
|
46
|
-
mediaUrl?: string;
|
|
47
|
-
}) => boolean;
|
|
48
|
-
sendFinalReply: (payload: {
|
|
49
|
-
text?: string;
|
|
50
|
-
mediaUrls?: string[];
|
|
51
|
-
mediaUrl?: string;
|
|
52
|
-
}) => boolean;
|
|
53
|
-
waitForIdle: () => Promise<void>;
|
|
54
|
-
getQueuedCounts: () => Record<string, number>;
|
|
55
|
-
markComplete: () => void;
|
|
56
|
-
};
|
|
57
|
-
replyOptions: Record<string, unknown>;
|
|
58
|
-
};
|
|
59
|
-
//# sourceMappingURL=stream-reply-sink.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stream-reply-sink.d.ts","sourceRoot":"","sources":["../../../src/streaming/stream-reply-sink.ts"],"names":[],"mappings":"AAAA;;;;;;;;;;;;;;;;;;GAkBG;AAMH,OAAO,KAAK,EAAE,sBAAsB,EAAE,MAAM,YAAY,CAAC;AACzD,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAGjD;;;;;;;;;;;;GAYG;AACH,wBAAgB,uBAAuB,CACrC,IAAI,EAAE,sBAAsB,EAC5B,aAAa,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,EACtC,SAAS,EAAE,aAAa,GACvB;IACD,UAAU,EAAE;QACV,cAAc,EAAE,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAC;YAAC,OAAO,CAAC,EAAE,OAAO,CAAA;SAAE,KAAK,OAAO,CAAC;QACpH,cAAc,EAAE,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC;QACjG,cAAc,EAAE,CAAC,OAAO,EAAE;YAAE,IAAI,CAAC,EAAE,MAAM,CAAC;YAAC,SAAS,CAAC,EAAE,MAAM,EAAE,CAAC;YAAC,QAAQ,CAAC,EAAE,MAAM,CAAA;SAAE,KAAK,OAAO,CAAC;QACjG,WAAW,EAAE,MAAM,OAAO,CAAC,IAAI,CAAC,CAAC;QACjC,eAAe,EAAE,MAAM,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC;QAC9C,YAAY,EAAE,MAAM,IAAI,CAAC;KAC1B,CAAC;IACF,YAAY,EAAE,MAAM,CAAC,MAAM,EAAE,OAAO,CAAC,CAAC;CACvC,CAiSA"}
|
|
@@ -1,293 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LightClaw — 真流式回复分发 (StreamReplySink)
|
|
3
|
-
*
|
|
4
|
-
* 参考 kimi-claw 的 `handleSessionPrompt` (ACP 通路) 中的流式分发架构。
|
|
5
|
-
*
|
|
6
|
-
* 核心改造:
|
|
7
|
-
* 从 `dispatchReplyWithBufferedBlockDispatcher` (Buffered Block 模式) 切换到
|
|
8
|
-
* `dispatchReplyFromConfig` (直接回调模式),实现逐 token 的真流式输出。
|
|
9
|
-
*
|
|
10
|
-
* 原理对比:
|
|
11
|
-
* - 旧模式(Buffered Block):框架内部缓冲 → 累积一个 block → deliver(payload, {kind})
|
|
12
|
-
* 用户体验:等一会儿突然蹦出一大段文字
|
|
13
|
-
* - 新模式(dispatchReplyFromConfig):AI 每个 token → onPartialReply 实时回调
|
|
14
|
-
* 用户体验:逐字/逐词流畅输出
|
|
15
|
-
*
|
|
16
|
-
* dispatchReplyFromConfig 需要两个参数:
|
|
17
|
-
* 1. dispatcher: ReplyDispatcher — 包含 sendToolResult / sendBlockReply / sendFinalReply 等
|
|
18
|
-
* 2. replyOptions: GetReplyOptions — 包含 onPartialReply / onReasoningStream / onToolStart 等
|
|
19
|
-
*/
|
|
20
|
-
import { CHANNEL_KEY } from "../config.js";
|
|
21
|
-
import { uploadFileToCos } from "../file-storage.js";
|
|
22
|
-
import { mediaUrlsToFiles } from "../media.js";
|
|
23
|
-
import { createDeltaTrackerState, toStreamDeltaText } from "./delta-tracker.js";
|
|
24
|
-
import { emitSignal } from "../types.js";
|
|
25
|
-
/**
|
|
26
|
-
* 创建真流式回复的 dispatcher 和 replyOptions。
|
|
27
|
-
*
|
|
28
|
-
* 返回 `{ dispatcher, replyOptions }` 对象,直接用于
|
|
29
|
-
* `dispatchReplyFromConfig` 的参数。
|
|
30
|
-
*
|
|
31
|
-
* 对齐 kimi-claw `handleSessionPrompt` 中的实现:
|
|
32
|
-
* - onPartialReply: 每个 token 增量 → 实时 emit stream_chunk
|
|
33
|
-
* - onReasoningStream: 思考过程增量 → 实时 emit reasoning_chunk
|
|
34
|
-
* - onToolStart: 工具调用开始通知
|
|
35
|
-
* - sendFinalReply: 去重后发送最终增量
|
|
36
|
-
* - sendBlockReply: 不处理(已通过 onPartialReply 实时推送)
|
|
37
|
-
*/
|
|
38
|
-
export function createStreamReplyConfig(opts, prefixOptions, signalCtx) {
|
|
39
|
-
const { emitter, targetId, originalMsgId, log, effectiveApiKey } = opts;
|
|
40
|
-
// ── 增量追踪器:追踪 AI 输出的累计文本快照 ──
|
|
41
|
-
// 参考 kimi-claw 中 const I = { latest: "" } 和 _ = { latest: "" }
|
|
42
|
-
const partialReplyState = createDeltaTrackerState();
|
|
43
|
-
const reasoningState = createDeltaTrackerState();
|
|
44
|
-
// ── 已通过流式推送的累计文本(用于 sendFinalReply 时去重) ──
|
|
45
|
-
// 参考 kimi-claw 中 let y = ""
|
|
46
|
-
let streamedText = "";
|
|
47
|
-
// ── 分发计数器(对齐 kimi-claw 中的 w = { tool: 0, block: 0, final: 0 }) ──
|
|
48
|
-
const counts = { tool: 0, block: 0, final: 0 };
|
|
49
|
-
// ── typing 状态管理 ──
|
|
50
|
-
let typingStartSent = false;
|
|
51
|
-
let idleSent = false;
|
|
52
|
-
/**
|
|
53
|
-
* 确保 typing_start 只发一次。
|
|
54
|
-
* 在 onPartialReply / onReasoningStream 首次触发时自动发送。
|
|
55
|
-
*/
|
|
56
|
-
const ensureTypingStart = () => {
|
|
57
|
-
if (!typingStartSent) {
|
|
58
|
-
typingStartSent = true;
|
|
59
|
-
emitSignal(signalCtx, "typing_start");
|
|
60
|
-
}
|
|
61
|
-
};
|
|
62
|
-
/**
|
|
63
|
-
* 发送 typing_stop(幂等)
|
|
64
|
-
*/
|
|
65
|
-
const sendTypingStop = () => {
|
|
66
|
-
if (idleSent)
|
|
67
|
-
return;
|
|
68
|
-
idleSent = true;
|
|
69
|
-
emitSignal(signalCtx, "typing_stop");
|
|
70
|
-
};
|
|
71
|
-
/**
|
|
72
|
-
* 处理最终回复中的媒体文件(COS 上传 + 发送)
|
|
73
|
-
*/
|
|
74
|
-
const handleMediaFinal = async (replyText, mediaList) => {
|
|
75
|
-
if (mediaList.length === 0)
|
|
76
|
-
return false;
|
|
77
|
-
const files = await mediaUrlsToFiles(mediaList, log);
|
|
78
|
-
const publicUrls = [];
|
|
79
|
-
const storageConfig = {
|
|
80
|
-
apiKey: effectiveApiKey,
|
|
81
|
-
};
|
|
82
|
-
for (const mediaUrl of mediaList) {
|
|
83
|
-
try {
|
|
84
|
-
const localPath = mediaUrl.startsWith("file://")
|
|
85
|
-
? mediaUrl.slice(7)
|
|
86
|
-
: mediaUrl;
|
|
87
|
-
if (localPath.startsWith("/") || localPath.match(/^[A-Za-z]:\\/)) {
|
|
88
|
-
const { existsSync } = await import("node:fs");
|
|
89
|
-
if (existsSync(localPath)) {
|
|
90
|
-
const result = await uploadFileToCos(localPath, storageConfig);
|
|
91
|
-
publicUrls.push(result.url || "");
|
|
92
|
-
log?.info(`[${CHANNEL_KEY}] [stream] Uploaded to COS: ${localPath} → ${result.url}`);
|
|
93
|
-
}
|
|
94
|
-
}
|
|
95
|
-
}
|
|
96
|
-
catch (uploadErr) {
|
|
97
|
-
log?.warn(`[${CHANNEL_KEY}] [stream] COS upload failed for ${mediaUrl}: ${uploadErr}`);
|
|
98
|
-
}
|
|
99
|
-
}
|
|
100
|
-
let enrichedText = replyText;
|
|
101
|
-
if (publicUrls.length > 0) {
|
|
102
|
-
const urlSection = publicUrls
|
|
103
|
-
.map((url, i) => {
|
|
104
|
-
const match = url.match(/filePath=([^&]+)/);
|
|
105
|
-
const filePath = match ? decodeURIComponent(match[1]) : "";
|
|
106
|
-
const fileName = filePath.split("/").pop() || `file${publicUrls.length > 1 ? ` (${i + 1})` : ""}`;
|
|
107
|
-
return `📎 [${fileName}](${url})`;
|
|
108
|
-
})
|
|
109
|
-
.join("\n");
|
|
110
|
-
enrichedText = enrichedText
|
|
111
|
-
? `${enrichedText}\n\n${urlSection}`
|
|
112
|
-
: urlSection;
|
|
113
|
-
}
|
|
114
|
-
if (files.length > 0) {
|
|
115
|
-
emitter.sendFiles(targetId, enrichedText, files, originalMsgId);
|
|
116
|
-
return true;
|
|
117
|
-
}
|
|
118
|
-
if (enrichedText.trim()) {
|
|
119
|
-
emitter.sendReply(targetId, enrichedText, originalMsgId);
|
|
120
|
-
return true;
|
|
121
|
-
}
|
|
122
|
-
return false;
|
|
123
|
-
};
|
|
124
|
-
// ════════════════════════════════════════════════════════════
|
|
125
|
-
// dispatcher — 对齐 kimi-claw 中的 R.dispatcher
|
|
126
|
-
// ════════════════════════════════════════════════════════════
|
|
127
|
-
const dispatcher = {
|
|
128
|
-
/**
|
|
129
|
-
* sendToolResult — 工具调用结果
|
|
130
|
-
*
|
|
131
|
-
* 参考 kimi-claw: 发送 tool_call_update session update
|
|
132
|
-
* lightclaw 场景下,工具结果通常由框架内部消费,不需要额外推送给用户
|
|
133
|
-
*/
|
|
134
|
-
sendToolResult: (payload) => {
|
|
135
|
-
counts.tool++;
|
|
136
|
-
log?.info(`[${CHANNEL_KEY}] [stream] sendToolResult: textLen=${payload.text?.length ?? 0} isError=${payload.isError ?? false}`);
|
|
137
|
-
return true;
|
|
138
|
-
},
|
|
139
|
-
/**
|
|
140
|
-
* sendBlockReply — 块回复
|
|
141
|
-
*
|
|
142
|
-
* 参考 kimi-claw: sendBlockReply 不处理,因为已通过 onPartialReply 实时推送
|
|
143
|
-
* 这是关键差异:旧模式中 deliver(block) 是主要输出渠道,
|
|
144
|
-
* 新模式中 onPartialReply 才是主输出渠道,sendBlockReply 可以忽略。
|
|
145
|
-
*/
|
|
146
|
-
sendBlockReply: (_payload) => {
|
|
147
|
-
counts.block++;
|
|
148
|
-
log?.debug?.(`[${CHANNEL_KEY}] [stream] sendBlockReply: ignored (already streamed via onPartialReply)`);
|
|
149
|
-
return true;
|
|
150
|
-
},
|
|
151
|
-
/**
|
|
152
|
-
* sendFinalReply — 最终回复
|
|
153
|
-
*
|
|
154
|
-
* 参考 kimi-claw sendFinalReply 的去重逻辑:
|
|
155
|
-
* 1. 如果最终文本与已流式推送的文本完全一致 → 不再发送
|
|
156
|
-
* 2. 如果最终文本以已推送文本为前缀 → 只发送增量部分
|
|
157
|
-
* 3. 如果完全不同 → 发送完整文本
|
|
158
|
-
*/
|
|
159
|
-
sendFinalReply: (payload) => {
|
|
160
|
-
counts.final++;
|
|
161
|
-
const replyText = payload.text ?? "";
|
|
162
|
-
const mediaList = payload.mediaUrls?.length
|
|
163
|
-
? payload.mediaUrls
|
|
164
|
-
: payload.mediaUrl ? [payload.mediaUrl] : [];
|
|
165
|
-
log?.info(`[${CHANNEL_KEY}] [stream] sendFinalReply: textLen=${replyText.length} mediaCount=${mediaList.length} streamedLen=${streamedText.length}`);
|
|
166
|
-
// 处理媒体文件(异步,但 dispatcher 是同步返回的)
|
|
167
|
-
if (mediaList.length > 0) {
|
|
168
|
-
handleMediaFinal(replyText, mediaList).catch((err) => {
|
|
169
|
-
log?.error(`[${CHANNEL_KEY}] [stream] sendFinalReply media handling error: ${err}`);
|
|
170
|
-
});
|
|
171
|
-
}
|
|
172
|
-
// 文本去重逻辑 — 参考 kimi-claw sendFinalReply
|
|
173
|
-
if (replyText.length > 0) {
|
|
174
|
-
if (streamedText) {
|
|
175
|
-
if (replyText === streamedText) {
|
|
176
|
-
// 完全一致,无需再发
|
|
177
|
-
log?.debug?.(`[${CHANNEL_KEY}] [stream] Final text identical to streamed, skipping`);
|
|
178
|
-
}
|
|
179
|
-
else if (replyText.startsWith(streamedText)) {
|
|
180
|
-
// 最终文本以已推送文本为前缀,只发送剩余增量
|
|
181
|
-
const remaining = replyText.slice(streamedText.length);
|
|
182
|
-
if (remaining.length > 0) {
|
|
183
|
-
streamedText += remaining;
|
|
184
|
-
partialReplyState.latest = replyText;
|
|
185
|
-
emitSignal(signalCtx, "stream_chunk", remaining);
|
|
186
|
-
log?.debug?.(`[${CHANNEL_KEY}] [stream] Final delta: ${remaining.length} chars`);
|
|
187
|
-
}
|
|
188
|
-
}
|
|
189
|
-
else {
|
|
190
|
-
// 最终文本与流式文本不匹配,发送完整回复
|
|
191
|
-
log?.warn(`[${CHANNEL_KEY}] [stream] Final text mismatch with streamed text, sending full reply`);
|
|
192
|
-
emitter.sendReply(targetId, replyText, originalMsgId);
|
|
193
|
-
}
|
|
194
|
-
}
|
|
195
|
-
else {
|
|
196
|
-
// 没有流式推送过(可能 onPartialReply 未触发),直接发送完整回复
|
|
197
|
-
emitter.sendReply(targetId, replyText, originalMsgId);
|
|
198
|
-
}
|
|
199
|
-
}
|
|
200
|
-
return true;
|
|
201
|
-
},
|
|
202
|
-
/**
|
|
203
|
-
* waitForIdle — 等待空闲
|
|
204
|
-
* 参考 kimi-claw: async () => {}
|
|
205
|
-
*/
|
|
206
|
-
waitForIdle: async () => { },
|
|
207
|
-
/**
|
|
208
|
-
* getQueuedCounts — 获取排队计数
|
|
209
|
-
* 参考 kimi-claw: () => ({ ...w })
|
|
210
|
-
*/
|
|
211
|
-
getQueuedCounts: () => ({ ...counts }),
|
|
212
|
-
/**
|
|
213
|
-
* markComplete — 标记完成
|
|
214
|
-
* 参考 kimi-claw: () => {}
|
|
215
|
-
*/
|
|
216
|
-
markComplete: () => {
|
|
217
|
-
sendTypingStop();
|
|
218
|
-
},
|
|
219
|
-
};
|
|
220
|
-
// ════════════════════════════════════════════════════════════
|
|
221
|
-
// replyOptions — 对齐 kimi-claw 中的 E (replyOptions)
|
|
222
|
-
// ════════════════════════════════════════════════════════════
|
|
223
|
-
const replyOptions = {
|
|
224
|
-
// ── 来自 prefixOptions 的配置(responsePrefix、onModelSelected 等) ──
|
|
225
|
-
...prefixOptions,
|
|
226
|
-
/**
|
|
227
|
-
* onPartialReply — 核心!每个 token 增量回调
|
|
228
|
-
*
|
|
229
|
-
* 参考 kimi-claw:
|
|
230
|
-
* onPartialReply: (e) => {
|
|
231
|
-
* const o = toStreamDeltaText(I, e.text);
|
|
232
|
-
* o && (y += o, t.sendSessionUpdate(r, { sessionUpdate: "agent_message_chunk", content: { type: "text", text: o } }, n));
|
|
233
|
-
* }
|
|
234
|
-
*
|
|
235
|
-
* 这是实现真流式的关键:AI 引擎每产出一个 token 就触发此回调。
|
|
236
|
-
*/
|
|
237
|
-
onPartialReply: (payload) => {
|
|
238
|
-
if (!payload.text)
|
|
239
|
-
return;
|
|
240
|
-
ensureTypingStart();
|
|
241
|
-
const delta = toStreamDeltaText(partialReplyState, payload.text);
|
|
242
|
-
if (delta && delta.length > 0) {
|
|
243
|
-
streamedText += delta;
|
|
244
|
-
emitSignal(signalCtx, "stream_chunk", delta);
|
|
245
|
-
log?.debug?.(`[${CHANNEL_KEY}] [stream] onPartialReply delta: ${delta.length} chars`);
|
|
246
|
-
}
|
|
247
|
-
},
|
|
248
|
-
/**
|
|
249
|
-
* onReasoningStream — 思考过程增量回调
|
|
250
|
-
*
|
|
251
|
-
* 参考 kimi-claw:
|
|
252
|
-
* onReasoningStream: (e) => {
|
|
253
|
-
* const o = toStreamDeltaText(_, e.text);
|
|
254
|
-
* o && t.sendSessionUpdate(r, { sessionUpdate: "agent_thought_chunk", content: { type: "text", text: o } }, n);
|
|
255
|
-
* }
|
|
256
|
-
*/
|
|
257
|
-
onReasoningStream: (payload) => {
|
|
258
|
-
if (!payload.text)
|
|
259
|
-
return;
|
|
260
|
-
ensureTypingStart();
|
|
261
|
-
const delta = toStreamDeltaText(reasoningState, payload.text);
|
|
262
|
-
if (delta && delta.length > 0) {
|
|
263
|
-
// 推送思考过程增量(使用 stream_chunk + 特殊前缀,或直接忽略)
|
|
264
|
-
// lightclaw 客户端目前不支持独立的 reasoning 通道,暂不推送
|
|
265
|
-
log?.debug?.(`[${CHANNEL_KEY}] [stream] onReasoningStream delta: ${delta.length} chars`);
|
|
266
|
-
}
|
|
267
|
-
},
|
|
268
|
-
/**
|
|
269
|
-
* onToolStart — 工具调用开始
|
|
270
|
-
*
|
|
271
|
-
* 参考 kimi-claw: 发送 tool_call session update
|
|
272
|
-
*/
|
|
273
|
-
onToolStart: (payload) => {
|
|
274
|
-
log?.info(`[${CHANNEL_KEY}] [stream] onToolStart: name=${payload.name ?? "unknown"} phase=${payload.phase ?? ""}`);
|
|
275
|
-
},
|
|
276
|
-
/**
|
|
277
|
-
* onReplyStart — AI 开始生成回复
|
|
278
|
-
*/
|
|
279
|
-
onReplyStart: () => {
|
|
280
|
-
ensureTypingStart();
|
|
281
|
-
},
|
|
282
|
-
/**
|
|
283
|
-
* onAgentRunStart — Agent 运行开始
|
|
284
|
-
*/
|
|
285
|
-
onAgentRunStart: (runId) => {
|
|
286
|
-
log?.info(`[${CHANNEL_KEY}] [stream] Agent run started: runId=${runId}`);
|
|
287
|
-
},
|
|
288
|
-
// 禁用 block streaming(我们通过 onPartialReply 直接处理)
|
|
289
|
-
disableBlockStreaming: false,
|
|
290
|
-
};
|
|
291
|
-
return { dispatcher, replyOptions };
|
|
292
|
-
}
|
|
293
|
-
//# sourceMappingURL=stream-reply-sink.js.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"stream-reply-sink.js","sourceRoot":"","sources":["../../../src/streaming/stream-reply-sink.ts"],"names":[],"mappings":"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"}
|
|
@@ -1,45 +0,0 @@
|
|
|
1
|
-
/**
|
|
2
|
-
* LightClaw — 流式输出类型定义
|
|
3
|
-
*
|
|
4
|
-
* 参考 kimi-claw 的 ImSendMessageStream 和 ACP handlers 中的流式回复模式。
|
|
5
|
-
*/
|
|
6
|
-
import type { SocketEmitter } from "../types.js";
|
|
7
|
-
/** 流式输出的消息子类型 */
|
|
8
|
-
export type StreamFrameKind = "typing_start" | "stream_chunk" | "reasoning_chunk" | "tool_start" | "tool_result" | "stream_end" | "typing_stop";
|
|
9
|
-
/**
|
|
10
|
-
* 增量追踪器内部状态。
|
|
11
|
-
*
|
|
12
|
-
* 参考 kimi-claw `toStreamDeltaText` 中的 `{ latest, mode, lastEmitted, localSnapshot }` 结构。
|
|
13
|
-
* 用于将 AI 引擎输出的文本(可能是快照/增量混合模式)转换为纯增量。
|
|
14
|
-
*/
|
|
15
|
-
export interface DeltaTrackerState {
|
|
16
|
-
/** 目前已知的最新完整文本(快照) */
|
|
17
|
-
latest: string;
|
|
18
|
-
/** 当前检测到的模式:snapshot = AI 输出全量快照;delta = AI 输出增量 */
|
|
19
|
-
mode: "snapshot" | "delta" | "unknown";
|
|
20
|
-
/** 上一次实际发出的增量文本 */
|
|
21
|
-
lastEmitted: string;
|
|
22
|
-
/** 用于 delta 模式下追踪本地快照 */
|
|
23
|
-
localSnapshot: string | undefined;
|
|
24
|
-
}
|
|
25
|
-
/** 创建 StreamReplySink 所需的参数 */
|
|
26
|
-
export interface StreamReplySinkOptions {
|
|
27
|
-
/** Socket 事件发射器(复用 inbound 中的 emitter) */
|
|
28
|
-
emitter: SocketEmitter;
|
|
29
|
-
/** 发送目标用户 ID */
|
|
30
|
-
targetId: string;
|
|
31
|
-
/** 回复消息 ID(整个流共用同一个) */
|
|
32
|
-
replyMsgId: string;
|
|
33
|
-
/** 原始用户消息 ID(用于 replyToMsgId) */
|
|
34
|
-
originalMsgId: string;
|
|
35
|
-
/** 日志器 */
|
|
36
|
-
log?: {
|
|
37
|
-
info: (msg: string) => void;
|
|
38
|
-
warn: (msg: string) => void;
|
|
39
|
-
error: (msg: string) => void;
|
|
40
|
-
debug?: (msg: string) => void;
|
|
41
|
-
};
|
|
42
|
-
/** 当前有效的 API Key(用于 COS 上传) */
|
|
43
|
-
effectiveApiKey: string;
|
|
44
|
-
}
|
|
45
|
-
//# sourceMappingURL=types.d.ts.map
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../../src/streaming/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,MAAM,aAAa,CAAC;AAMjD,iBAAiB;AACjB,MAAM,MAAM,eAAe,GACvB,cAAc,GACd,cAAc,GACd,iBAAiB,GACjB,YAAY,GACZ,aAAa,GACb,YAAY,GACZ,aAAa,CAAC;AAMlB;;;;;GAKG;AACH,MAAM,WAAW,iBAAiB;IAChC,sBAAsB;IACtB,MAAM,EAAE,MAAM,CAAC;IACf,oDAAoD;IACpD,IAAI,EAAE,UAAU,GAAG,OAAO,GAAG,SAAS,CAAC;IACvC,mBAAmB;IACnB,WAAW,EAAE,MAAM,CAAC;IACpB,yBAAyB;IACzB,aAAa,EAAE,MAAM,GAAG,SAAS,CAAC;CACnC;AAMD,+BAA+B;AAC/B,MAAM,WAAW,sBAAsB;IACrC,0CAA0C;IAC1C,OAAO,EAAE,aAAa,CAAC;IACvB,gBAAgB;IAChB,QAAQ,EAAE,MAAM,CAAC;IACjB,wBAAwB;IACxB,UAAU,EAAE,MAAM,CAAC;IACnB,iCAAiC;IACjC,aAAa,EAAE,MAAM,CAAC;IACtB,UAAU;IACV,GAAG,CAAC,EAAE;QACJ,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5B,IAAI,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC5B,KAAK,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;QAC7B,KAAK,CAAC,EAAE,CAAC,GAAG,EAAE,MAAM,KAAK,IAAI,CAAC;KAC/B,CAAC;IACF,+BAA+B;IAC/B,eAAe,EAAE,MAAM,CAAC;CACzB"}
|
|
@@ -1 +0,0 @@
|
|
|
1
|
-
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../../src/streaming/types.ts"],"names":[],"mappings":"AAAA;;;;GAIG"}
|