nolo-cli 0.8.0 → 0.9.1
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/_executeModel-UD44KGZ4.js +203 -0
- package/action-ZJWE3Y5S.js +28 -0
- package/addContentAction-TVDBNJM5.js +26 -0
- package/addDialogAgentAction-EKH774VT.js +68 -0
- package/agentDeleteCommand-TCPEFJTF.js +327 -0
- package/agentEmailCommands-CZ3UILZ4.js +626 -0
- package/agentListCommands-SJKO2FFB.js +223 -0
- package/agentMachineCommands-PSNABISZ.js +425 -0
- package/agentPullCommand-O2MTUAFR.js +122 -0
- package/agentRecordCommands-MH2JKFHS.js +94 -0
- package/agentRunCommand-4KH6MIZR.js +110 -0
- package/agentRunControl-K4VKNEHF.js +57 -0
- package/agentRuntimeLocal-SKHJUWSS.js +104 -0
- package/agentSlice-3WXXN4VK.js +42 -0
- package/authCommand-PXFX6JDT.js +581 -0
- package/authCommands-356AYWEK.js +35 -0
- package/chunk-2EISRLRC.js +238 -0
- package/chunk-2FTTDCD4.js +675 -0
- package/chunk-2K2ECKSG.js +222 -0
- package/chunk-2PUGVR6E.js +19 -0
- package/chunk-3ASDTTEI.js +1774 -0
- package/chunk-3EUW3WEO.js +8 -0
- package/chunk-3LY4F7SV.js +9293 -0
- package/chunk-3TZDXYSF.js +71 -0
- package/chunk-3WYQEUBI.js +34 -0
- package/chunk-4CQLVGRV.js +50 -0
- package/chunk-4IJYA27C.js +342 -0
- package/chunk-4K2KKCSD.js +577 -0
- package/chunk-4SN6J65J.js +38 -0
- package/chunk-4VNS5WPM.js +42 -0
- package/chunk-4ZDM73SD.js +34 -0
- package/chunk-5CS33IBL.js +301 -0
- package/chunk-5KLGBAAR.js +386 -0
- package/chunk-5MIDPSSK.js +206 -0
- package/chunk-5MMAK75Q.js +15 -0
- package/chunk-5PJ3VWCS.js +360 -0
- package/chunk-63SLEOLK.js +175 -0
- package/chunk-63W5N6P3.js +150 -0
- package/chunk-6GANEXVO.js +231 -0
- package/chunk-6HYVJV7B.js +102 -0
- package/chunk-6WD3OOJS.js +20 -0
- package/chunk-7OOXPE3N.js +628 -0
- package/chunk-7QRKCBOP.js +1078 -0
- package/chunk-ATNTHIZI.js +13 -0
- package/chunk-AUQ747NZ.js +902 -0
- package/chunk-AVMKZHBC.js +85 -0
- package/chunk-AZPCASRE.js +36 -0
- package/chunk-BBYVOSU2.js +792 -0
- package/chunk-C3RUNRHT.js +51 -0
- package/chunk-CF2WZF4N.js +134 -0
- package/chunk-CF7PE763.js +10 -0
- package/chunk-CKOJQDKR.js +198 -0
- package/chunk-COMXDWNM.js +807 -0
- package/chunk-CPB2YM4D.js +171 -0
- package/chunk-CWMSB67P.js +34 -0
- package/chunk-DCOA4QSN.js +105 -0
- package/chunk-DFFIZ25G.js +30 -0
- package/chunk-DWLEOOGR.js +8 -0
- package/chunk-DZ3XJVET.js +231 -0
- package/chunk-EHLHSX7R.js +1433 -0
- package/chunk-ELRRLDWI.js +478 -0
- package/chunk-EXTIQUCI.js +482 -0
- package/chunk-EYEEHCU4.js +3066 -0
- package/chunk-G3SUCT4W.js +8 -0
- package/chunk-G43KEVSC.js +1447 -0
- package/chunk-GAKEBQD6.js +30 -0
- package/chunk-GBRG75BQ.js +86 -0
- package/chunk-GG4AJPSR.js +191 -0
- package/chunk-H33743XH.js +25 -0
- package/chunk-HNMXYYJR.js +12 -0
- package/chunk-I42VC5KR.js +15 -0
- package/chunk-IDZTNRP2.js +85 -0
- package/chunk-ISNCLGUZ.js +146 -0
- package/chunk-IWX4UW2E.js +548 -0
- package/chunk-JBTJAPUF.js +35 -0
- package/chunk-JEXVIMYZ.js +55 -0
- package/chunk-JRKKFRAV.js +537 -0
- package/chunk-JZPBVBIY.js +94 -0
- package/chunk-KKY4B2Q2.js +21 -0
- package/chunk-KOCA5FCS.js +4750 -0
- package/chunk-KQHGZWSV.js +63 -0
- package/chunk-KTVVPRJS.js +138 -0
- package/chunk-KUG52IAP.js +8 -0
- package/chunk-KX6YBIHG.js +8 -0
- package/chunk-LARL2V6G.js +9017 -0
- package/chunk-LCBAMY2M.js +60 -0
- package/chunk-LM6IYQ3F.js +16 -0
- package/chunk-M76ECQRD.js +13658 -0
- package/chunk-MGOJ5R6J.js +113 -0
- package/chunk-MKMWT7SA.js +27196 -0
- package/chunk-MRC4Q34M.js +188 -0
- package/chunk-OLZBQWZU.js +6 -0
- package/chunk-OPZYGHUC.js +89 -0
- package/chunk-OWOHNGCN.js +50 -0
- package/chunk-PAKN26FP.js +239 -0
- package/chunk-PCWC3JTN.js +91 -0
- package/chunk-PG5PNA7Y.js +939 -0
- package/chunk-Q2YZULBH.js +104 -0
- package/chunk-QSJ3HFEF.js +8 -0
- package/chunk-QYXLLLGL.js +4519 -0
- package/chunk-ROWQEJRP.js +139 -0
- package/chunk-S7Z2HMCN.js +25 -0
- package/chunk-SBR32ZZS.js +1901 -0
- package/chunk-SH2CFQOH.js +3391 -0
- package/chunk-SL35K7AH.js +226 -0
- package/chunk-TILFE6LV.js +381 -0
- package/chunk-TSS3XJY4.js +2052 -0
- package/chunk-U4TRT5V4.js +246 -0
- package/chunk-U6VC4MU4.js +8 -0
- package/chunk-UAD2WT54.js +64 -0
- package/chunk-UUJDGGAK.js +421 -0
- package/chunk-VF4K2QE6.js +21 -0
- package/chunk-VHBWHNVT.js +59 -0
- package/chunk-VIDPVFP3.js +20 -0
- package/chunk-VP4WJWKW.js +8882 -0
- package/chunk-W4NVOSFF.js +37 -0
- package/chunk-WKM6FH35.js +190 -0
- package/chunk-WWC7I6TA.js +8 -0
- package/chunk-X7W4TNB4.js +1550 -0
- package/chunk-XE635NI4.js +1948 -0
- package/chunk-XKIMRZO2.js +221 -0
- package/chunk-XS466O7S.js +933 -0
- package/chunk-Y5GCAMGP.js +9 -0
- package/chunk-YBWOP46I.js +3272 -0
- package/chunk-YPRZSPF3.js +148 -0
- package/chunk-YYUCRDOK.js +69 -0
- package/chunk-Z4NR32XR.js +50 -0
- package/chunk-ZIG5LJ7X.js +24765 -0
- package/chunk-ZNFJWEN4.js +56 -0
- package/chunk-ZQ2UM4KG.js +2878 -0
- package/cliExecutor-DNE7TCC4.js +36 -0
- package/clipboardy-BVE3LVEH.js +12001 -0
- package/createAgentAutomationAction-63OIP6X5.js +866 -0
- package/createDialogAction-ZLYZDYBB.js +54 -0
- package/createPageAction-AL5FSDCK.js +171 -0
- package/db-HCJ5PRB5.js +271 -0
- package/dbSlice-PR44QPXY.js +60 -0
- package/deleteFile-4SKGPHOZ.js +41 -0
- package/dialogCommands-F6YI5742.js +63 -0
- package/dialogSlice-JSBNZRLR.js +148 -0
- package/docCreateCommands-4CTO7ERT.js +219 -0
- package/docDeleteCommands-VBWERDRG.js +180 -0
- package/docReadCommands-KNP4MFYB.js +133 -0
- package/docStore-ADP5VYY4.js +98 -0
- package/docUpdateCommands-4BCBJQHA.js +317 -0
- package/fetchMessages-U6ZHN56A.js +17 -0
- package/getPricing-3E6UNPKL.js +30 -0
- package/handleSendMessageAction-IJDQ6ARA.js +185 -0
- package/index.js +188 -112943
- package/lib-FYYBQIXS.js +19839 -0
- package/localDialogRead-Z5UTAJPU.js +26 -0
- package/localFirstLog-DHNOY2NA.js +19 -0
- package/localRuntimeAuthority-TEGCOWX4.js +471 -0
- package/localRuntimeDb-LGDF6FQP.js +12 -0
- package/machineCommands-EA3M4XTN.js +779 -0
- package/machineInfo-M6KB6HZN.js +10 -0
- package/machineWsRunDispatch-NAPKE5ZA.js +110 -0
- package/memoryCommands-O4MIPIWQ.js +476 -0
- package/models-4OPOOLFS.js +9 -0
- package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
- package/package.json +3 -25
- package/read-RIJCIVKX.js +17 -0
- package/readDocTool-GZD2N3LO.js +16 -0
- package/readlineWorkspace-KMWT4T4S.js +5509 -0
- package/removeDialogAgentAction-GJQJNDW6.js +73 -0
- package/runAgentClientLoop-U5Q6NDTJ.js +291 -0
- package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
- package/setDialogExtraReferencesAction-M7OQG54N.js +60 -0
- package/setPrimaryDialogAgentAction-46UNFJDL.js +80 -0
- package/settingSlice-FVUUH4JA.js +199 -0
- package/skillSummaryMarker-42HKSXYY.js +9 -0
- package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
- package/slateUtils-2M7MUTWX.js +19 -0
- package/spaceCommands-E2CFRPZJ.js +454 -0
- package/spaceSlice-L3R6IMXE.js +147 -0
- package/streamAgentChatTurn-UBDAOQWE.js +4528 -0
- package/tableCommands-GOFP7QKA.js +1286 -0
- package/toolApiClient-QI3YBNWB.js +26 -0
- package/tools-TMJXZTCA.js +85 -0
- package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
- package/understanding-GQKNDDWM.js +271 -0
- package/updateCommands-VOSPKXFF.js +31 -0
- package/updateDialogTitleAction-ZTWRDXI2.js +188 -0
|
@@ -0,0 +1,1948 @@
|
|
|
1
|
+
import {
|
|
2
|
+
clipHeadAndTail
|
|
3
|
+
} from "./chunk-JEXVIMYZ.js";
|
|
4
|
+
import {
|
|
5
|
+
prepareTools
|
|
6
|
+
} from "./chunk-CPB2YM4D.js";
|
|
7
|
+
import {
|
|
8
|
+
getPublicImageAgentMode
|
|
9
|
+
} from "./chunk-VIDPVFP3.js";
|
|
10
|
+
import {
|
|
11
|
+
performFetchRequest
|
|
12
|
+
} from "./chunk-SBR32ZZS.js";
|
|
13
|
+
import {
|
|
14
|
+
createSSEParser,
|
|
15
|
+
createToolRunId,
|
|
16
|
+
findToolExecutor,
|
|
17
|
+
getToolResultErrorData,
|
|
18
|
+
toolDefinitionsByName,
|
|
19
|
+
toolRunFailed,
|
|
20
|
+
toolRunSetPending,
|
|
21
|
+
toolRunStarted,
|
|
22
|
+
toolRunSucceeded
|
|
23
|
+
} from "./chunk-MKMWT7SA.js";
|
|
24
|
+
import {
|
|
25
|
+
addToolMessage,
|
|
26
|
+
getModelInfo,
|
|
27
|
+
messageStreamEnd,
|
|
28
|
+
messageStreaming,
|
|
29
|
+
selectCurrentSpaceId,
|
|
30
|
+
updateToolMessage
|
|
31
|
+
} from "./chunk-ZIG5LJ7X.js";
|
|
32
|
+
import {
|
|
33
|
+
streamAgentChatTurn
|
|
34
|
+
} from "./chunk-4K2KKCSD.js";
|
|
35
|
+
import {
|
|
36
|
+
EMPTY_ASSISTANT_REPAIR_PROMPT
|
|
37
|
+
} from "./chunk-XS466O7S.js";
|
|
38
|
+
import {
|
|
39
|
+
createThinkParserState,
|
|
40
|
+
flushThinkParser,
|
|
41
|
+
processThinkChunk
|
|
42
|
+
} from "./chunk-OPZYGHUC.js";
|
|
43
|
+
import {
|
|
44
|
+
getApiEndpoint
|
|
45
|
+
} from "./chunk-BBYVOSU2.js";
|
|
46
|
+
import {
|
|
47
|
+
isNoloHostedProvider
|
|
48
|
+
} from "./chunk-IWX4UW2E.js";
|
|
49
|
+
import {
|
|
50
|
+
extractCustomId
|
|
51
|
+
} from "./chunk-DFFIZ25G.js";
|
|
52
|
+
import {
|
|
53
|
+
addActiveController,
|
|
54
|
+
removeActiveController,
|
|
55
|
+
tokenUsageLiveUpdate
|
|
56
|
+
} from "./chunk-3ASDTTEI.js";
|
|
57
|
+
import {
|
|
58
|
+
toErrorMessage
|
|
59
|
+
} from "./chunk-HNMXYYJR.js";
|
|
60
|
+
import {
|
|
61
|
+
selectCurrentServer
|
|
62
|
+
} from "./chunk-TSS3XJY4.js";
|
|
63
|
+
import {
|
|
64
|
+
write
|
|
65
|
+
} from "./chunk-COMXDWNM.js";
|
|
66
|
+
import {
|
|
67
|
+
createDialogMessageKeyAndId,
|
|
68
|
+
dialogMessageKey,
|
|
69
|
+
toTrimmedString
|
|
70
|
+
} from "./chunk-SH2CFQOH.js";
|
|
71
|
+
import {
|
|
72
|
+
createAsyncThunk,
|
|
73
|
+
selectIdentityToken
|
|
74
|
+
} from "./chunk-EYEEHCU4.js";
|
|
75
|
+
import {
|
|
76
|
+
isAbortError
|
|
77
|
+
} from "./chunk-QYXLLLGL.js";
|
|
78
|
+
import {
|
|
79
|
+
asTrimmedString
|
|
80
|
+
} from "./chunk-3EUW3WEO.js";
|
|
81
|
+
import {
|
|
82
|
+
asOptionalTrimmedString
|
|
83
|
+
} from "./chunk-G3SUCT4W.js";
|
|
84
|
+
import {
|
|
85
|
+
isRecord
|
|
86
|
+
} from "./chunk-KUG52IAP.js";
|
|
87
|
+
|
|
88
|
+
// packages/integrations/openai/filterAndCleanMessages.ts
|
|
89
|
+
var isValidMessagePart = (part) => {
|
|
90
|
+
if (!part || typeof part !== "object") return false;
|
|
91
|
+
if (part.type === "text") return typeof part.text === "string";
|
|
92
|
+
if (part.type === "image_url") {
|
|
93
|
+
const img = part.image_url;
|
|
94
|
+
return img && typeof img === "object" && typeof img.url === "string" && img.url.length > 0;
|
|
95
|
+
}
|
|
96
|
+
return false;
|
|
97
|
+
};
|
|
98
|
+
var extractContent = (msg) => {
|
|
99
|
+
if (msg.role === "tool") {
|
|
100
|
+
const llmContext = asTrimmedString(msg.toolPayload?.llmContext);
|
|
101
|
+
if (llmContext) return clipHeadAndTail(llmContext, { toolCallId: extractToolCallId(msg) ?? void 0 }).content;
|
|
102
|
+
const summary = asTrimmedString(msg.toolPayload?.summary);
|
|
103
|
+
if (summary) return clipHeadAndTail(summary, { toolCallId: extractToolCallId(msg) ?? void 0 }).content;
|
|
104
|
+
if (typeof msg.content === "string") return clipHeadAndTail(msg.content, { toolCallId: extractToolCallId(msg) ?? void 0 }).content;
|
|
105
|
+
if (msg.content != null) return clipHeadAndTail(JSON.stringify(msg.content), { toolCallId: extractToolCallId(msg) ?? void 0 }).content;
|
|
106
|
+
return null;
|
|
107
|
+
}
|
|
108
|
+
if (typeof msg.content === "string") return msg.content;
|
|
109
|
+
if (Array.isArray(msg.content)) {
|
|
110
|
+
const parts = msg.content.filter(isValidMessagePart);
|
|
111
|
+
return parts.length > 0 ? parts : null;
|
|
112
|
+
}
|
|
113
|
+
if (msg.content && typeof msg.content === "object" && isValidMessagePart(msg.content)) {
|
|
114
|
+
return [msg.content];
|
|
115
|
+
}
|
|
116
|
+
return null;
|
|
117
|
+
};
|
|
118
|
+
var extractToolCalls = (msg) => {
|
|
119
|
+
const calls = msg.tool_calls ?? msg.toolCalls;
|
|
120
|
+
return Array.isArray(calls) && calls.length > 0 ? calls : void 0;
|
|
121
|
+
};
|
|
122
|
+
var extractToolCallId = (msg) => msg.tool_call_id ?? msg.toolCallId ?? msg.toolPayload?.rawToolCall?.id;
|
|
123
|
+
var extractName = (msg) => {
|
|
124
|
+
if (typeof msg.name === "string") return msg.name;
|
|
125
|
+
if (typeof msg.toolName === "string") return msg.toolName;
|
|
126
|
+
if (typeof msg.toolPayload?.toolName === "string") return msg.toolPayload.toolName;
|
|
127
|
+
return void 0;
|
|
128
|
+
};
|
|
129
|
+
var getToolName = (msg) => msg?.toolName ?? msg?.toolPayload?.toolName;
|
|
130
|
+
var isHandoffToolMessage = (msg) => {
|
|
131
|
+
const toolName = getToolName(msg);
|
|
132
|
+
return toolName === "runStreamingAgent";
|
|
133
|
+
};
|
|
134
|
+
var getToolMessageCallId = (msg) => msg?.tool_call_id ?? msg?.toolCallId ?? msg?.toolPayload?.rawToolCall?.id;
|
|
135
|
+
var collectHandoffToolCallIds = (msgs) => {
|
|
136
|
+
const ids = /* @__PURE__ */ new Set();
|
|
137
|
+
for (const msg of msgs) {
|
|
138
|
+
if (msg?.role !== "tool" || !isHandoffToolMessage(msg)) continue;
|
|
139
|
+
const id = getToolMessageCallId(msg);
|
|
140
|
+
if (id) ids.add(id);
|
|
141
|
+
}
|
|
142
|
+
return ids;
|
|
143
|
+
};
|
|
144
|
+
var stripHandoffToolMessage = (msg) => msg?.role === "tool" && isHandoffToolMessage(msg) ? null : msg;
|
|
145
|
+
var removeToolCallsById = (calls, ids) => calls?.filter((call) => !ids.has(call.id));
|
|
146
|
+
var stripHandoffToolCallsFromAssistant = (msg, handoffIds) => {
|
|
147
|
+
if (msg?.role !== "assistant") return msg;
|
|
148
|
+
const calls = msg.tool_calls ?? msg.toolCalls;
|
|
149
|
+
if (!calls?.some((call) => handoffIds.has(call.id))) return msg;
|
|
150
|
+
const kept = removeToolCallsById(calls, handoffIds);
|
|
151
|
+
return {
|
|
152
|
+
...msg,
|
|
153
|
+
tool_calls: kept?.length ? kept : void 0,
|
|
154
|
+
toolCalls: kept?.length ? kept : void 0
|
|
155
|
+
};
|
|
156
|
+
};
|
|
157
|
+
var stripHandoffToolMessages = (msgs) => {
|
|
158
|
+
const handoffIds = collectHandoffToolCallIds(msgs);
|
|
159
|
+
if (handoffIds.size === 0) return msgs;
|
|
160
|
+
const out = [];
|
|
161
|
+
for (const raw of msgs) {
|
|
162
|
+
const stripped = stripHandoffToolMessage(raw);
|
|
163
|
+
if (!stripped) continue;
|
|
164
|
+
out.push(stripHandoffToolCallsFromAssistant(stripped, handoffIds));
|
|
165
|
+
}
|
|
166
|
+
return out;
|
|
167
|
+
};
|
|
168
|
+
var toOpenAIMessage = (rawMsg) => {
|
|
169
|
+
const msg = rawMsg;
|
|
170
|
+
const { role, id } = msg;
|
|
171
|
+
if (!role || !["system", "user", "assistant", "tool"].includes(role)) return null;
|
|
172
|
+
const toolCalls = extractToolCalls(msg);
|
|
173
|
+
let content = extractContent(msg);
|
|
174
|
+
if (content === null) {
|
|
175
|
+
if (role === "assistant" && toolCalls) content = "";
|
|
176
|
+
else return null;
|
|
177
|
+
}
|
|
178
|
+
const cleaned = { role, content };
|
|
179
|
+
if (id) cleaned.id = id;
|
|
180
|
+
const name = extractName(msg);
|
|
181
|
+
if (name) cleaned.name = name;
|
|
182
|
+
if (role === "assistant" && toolCalls) {
|
|
183
|
+
cleaned.tool_calls = toolCalls;
|
|
184
|
+
}
|
|
185
|
+
if (role === "tool") {
|
|
186
|
+
const toolCallId = extractToolCallId(msg);
|
|
187
|
+
if (toolCallId) cleaned.tool_call_id = toolCallId;
|
|
188
|
+
if (!cleaned.tool_call_id) {
|
|
189
|
+
console.warn("[filterAndCleanMessages] \u4E22\u5F03\u65E0\u6548 tool \u6D88\u606F\uFF1A\u7F3A\u5C11 tool_call_id", msg);
|
|
190
|
+
return null;
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
return cleaned;
|
|
194
|
+
};
|
|
195
|
+
var removeOrphanedToolPairs = (msgs) => {
|
|
196
|
+
const dropIndexes = /* @__PURE__ */ new Set();
|
|
197
|
+
const toolToAssistant = /* @__PURE__ */ new Map();
|
|
198
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
199
|
+
if (msgs[i].role !== "tool") continue;
|
|
200
|
+
for (let j = i - 1; j >= 0; j--) {
|
|
201
|
+
if (msgs[j].role === "assistant") {
|
|
202
|
+
toolToAssistant.set(i, j);
|
|
203
|
+
break;
|
|
204
|
+
}
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
for (let i = 0; i < msgs.length; i++) {
|
|
208
|
+
if (msgs[i].role === "tool" && !toolToAssistant.has(i)) {
|
|
209
|
+
dropIndexes.add(i);
|
|
210
|
+
console.warn("[filterAndCleanMessages] \u4E22\u5F03\u5B64\u7ACB tool \u6D88\u606F\uFF1A\u7F3A\u5C11\u524D\u7F6E assistant", msgs[i].tool_call_id);
|
|
211
|
+
}
|
|
212
|
+
}
|
|
213
|
+
for (const [toolIdx, assistantIdx] of toolToAssistant) {
|
|
214
|
+
const toolMsg = msgs[toolIdx];
|
|
215
|
+
if (!toolMsg.tool_call_id) continue;
|
|
216
|
+
const knownIds = new Set((msgs[assistantIdx].tool_calls ?? []).map((tc) => tc.id));
|
|
217
|
+
if (!knownIds.has(toolMsg.tool_call_id)) {
|
|
218
|
+
dropIndexes.add(toolIdx);
|
|
219
|
+
console.warn("[filterAndCleanMessages] \u4E22\u5F03\u5B64\u7ACB tool \u6D88\u606F:", toolMsg.tool_call_id);
|
|
220
|
+
}
|
|
221
|
+
}
|
|
222
|
+
for (const assistantIdx of new Set(toolToAssistant.values())) {
|
|
223
|
+
if (dropIndexes.has(assistantIdx)) continue;
|
|
224
|
+
const stub = msgs[assistantIdx];
|
|
225
|
+
const content = typeof stub.content === "string" ? stub.content.trim() : stub.content;
|
|
226
|
+
const isEmpty = content === "" || Array.isArray(content) && content.length === 0;
|
|
227
|
+
if (!isEmpty) continue;
|
|
228
|
+
const allToolsDropped = (stub.tool_calls ?? []).every((tc) => {
|
|
229
|
+
return msgs.every(
|
|
230
|
+
(m, idx) => m.role !== "tool" || m.tool_call_id !== tc.id || dropIndexes.has(idx)
|
|
231
|
+
);
|
|
232
|
+
});
|
|
233
|
+
if (allToolsDropped) {
|
|
234
|
+
dropIndexes.add(assistantIdx);
|
|
235
|
+
console.warn("[filterAndCleanMessages] \u4E00\u5E76\u4E22\u5F03\u5B64\u7ACB assistant stub\uFF08index:", assistantIdx, ")");
|
|
236
|
+
}
|
|
237
|
+
}
|
|
238
|
+
if (dropIndexes.size === 0) return msgs;
|
|
239
|
+
return msgs.filter((_, idx) => !dropIndexes.has(idx));
|
|
240
|
+
};
|
|
241
|
+
var filterAndCleanMessages = (msgs) => {
|
|
242
|
+
if (!Array.isArray(msgs)) return [];
|
|
243
|
+
const flat = msgs.flat();
|
|
244
|
+
const preprocessed = stripHandoffToolMessages(flat);
|
|
245
|
+
const cleaned = preprocessed.map(toOpenAIMessage).filter((m) => m !== null);
|
|
246
|
+
return removeOrphanedToolPairs(cleaned);
|
|
247
|
+
};
|
|
248
|
+
|
|
249
|
+
// packages/chat/messages/persistToolMessage.ts
|
|
250
|
+
async function persistToolMessage(dispatch, message, options = {}) {
|
|
251
|
+
const id = asOptionalTrimmedString(message?.id);
|
|
252
|
+
const dbKey = asOptionalTrimmedString(message?.dbKey);
|
|
253
|
+
if (!id || !dbKey) {
|
|
254
|
+
const err = new Error(
|
|
255
|
+
`[persistToolMessage] missing id/dbKey (id=${String(id)} dbKey=${String(dbKey)})`
|
|
256
|
+
);
|
|
257
|
+
if (options.soft) {
|
|
258
|
+
console.error(err.message);
|
|
259
|
+
return;
|
|
260
|
+
}
|
|
261
|
+
throw err;
|
|
262
|
+
}
|
|
263
|
+
const { controller: _controller, ...rest } = message;
|
|
264
|
+
const isStreaming = options.isStreaming !== void 0 ? options.isStreaming : Boolean(rest.isStreaming);
|
|
265
|
+
try {
|
|
266
|
+
const writeRequest = dispatch(
|
|
267
|
+
write({
|
|
268
|
+
data: {
|
|
269
|
+
...rest,
|
|
270
|
+
id,
|
|
271
|
+
dbKey,
|
|
272
|
+
role: "tool",
|
|
273
|
+
isStreaming,
|
|
274
|
+
type: "msg" /* MSG */
|
|
275
|
+
},
|
|
276
|
+
customKey: dbKey
|
|
277
|
+
})
|
|
278
|
+
);
|
|
279
|
+
if (writeRequest && typeof writeRequest.unwrap === "function") {
|
|
280
|
+
await writeRequest.unwrap();
|
|
281
|
+
return;
|
|
282
|
+
}
|
|
283
|
+
await writeRequest;
|
|
284
|
+
} catch (error) {
|
|
285
|
+
console.error("[persistToolMessage] write failed", { id, dbKey, error });
|
|
286
|
+
if (!options.soft) throw error;
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
async function persistToolMessages(dispatch, messages, options = {}) {
|
|
290
|
+
for (const message of messages) {
|
|
291
|
+
await persistToolMessage(dispatch, message, options);
|
|
292
|
+
}
|
|
293
|
+
}
|
|
294
|
+
|
|
295
|
+
// packages/ai/chat/updateTotalUsage.ts
|
|
296
|
+
function updateTotalUsage(currentUsage, newUsageChunk) {
|
|
297
|
+
if (!newUsageChunk) {
|
|
298
|
+
return currentUsage;
|
|
299
|
+
}
|
|
300
|
+
if (!currentUsage) {
|
|
301
|
+
return {
|
|
302
|
+
completion_tokens: 0,
|
|
303
|
+
prompt_tokens: 0,
|
|
304
|
+
total_tokens: 0,
|
|
305
|
+
...newUsageChunk
|
|
306
|
+
};
|
|
307
|
+
}
|
|
308
|
+
const updatedUsage = { ...currentUsage };
|
|
309
|
+
if (newUsageChunk.completion_tokens !== void 0) updatedUsage.completion_tokens = newUsageChunk.completion_tokens;
|
|
310
|
+
if (newUsageChunk.prompt_tokens !== void 0) updatedUsage.prompt_tokens = newUsageChunk.prompt_tokens;
|
|
311
|
+
if (newUsageChunk.total_tokens !== void 0) updatedUsage.total_tokens = newUsageChunk.total_tokens;
|
|
312
|
+
if (newUsageChunk.prompt_tokens_details) {
|
|
313
|
+
updatedUsage.prompt_tokens_details = {
|
|
314
|
+
...updatedUsage.prompt_tokens_details || {},
|
|
315
|
+
...newUsageChunk.prompt_tokens_details
|
|
316
|
+
};
|
|
317
|
+
}
|
|
318
|
+
if (newUsageChunk.completion_tokens_details) {
|
|
319
|
+
updatedUsage.completion_tokens_details = {
|
|
320
|
+
...updatedUsage.completion_tokens_details || {},
|
|
321
|
+
...newUsageChunk.completion_tokens_details
|
|
322
|
+
};
|
|
323
|
+
}
|
|
324
|
+
if (typeof newUsageChunk.cost === "number") {
|
|
325
|
+
updatedUsage.cost = newUsageChunk.cost;
|
|
326
|
+
}
|
|
327
|
+
if (newUsageChunk.cost_details) {
|
|
328
|
+
updatedUsage.cost_details = {
|
|
329
|
+
...updatedUsage.cost_details || {},
|
|
330
|
+
...newUsageChunk.cost_details
|
|
331
|
+
};
|
|
332
|
+
}
|
|
333
|
+
const billingProvider = asOptionalTrimmedString(newUsageChunk.billing_provider);
|
|
334
|
+
if (billingProvider) {
|
|
335
|
+
updatedUsage.billing_provider = billingProvider;
|
|
336
|
+
}
|
|
337
|
+
const billingModel = asOptionalTrimmedString(newUsageChunk.billing_model);
|
|
338
|
+
if (billingModel) {
|
|
339
|
+
updatedUsage.billing_model = billingModel;
|
|
340
|
+
}
|
|
341
|
+
return updatedUsage;
|
|
342
|
+
}
|
|
343
|
+
|
|
344
|
+
// packages/chat/messages/toolThunks.ts
|
|
345
|
+
var TOOL_ARGS_SENTINELS = [
|
|
346
|
+
"<|tool_calls_section_end|>",
|
|
347
|
+
"<|tool_calls_end|>",
|
|
348
|
+
"<|endofjson|>"
|
|
349
|
+
];
|
|
350
|
+
function cleanToolArguments(argStr) {
|
|
351
|
+
if (!argStr) return argStr;
|
|
352
|
+
let cleaned = argStr;
|
|
353
|
+
for (const s of TOOL_ARGS_SENTINELS) {
|
|
354
|
+
const idx = cleaned.indexOf(s);
|
|
355
|
+
if (idx >= 0) {
|
|
356
|
+
cleaned = cleaned.slice(0, idx);
|
|
357
|
+
}
|
|
358
|
+
}
|
|
359
|
+
return cleaned.trim();
|
|
360
|
+
}
|
|
361
|
+
var defaultSummary = (toolName, status) => {
|
|
362
|
+
if (status === "pending") return `\u23F8\uFE0F ${toolName} \u7B49\u5F85\u786E\u8BA4/\u6388\u6743`;
|
|
363
|
+
if (status === "running") return `\u23F3 ${toolName} \u6267\u884C\u4E2D\u2026`;
|
|
364
|
+
if (status === "failed") return `\u274C ${toolName} \u6267\u884C\u5931\u8D25`;
|
|
365
|
+
return `\u2705 ${toolName} \u6267\u884C\u5B8C\u6210`;
|
|
366
|
+
};
|
|
367
|
+
var processToolData = createAsyncThunk(
|
|
368
|
+
"message/processToolData",
|
|
369
|
+
async (args, thunkApi) => {
|
|
370
|
+
const { toolCall, parentMessageId, toolRunId } = args;
|
|
371
|
+
const { dispatch, rejectWithValue } = thunkApi;
|
|
372
|
+
const func = toolCall.function;
|
|
373
|
+
if (!func || !func.name) {
|
|
374
|
+
throw new Error(
|
|
375
|
+
"Invalid tool call data: missing function or function.name"
|
|
376
|
+
);
|
|
377
|
+
}
|
|
378
|
+
const toolCallId = toolCall.id || `call_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
379
|
+
const rawToolName = func.name;
|
|
380
|
+
let toolArgs = func.arguments;
|
|
381
|
+
const found = findToolExecutor(rawToolName);
|
|
382
|
+
const canonicalName = found.canonicalName;
|
|
383
|
+
const def = toolDefinitionsByName[canonicalName];
|
|
384
|
+
const behavior = def?.behavior;
|
|
385
|
+
const interaction = def?.interaction ?? "auto";
|
|
386
|
+
if (typeof toolArgs === "string") {
|
|
387
|
+
const cleaned = cleanToolArguments(toolArgs);
|
|
388
|
+
try {
|
|
389
|
+
toolArgs = JSON.parse(cleaned);
|
|
390
|
+
} catch (e) {
|
|
391
|
+
console.error(
|
|
392
|
+
"[ToolThunks/processToolData] toolArgs JSON.parse failed:",
|
|
393
|
+
toolArgs
|
|
394
|
+
);
|
|
395
|
+
throw new Error(`Failed to parse tool arguments JSON: ${e}`);
|
|
396
|
+
}
|
|
397
|
+
}
|
|
398
|
+
const activity = isRecord(toolArgs) && isRecord(toolArgs._activity) ? toolArgs._activity : void 0;
|
|
399
|
+
const executionToolArgs = isRecord(toolArgs) && Object.prototype.hasOwnProperty.call(toolArgs, "_activity") ? (({ _activity: _ignored, ...rest }) => rest)(toolArgs) : toolArgs;
|
|
400
|
+
const inputSummary = JSON.stringify(executionToolArgs).slice(0, 400);
|
|
401
|
+
toolRunStarted({
|
|
402
|
+
id: toolRunId,
|
|
403
|
+
messageId: parentMessageId,
|
|
404
|
+
toolName: canonicalName,
|
|
405
|
+
behavior,
|
|
406
|
+
inputSummary,
|
|
407
|
+
interaction,
|
|
408
|
+
input: executionToolArgs
|
|
409
|
+
});
|
|
410
|
+
if (interaction === "confirm" || interaction === "authorize") {
|
|
411
|
+
try {
|
|
412
|
+
toolRunSetPending({ id: toolRunId });
|
|
413
|
+
const previewExecutor = def?.previewExecutor;
|
|
414
|
+
const previewResult = previewExecutor ? await previewExecutor(executionToolArgs, thunkApi, { parentMessageId }) : {
|
|
415
|
+
rawData: {
|
|
416
|
+
previewOnly: true,
|
|
417
|
+
toolName: canonicalName,
|
|
418
|
+
interaction
|
|
419
|
+
}
|
|
420
|
+
};
|
|
421
|
+
const rawData = previewResult?.rawData ?? previewResult;
|
|
422
|
+
const summary = typeof previewResult?.displayData === "string" && previewResult.displayData.trim() || (interaction === "authorize" ? `\u{1F510} ${canonicalName} \u9700\u8981\u6388\u6743\u540E\u6267\u884C` : `\u26A0\uFE0F ${canonicalName} \u9700\u8981\u786E\u8BA4\u540E\u6267\u884C`);
|
|
423
|
+
const toolPayload = {
|
|
424
|
+
toolName: canonicalName,
|
|
425
|
+
status: "pending",
|
|
426
|
+
input: executionToolArgs,
|
|
427
|
+
rawToolCall: toolCall,
|
|
428
|
+
toolRunId,
|
|
429
|
+
summary,
|
|
430
|
+
...activity ? { activity } : {}
|
|
431
|
+
};
|
|
432
|
+
return {
|
|
433
|
+
toolCallId,
|
|
434
|
+
rawResult: rawData,
|
|
435
|
+
summary,
|
|
436
|
+
toolName: canonicalName,
|
|
437
|
+
toolRunId,
|
|
438
|
+
toolPayload,
|
|
439
|
+
hasPendingInteraction: true
|
|
440
|
+
// ✅ 暂停点
|
|
441
|
+
};
|
|
442
|
+
} catch (e) {
|
|
443
|
+
const errorMessage = toErrorMessage(e);
|
|
444
|
+
const structured = getToolResultErrorData(e);
|
|
445
|
+
const rawErrorResult = structured?.rawData !== void 0 ? structured.rawData : { error: errorMessage };
|
|
446
|
+
const summary = asOptionalTrimmedString(structured?.displayData) || `\u274C ${canonicalName} \u9884\u89C8\u5931\u8D25: ${errorMessage}`;
|
|
447
|
+
toolRunFailed({
|
|
448
|
+
id: toolRunId,
|
|
449
|
+
error: errorMessage,
|
|
450
|
+
outputSummary: summary
|
|
451
|
+
});
|
|
452
|
+
const errorPayload = {
|
|
453
|
+
type: e?.name || "Error",
|
|
454
|
+
message: errorMessage,
|
|
455
|
+
code: structured?.code ?? e?.code,
|
|
456
|
+
retryable: structured?.retryable ?? e?.retryable ?? true
|
|
457
|
+
};
|
|
458
|
+
const toolPayload = {
|
|
459
|
+
toolName: canonicalName,
|
|
460
|
+
status: "failed",
|
|
461
|
+
input: executionToolArgs,
|
|
462
|
+
rawToolCall: toolCall,
|
|
463
|
+
error: errorPayload,
|
|
464
|
+
toolRunId,
|
|
465
|
+
summary,
|
|
466
|
+
...activity ? { activity } : {}
|
|
467
|
+
};
|
|
468
|
+
return rejectWithValue({
|
|
469
|
+
toolCallId,
|
|
470
|
+
rawResult: rawErrorResult,
|
|
471
|
+
summary,
|
|
472
|
+
toolName: canonicalName,
|
|
473
|
+
toolRunId,
|
|
474
|
+
toolPayload,
|
|
475
|
+
hasPendingInteraction: true
|
|
476
|
+
});
|
|
477
|
+
}
|
|
478
|
+
}
|
|
479
|
+
try {
|
|
480
|
+
const toolResult = await found.executor(executionToolArgs, thunkApi, {
|
|
481
|
+
parentMessageId,
|
|
482
|
+
toolRunId
|
|
483
|
+
});
|
|
484
|
+
const rawData = toolResult?.rawData ?? toolResult;
|
|
485
|
+
const llmContext = asOptionalTrimmedString(toolResult?.llmContext);
|
|
486
|
+
const summary = asOptionalTrimmedString(toolResult?.displayData) || defaultSummary(canonicalName, "succeeded");
|
|
487
|
+
toolRunSucceeded({
|
|
488
|
+
id: toolRunId,
|
|
489
|
+
outputSummary: summary
|
|
490
|
+
});
|
|
491
|
+
const toolPayload = {
|
|
492
|
+
toolName: canonicalName,
|
|
493
|
+
status: "succeeded",
|
|
494
|
+
input: executionToolArgs,
|
|
495
|
+
rawToolCall: toolCall,
|
|
496
|
+
toolRunId,
|
|
497
|
+
summary,
|
|
498
|
+
...llmContext ? { llmContext } : {},
|
|
499
|
+
...activity ? { activity } : {}
|
|
500
|
+
};
|
|
501
|
+
const isUiAskChoice = canonicalName === "ui_ask_choice";
|
|
502
|
+
const blocking = typeof executionToolArgs?.blocking === "boolean" ? executionToolArgs.blocking : true;
|
|
503
|
+
const hasPendingInteraction = isUiAskChoice && blocking === true;
|
|
504
|
+
return {
|
|
505
|
+
toolCallId,
|
|
506
|
+
rawResult: rawData,
|
|
507
|
+
summary,
|
|
508
|
+
toolName: canonicalName,
|
|
509
|
+
toolRunId,
|
|
510
|
+
toolPayload,
|
|
511
|
+
hasPendingInteraction
|
|
512
|
+
};
|
|
513
|
+
} catch (e) {
|
|
514
|
+
const errorMessage = toErrorMessage(e);
|
|
515
|
+
const structured = getToolResultErrorData(e);
|
|
516
|
+
const requiresConfirmation = structured?.code === "self_evolution_requires_confirmation" || structured?.code === "agent_update_requires_confirmation";
|
|
517
|
+
if (requiresConfirmation) {
|
|
518
|
+
const confirmedInput = {
|
|
519
|
+
...executionToolArgs ?? {},
|
|
520
|
+
__confirmedSelfEvolution: true
|
|
521
|
+
};
|
|
522
|
+
const summary2 = typeof structured?.displayData === "string" && structured.displayData.trim() || `\u26A0\uFE0F ${canonicalName} \u9700\u8981\u786E\u8BA4\u540E\u6267\u884C`;
|
|
523
|
+
toolRunStarted({
|
|
524
|
+
id: toolRunId,
|
|
525
|
+
messageId: parentMessageId,
|
|
526
|
+
toolName: canonicalName,
|
|
527
|
+
behavior,
|
|
528
|
+
inputSummary: JSON.stringify(confirmedInput).slice(0, 400),
|
|
529
|
+
interaction: "confirm",
|
|
530
|
+
input: confirmedInput
|
|
531
|
+
});
|
|
532
|
+
toolRunSetPending({ id: toolRunId });
|
|
533
|
+
const toolPayload2 = {
|
|
534
|
+
toolName: canonicalName,
|
|
535
|
+
status: "pending",
|
|
536
|
+
input: confirmedInput,
|
|
537
|
+
rawToolCall: toolCall,
|
|
538
|
+
toolRunId,
|
|
539
|
+
summary: summary2,
|
|
540
|
+
...activity ? { activity } : {}
|
|
541
|
+
};
|
|
542
|
+
return {
|
|
543
|
+
toolCallId,
|
|
544
|
+
rawResult: structured?.rawData !== void 0 ? structured.rawData : { error: "self_evolution_requires_confirmation" },
|
|
545
|
+
summary: summary2,
|
|
546
|
+
toolName: canonicalName,
|
|
547
|
+
toolRunId,
|
|
548
|
+
toolPayload: toolPayload2,
|
|
549
|
+
hasPendingInteraction: true
|
|
550
|
+
};
|
|
551
|
+
}
|
|
552
|
+
const rawErrorResult = structured?.rawData !== void 0 ? structured.rawData : { error: errorMessage };
|
|
553
|
+
const summary = asOptionalTrimmedString(structured?.displayData) || `\u274C ${canonicalName} \u6267\u884C\u5931\u8D25: ${errorMessage}`;
|
|
554
|
+
toolRunFailed({
|
|
555
|
+
id: toolRunId,
|
|
556
|
+
error: errorMessage,
|
|
557
|
+
outputSummary: summary
|
|
558
|
+
});
|
|
559
|
+
const errorPayload = {
|
|
560
|
+
type: e?.name || "Error",
|
|
561
|
+
message: errorMessage,
|
|
562
|
+
code: structured?.code ?? e?.code,
|
|
563
|
+
retryable: structured?.retryable ?? e?.retryable ?? true
|
|
564
|
+
};
|
|
565
|
+
const toolPayload = {
|
|
566
|
+
toolName: canonicalName,
|
|
567
|
+
status: "failed",
|
|
568
|
+
input: executionToolArgs,
|
|
569
|
+
rawToolCall: toolCall,
|
|
570
|
+
error: errorPayload,
|
|
571
|
+
toolRunId,
|
|
572
|
+
summary,
|
|
573
|
+
...activity ? { activity } : {}
|
|
574
|
+
};
|
|
575
|
+
return rejectWithValue({
|
|
576
|
+
toolCallId,
|
|
577
|
+
rawResult: rawErrorResult,
|
|
578
|
+
summary,
|
|
579
|
+
toolName: canonicalName,
|
|
580
|
+
toolRunId,
|
|
581
|
+
toolPayload,
|
|
582
|
+
hasPendingInteraction: false
|
|
583
|
+
});
|
|
584
|
+
}
|
|
585
|
+
}
|
|
586
|
+
);
|
|
587
|
+
var handleToolCalls = createAsyncThunk(
|
|
588
|
+
"message/handleToolCalls",
|
|
589
|
+
async (args, thunkApi) => {
|
|
590
|
+
const {
|
|
591
|
+
accumulatedCalls,
|
|
592
|
+
currentContentBuffer,
|
|
593
|
+
agentConfig,
|
|
594
|
+
messageId,
|
|
595
|
+
dialogId,
|
|
596
|
+
dialogKey
|
|
597
|
+
} = args;
|
|
598
|
+
const { dispatch } = thunkApi;
|
|
599
|
+
const updatedContentBuffer = [...currentContentBuffer];
|
|
600
|
+
let hasHandedOff = false;
|
|
601
|
+
let hasPendingInteraction = false;
|
|
602
|
+
for (let toolIndex = 0; toolIndex < accumulatedCalls.length; toolIndex++) {
|
|
603
|
+
const toolCall = accumulatedCalls[toolIndex];
|
|
604
|
+
if (!toolCall.function?.name) continue;
|
|
605
|
+
const toolCallId = toolCall.id || `call_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
606
|
+
const toolRunId = createToolRunId();
|
|
607
|
+
const canonicalToolName = findToolExecutor(toolCall.function.name).canonicalName;
|
|
608
|
+
const runningToolMessageId = `${messageId}-t${String(toolIndex).padStart(3, "0")}`;
|
|
609
|
+
const runningToolDbKey = dialogMessageKey(dialogId, runningToolMessageId);
|
|
610
|
+
const runningToolMessage = {
|
|
611
|
+
id: runningToolMessageId,
|
|
612
|
+
dbKey: runningToolDbKey,
|
|
613
|
+
role: "tool",
|
|
614
|
+
content: JSON.stringify({ pending: true }),
|
|
615
|
+
toolCallId,
|
|
616
|
+
thinkContent: "",
|
|
617
|
+
cybotKey: agentConfig.dbKey,
|
|
618
|
+
isStreaming: true,
|
|
619
|
+
toolName: canonicalToolName,
|
|
620
|
+
parentMessageId: messageId,
|
|
621
|
+
toolRunId,
|
|
622
|
+
toolPayload: {
|
|
623
|
+
toolName: canonicalToolName,
|
|
624
|
+
status: "running",
|
|
625
|
+
input: {},
|
|
626
|
+
rawToolCall: toolCall,
|
|
627
|
+
toolRunId,
|
|
628
|
+
summary: defaultSummary(canonicalToolName, "running")
|
|
629
|
+
}
|
|
630
|
+
};
|
|
631
|
+
dispatch(addToolMessage(runningToolMessage));
|
|
632
|
+
await persistToolMessage(dispatch, runningToolMessage, {
|
|
633
|
+
isStreaming: true,
|
|
634
|
+
soft: true
|
|
635
|
+
});
|
|
636
|
+
try {
|
|
637
|
+
const result = await dispatch(
|
|
638
|
+
processToolData({
|
|
639
|
+
toolCall: { ...toolCall, id: toolCallId },
|
|
640
|
+
parentMessageId: messageId,
|
|
641
|
+
toolRunId
|
|
642
|
+
})
|
|
643
|
+
).unwrap();
|
|
644
|
+
const toolName = result.toolName;
|
|
645
|
+
let rawResult = result.rawResult;
|
|
646
|
+
let toolPayload = result.toolPayload;
|
|
647
|
+
if (toolName) {
|
|
648
|
+
const completedToolMessage = {
|
|
649
|
+
...runningToolMessage,
|
|
650
|
+
content: JSON.stringify(rawResult),
|
|
651
|
+
toolCallId: result.toolCallId,
|
|
652
|
+
isStreaming: false,
|
|
653
|
+
toolName,
|
|
654
|
+
toolRunId: result.toolRunId,
|
|
655
|
+
toolPayload
|
|
656
|
+
};
|
|
657
|
+
dispatch(
|
|
658
|
+
updateToolMessage({
|
|
659
|
+
id: runningToolMessageId,
|
|
660
|
+
changes: {
|
|
661
|
+
content: completedToolMessage.content,
|
|
662
|
+
toolCallId: completedToolMessage.toolCallId,
|
|
663
|
+
isStreaming: false,
|
|
664
|
+
toolName,
|
|
665
|
+
toolRunId: result.toolRunId,
|
|
666
|
+
toolPayload
|
|
667
|
+
}
|
|
668
|
+
})
|
|
669
|
+
);
|
|
670
|
+
await persistToolMessage(dispatch, completedToolMessage, {
|
|
671
|
+
isStreaming: false,
|
|
672
|
+
soft: true
|
|
673
|
+
});
|
|
674
|
+
if (result.hasPendingInteraction) {
|
|
675
|
+
hasPendingInteraction = true;
|
|
676
|
+
}
|
|
677
|
+
if (toolName === "runStreamingAgent") {
|
|
678
|
+
const raw = result.rawResult ?? {};
|
|
679
|
+
const agentKey = raw.agentKey;
|
|
680
|
+
const userInput = raw.userInput;
|
|
681
|
+
const serverBase = asOptionalTrimmedString(raw.serverBase);
|
|
682
|
+
if (agentKey && userInput) {
|
|
683
|
+
hasHandedOff = true;
|
|
684
|
+
void dispatch(
|
|
685
|
+
streamAgentChatTurn({
|
|
686
|
+
agentKey,
|
|
687
|
+
userInput,
|
|
688
|
+
...dialogKey ? { dialogKey } : {},
|
|
689
|
+
...serverBase ? { serverBase } : {}
|
|
690
|
+
})
|
|
691
|
+
);
|
|
692
|
+
}
|
|
693
|
+
}
|
|
694
|
+
}
|
|
695
|
+
} catch (rejectedValue) {
|
|
696
|
+
console.error(
|
|
697
|
+
"[ToolThunks/handleToolCalls] processToolData rejected:",
|
|
698
|
+
rejectedValue
|
|
699
|
+
);
|
|
700
|
+
if (rejectedValue.toolName) {
|
|
701
|
+
const errorResult = {
|
|
702
|
+
error: true,
|
|
703
|
+
message: rejectedValue.toolPayload?.error?.message || "\u672A\u77E5\u9519\u8BEF"
|
|
704
|
+
};
|
|
705
|
+
const failedToolMessage = {
|
|
706
|
+
...runningToolMessage,
|
|
707
|
+
content: JSON.stringify(errorResult),
|
|
708
|
+
toolCallId: rejectedValue.toolCallId,
|
|
709
|
+
isStreaming: false,
|
|
710
|
+
toolName: rejectedValue.toolName,
|
|
711
|
+
toolRunId: rejectedValue.toolRunId,
|
|
712
|
+
toolPayload: rejectedValue.toolPayload
|
|
713
|
+
};
|
|
714
|
+
dispatch(
|
|
715
|
+
updateToolMessage({
|
|
716
|
+
id: runningToolMessageId,
|
|
717
|
+
changes: {
|
|
718
|
+
content: failedToolMessage.content,
|
|
719
|
+
toolCallId: failedToolMessage.toolCallId,
|
|
720
|
+
isStreaming: false,
|
|
721
|
+
toolName: rejectedValue.toolName,
|
|
722
|
+
toolRunId: rejectedValue.toolRunId,
|
|
723
|
+
toolPayload: rejectedValue.toolPayload
|
|
724
|
+
}
|
|
725
|
+
})
|
|
726
|
+
);
|
|
727
|
+
await persistToolMessage(dispatch, failedToolMessage, {
|
|
728
|
+
isStreaming: false,
|
|
729
|
+
soft: true
|
|
730
|
+
});
|
|
731
|
+
if (rejectedValue.hasPendingInteraction) {
|
|
732
|
+
hasPendingInteraction = true;
|
|
733
|
+
}
|
|
734
|
+
}
|
|
735
|
+
}
|
|
736
|
+
}
|
|
737
|
+
return {
|
|
738
|
+
finalContentBuffer: updatedContentBuffer,
|
|
739
|
+
hasHandedOff,
|
|
740
|
+
hasPendingInteraction
|
|
741
|
+
};
|
|
742
|
+
}
|
|
743
|
+
);
|
|
744
|
+
|
|
745
|
+
// packages/ai/chat/parseApiError.ts
|
|
746
|
+
async function parseApiError(response) {
|
|
747
|
+
const errorBody = await response.text();
|
|
748
|
+
const truncateErrorMessage = (message, maxChars = 320) => message.length <= maxChars ? message : `${message.slice(0, maxChars)}\u2026`;
|
|
749
|
+
const isContextOverflow = (message) => /maximum context length|context length|context_length_exceeded|requested about .*tokens|too many tokens/i.test(message);
|
|
750
|
+
let defaultMessage = `\u72B6\u6001\u7801 ${response.status} ${response.statusText}`;
|
|
751
|
+
let errorMessage = defaultMessage;
|
|
752
|
+
let errorCode = `E${response.status}`;
|
|
753
|
+
try {
|
|
754
|
+
const errorJson = JSON.parse(errorBody);
|
|
755
|
+
errorMessage = errorJson?.error?.message || errorJson?.message || errorJson?.msg || errorBody || defaultMessage;
|
|
756
|
+
errorCode = errorJson?.error?.code || errorJson?.code || errorCode;
|
|
757
|
+
} catch (_e) {
|
|
758
|
+
if (errorBody) {
|
|
759
|
+
errorMessage = errorBody;
|
|
760
|
+
}
|
|
761
|
+
}
|
|
762
|
+
switch (response.status) {
|
|
763
|
+
case 400:
|
|
764
|
+
if (isContextOverflow(errorMessage) || isContextOverflow(errorBody) || errorCode === "UPSTREAM_400") {
|
|
765
|
+
return "\u4E0A\u4E0B\u6587\u8FC7\u957F\uFF1A\u672C\u8F6E\u6D88\u606F\u6216\u5DE5\u5177\u7ED3\u679C\u592A\u5927\u3002\u8BF7\u7F29\u5C0F\u8303\u56F4\uFF0C\u6216\u5148\u8BFB\u53D6\u66F4\u5C0F\u7247\u6BB5\u540E\u518D\u7EE7\u7EED\u3002";
|
|
766
|
+
}
|
|
767
|
+
if (errorCode === "MISSING_PROVIDER_API_KEY") {
|
|
768
|
+
return truncateErrorMessage(errorMessage);
|
|
769
|
+
}
|
|
770
|
+
if (errorMessage && errorMessage !== defaultMessage) {
|
|
771
|
+
return `\u8BF7\u6C42\u53C2\u6570\u9519\u8BEF: ${truncateErrorMessage(errorMessage)}`;
|
|
772
|
+
}
|
|
773
|
+
return "\u8BF7\u6C42\u53C2\u6570\u9519\u8BEF\uFF0C\u8BF7\u68C0\u67E5\u8F93\u5165";
|
|
774
|
+
case 413:
|
|
775
|
+
return "\u8BF7\u6C42\u5185\u5BB9\u8FC7\u5927\uFF1A\u8BF7\u51CF\u5C11\u4E00\u6B21\u53D1\u9001\u7684\u6D88\u606F\u3001\u6587\u4EF6\u6216\u5DE5\u5177\u7ED3\u679C\u3002";
|
|
776
|
+
case 401:
|
|
777
|
+
switch (errorCode) {
|
|
778
|
+
case "AUTH_TOKEN_EXPIRED":
|
|
779
|
+
return "\u767B\u5F55\u72B6\u6001\u5DF2\u8FC7\u671F\uFF0C\u8BF7\u5148\u767B\u51FA\u540E\u91CD\u65B0\u767B\u5F55";
|
|
780
|
+
case "AUTH_ACCOUNT_INVALID":
|
|
781
|
+
return "\u8D26\u6237\u65E0\u6548\u6216\u5DF2\u88AB\u505C\u7528\uFF0C\u8BF7\u8054\u7CFB\u7BA1\u7406\u5458";
|
|
782
|
+
case "AUTH_NO_TOKEN":
|
|
783
|
+
return "\u672A\u68C0\u6D4B\u5230\u767B\u5F55\u72B6\u6001\uFF0C\u8BF7\u5148\u767B\u5F55";
|
|
784
|
+
case "AUTH_INVALID_TOKEN":
|
|
785
|
+
return "\u767B\u5F55\u51ED\u8BC1\u65E0\u6548\uFF0C\u8BF7\u5148\u767B\u51FA\u540E\u91CD\u65B0\u767B\u5F55";
|
|
786
|
+
case "AUTH_TOKEN_NOT_ACTIVE":
|
|
787
|
+
return "\u4EE4\u724C\u5C1A\u672A\u751F\u6548\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5";
|
|
788
|
+
default:
|
|
789
|
+
return errorMessage && errorMessage !== `\u72B6\u6001\u7801 401 Unauthorized` ? `\u8BA4\u8BC1\u9519\u8BEF: ${truncateErrorMessage(errorMessage)}` : "\u8EAB\u4EFD\u9A8C\u8BC1\u5931\u8D25\uFF0C\u8BF7\u5148\u767B\u51FA\u540E\u91CD\u65B0\u767B\u5F55";
|
|
790
|
+
}
|
|
791
|
+
case 503:
|
|
792
|
+
return errorMessage && errorMessage !== `\u72B6\u6001\u7801 503 Service Unavailable` ? truncateErrorMessage(errorMessage) : "\u670D\u52A1\u6682\u65F6\u4E0D\u53EF\u7528\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5";
|
|
793
|
+
case 504:
|
|
794
|
+
return "\u8BF7\u6C42\u8D85\u65F6\uFF0C\u8BF7\u7A0D\u540E\u518D\u8BD5";
|
|
795
|
+
default:
|
|
796
|
+
return `API\u8BF7\u6C42\u5931\u8D25: ${truncateErrorMessage(errorMessage)}`;
|
|
797
|
+
}
|
|
798
|
+
}
|
|
799
|
+
|
|
800
|
+
// packages/ai/chat/accumulateToolCallChunks.ts
|
|
801
|
+
function accumulateToolCallChunks(currentAccumulatedCalls, toolCallChunks) {
|
|
802
|
+
const out = [...currentAccumulatedCalls];
|
|
803
|
+
for (const chunk of toolCallChunks) {
|
|
804
|
+
const { index, id, type, function: fn } = chunk;
|
|
805
|
+
if (index !== void 0) {
|
|
806
|
+
while (out.length <= index) {
|
|
807
|
+
out.push({
|
|
808
|
+
id: "",
|
|
809
|
+
type: "function",
|
|
810
|
+
function: { name: "", arguments: "" }
|
|
811
|
+
});
|
|
812
|
+
}
|
|
813
|
+
const cur = out[index];
|
|
814
|
+
if (id && !cur.id) cur.id = id;
|
|
815
|
+
if (type && !cur.type) cur.type = type;
|
|
816
|
+
if (!cur.function) cur.function = { name: "", arguments: "" };
|
|
817
|
+
if (fn) {
|
|
818
|
+
if (fn.name) cur.function.name += fn.name;
|
|
819
|
+
if (fn.arguments) {
|
|
820
|
+
if (typeof fn.arguments === "string") {
|
|
821
|
+
const currentArgs = typeof cur.function.arguments === "string" ? cur.function.arguments : "";
|
|
822
|
+
cur.function.arguments = currentArgs + fn.arguments;
|
|
823
|
+
} else {
|
|
824
|
+
cur.function.arguments = fn.arguments;
|
|
825
|
+
}
|
|
826
|
+
}
|
|
827
|
+
}
|
|
828
|
+
continue;
|
|
829
|
+
}
|
|
830
|
+
if (fn?.name || fn?.arguments) {
|
|
831
|
+
let targetIndex = -1;
|
|
832
|
+
if (id) {
|
|
833
|
+
targetIndex = out.findIndex((c) => c.id === id);
|
|
834
|
+
} else if (out.length > 0) {
|
|
835
|
+
targetIndex = out.length - 1;
|
|
836
|
+
}
|
|
837
|
+
if (targetIndex >= 0) {
|
|
838
|
+
const target = out[targetIndex];
|
|
839
|
+
if (fn.name) target.function.name += fn.name;
|
|
840
|
+
if (fn.arguments) {
|
|
841
|
+
if (typeof fn.arguments === "string") {
|
|
842
|
+
const currentArgs = typeof target.function.arguments === "string" ? target.function.arguments : "";
|
|
843
|
+
target.function.arguments = currentArgs + fn.arguments;
|
|
844
|
+
} else {
|
|
845
|
+
target.function.arguments = fn.arguments;
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
} else if (id) {
|
|
849
|
+
const newCall = {
|
|
850
|
+
id,
|
|
851
|
+
type: type || "function",
|
|
852
|
+
function: {
|
|
853
|
+
name: fn.name || "",
|
|
854
|
+
arguments: fn.arguments || (!fn.arguments && typeof fn.arguments === "object" ? {} : "")
|
|
855
|
+
// Initial empty value based on type? Or just default string
|
|
856
|
+
}
|
|
857
|
+
};
|
|
858
|
+
if (fn.arguments) {
|
|
859
|
+
newCall.function.arguments = fn.arguments;
|
|
860
|
+
} else {
|
|
861
|
+
newCall.function.arguments = "";
|
|
862
|
+
}
|
|
863
|
+
out.push(newCall);
|
|
864
|
+
}
|
|
865
|
+
}
|
|
866
|
+
}
|
|
867
|
+
return out;
|
|
868
|
+
}
|
|
869
|
+
|
|
870
|
+
// packages/ai/chat/toolCallArgumentGuard.ts
|
|
871
|
+
var INVALID_TOOL_ARGS_REPLACEMENT = '{"_invalid":true,"_reason":"arguments truncated or malformed"}';
|
|
872
|
+
var INVALID_TOOL_ARGS_OUTBOUND_REPLACEMENT = '{"_invalid":true}';
|
|
873
|
+
var INVALID_TOOL_RESULT_HINT = "\u5DE5\u5177\u53C2\u6570 JSON \u88AB\u622A\u65AD\uFF08\u5927\u6982\u7387\u8D85\u51FA\u8F93\u51FA\u957F\u5EA6\uFF09\u3002\u4E0D\u8981\u628A\u5B8C\u6574\u6E90\u7801\u585E\u8FDB\u4E00\u6B21\u5DE5\u5177\u8C03\u7528\uFF1A\u5148\u7528 appFileWrite \u5206\u6587\u4EF6\u591A\u6B21\u5199\u5165\uFF0C\u518D\u8C03\u7528\u4E0D\u5E26\u5185\u8054\u5927\u53C2\u6570\u7684 appPreflight/appDeploy\u3002";
|
|
874
|
+
var ORPHAN_TOOL_RESULT_PLACEHOLDER = '{"error":"tool call was interrupted"}';
|
|
875
|
+
function parseToolCallArguments(args) {
|
|
876
|
+
if (isRecord(args)) {
|
|
877
|
+
return { valid: true, parsed: args };
|
|
878
|
+
}
|
|
879
|
+
if (typeof args !== "string") return { valid: false };
|
|
880
|
+
const trimmed = args.trim();
|
|
881
|
+
if (!trimmed) return { valid: false };
|
|
882
|
+
try {
|
|
883
|
+
const parsed = JSON.parse(trimmed);
|
|
884
|
+
if (!isRecord(parsed)) {
|
|
885
|
+
return { valid: false };
|
|
886
|
+
}
|
|
887
|
+
return { valid: true, parsed };
|
|
888
|
+
} catch {
|
|
889
|
+
return { valid: false };
|
|
890
|
+
}
|
|
891
|
+
}
|
|
892
|
+
function buildInvalidToolCallSelfHealResult(callId, toolName) {
|
|
893
|
+
return JSON.stringify({
|
|
894
|
+
error: true,
|
|
895
|
+
toolCallId: callId,
|
|
896
|
+
...toolName ? { toolName } : {},
|
|
897
|
+
message: INVALID_TOOL_RESULT_HINT
|
|
898
|
+
});
|
|
899
|
+
}
|
|
900
|
+
function normalizeToolCallArgumentsInPlace(call, replacement) {
|
|
901
|
+
if (!call || typeof call !== "object") return;
|
|
902
|
+
const fn = call.function;
|
|
903
|
+
if (!fn || typeof fn !== "object") return;
|
|
904
|
+
const { valid } = parseToolCallArguments(fn.arguments);
|
|
905
|
+
if (!valid) {
|
|
906
|
+
fn.arguments = replacement;
|
|
907
|
+
} else if (typeof fn.arguments !== "string") {
|
|
908
|
+
try {
|
|
909
|
+
fn.arguments = JSON.stringify(fn.arguments);
|
|
910
|
+
} catch {
|
|
911
|
+
fn.arguments = replacement;
|
|
912
|
+
}
|
|
913
|
+
}
|
|
914
|
+
}
|
|
915
|
+
function sanitizeOutboundMessages(messages, opts = {}) {
|
|
916
|
+
if (!Array.isArray(messages) || messages.length === 0) return messages ?? [];
|
|
917
|
+
const argsReplacement = opts.argsReplacement ?? INVALID_TOOL_ARGS_OUTBOUND_REPLACEMENT;
|
|
918
|
+
const orphanReplacement = opts.orphanReplacement ?? ORPHAN_TOOL_RESULT_PLACEHOLDER;
|
|
919
|
+
const cloned = messages.map((msg) => ({ ...msg }));
|
|
920
|
+
const answeredCallIds = /* @__PURE__ */ new Set();
|
|
921
|
+
for (const msg of cloned) {
|
|
922
|
+
if (msg?.role === "tool" && typeof msg.tool_call_id === "string") {
|
|
923
|
+
answeredCallIds.add(msg.tool_call_id);
|
|
924
|
+
}
|
|
925
|
+
}
|
|
926
|
+
const result = [];
|
|
927
|
+
for (const msg of cloned) {
|
|
928
|
+
if (msg?.role === "assistant" && Array.isArray(msg.tool_calls)) {
|
|
929
|
+
msg.tool_calls = msg.tool_calls.map((call) => ({
|
|
930
|
+
...call,
|
|
931
|
+
function: { ...call?.function ?? {} }
|
|
932
|
+
}));
|
|
933
|
+
for (const call of msg.tool_calls) {
|
|
934
|
+
normalizeToolCallArgumentsInPlace(call, argsReplacement);
|
|
935
|
+
}
|
|
936
|
+
result.push(msg);
|
|
937
|
+
for (const call of msg.tool_calls) {
|
|
938
|
+
const callId = typeof call?.id === "string" ? call.id : void 0;
|
|
939
|
+
if (!callId) continue;
|
|
940
|
+
if (answeredCallIds.has(callId)) continue;
|
|
941
|
+
answeredCallIds.add(callId);
|
|
942
|
+
result.push({
|
|
943
|
+
role: "tool",
|
|
944
|
+
tool_call_id: callId,
|
|
945
|
+
content: orphanReplacement
|
|
946
|
+
});
|
|
947
|
+
}
|
|
948
|
+
continue;
|
|
949
|
+
}
|
|
950
|
+
result.push(msg);
|
|
951
|
+
}
|
|
952
|
+
return result;
|
|
953
|
+
}
|
|
954
|
+
function sanitizeOutboundResponsesInput(input, opts = {}) {
|
|
955
|
+
if (!Array.isArray(input) || input.length === 0) return input ?? [];
|
|
956
|
+
const argsReplacement = opts.argsReplacement ?? INVALID_TOOL_ARGS_OUTBOUND_REPLACEMENT;
|
|
957
|
+
const orphanReplacement = opts.orphanReplacement ?? ORPHAN_TOOL_RESULT_PLACEHOLDER;
|
|
958
|
+
const answeredCallIds = /* @__PURE__ */ new Set();
|
|
959
|
+
for (const item of input) {
|
|
960
|
+
if (item?.type === "function_call_output" && typeof item.call_id === "string") {
|
|
961
|
+
answeredCallIds.add(item.call_id);
|
|
962
|
+
}
|
|
963
|
+
}
|
|
964
|
+
const result = [];
|
|
965
|
+
for (const item of input) {
|
|
966
|
+
if (item?.type === "function_call") {
|
|
967
|
+
const cloned = { ...item };
|
|
968
|
+
const { valid } = parseToolCallArguments(cloned.arguments);
|
|
969
|
+
if (!valid) {
|
|
970
|
+
cloned.arguments = argsReplacement;
|
|
971
|
+
} else if (typeof cloned.arguments !== "string") {
|
|
972
|
+
try {
|
|
973
|
+
cloned.arguments = JSON.stringify(cloned.arguments);
|
|
974
|
+
} catch {
|
|
975
|
+
cloned.arguments = argsReplacement;
|
|
976
|
+
}
|
|
977
|
+
}
|
|
978
|
+
result.push(cloned);
|
|
979
|
+
const callId = typeof cloned.call_id === "string" ? cloned.call_id : void 0;
|
|
980
|
+
if (callId && !answeredCallIds.has(callId)) {
|
|
981
|
+
answeredCallIds.add(callId);
|
|
982
|
+
result.push({
|
|
983
|
+
type: "function_call_output",
|
|
984
|
+
call_id: callId,
|
|
985
|
+
output: orphanReplacement
|
|
986
|
+
});
|
|
987
|
+
}
|
|
988
|
+
continue;
|
|
989
|
+
}
|
|
990
|
+
result.push(item);
|
|
991
|
+
}
|
|
992
|
+
return result;
|
|
993
|
+
}
|
|
994
|
+
|
|
995
|
+
// packages/ai/chat/inlineImageUrlsForCustomProvider.ts
|
|
996
|
+
var FILE_CONTENT_PATH = "/api/v1/db/file/content/";
|
|
997
|
+
var shouldInlineImageUrlsForAgent = (agentConfig) => {
|
|
998
|
+
const apiSource = agentConfig?.apiSource?.toLowerCase();
|
|
999
|
+
const provider = agentConfig?.provider?.toLowerCase();
|
|
1000
|
+
const model = agentConfig?.model?.toLowerCase();
|
|
1001
|
+
if (apiSource === "custom" || provider === "custom") return true;
|
|
1002
|
+
if (isNoloHostedProvider(provider)) return true;
|
|
1003
|
+
return provider === "openrouter" && model === "minimax/minimax-m3";
|
|
1004
|
+
};
|
|
1005
|
+
var isInlineCandidate = (url) => /^https?:\/\//i.test(url) && url.includes(FILE_CONTENT_PATH);
|
|
1006
|
+
var bytesToBase64 = (bytes) => {
|
|
1007
|
+
let binary = "";
|
|
1008
|
+
for (const byte of bytes) {
|
|
1009
|
+
binary += String.fromCharCode(byte);
|
|
1010
|
+
}
|
|
1011
|
+
return btoa(binary);
|
|
1012
|
+
};
|
|
1013
|
+
var defaultFetchImage = async (url) => {
|
|
1014
|
+
const response = await fetch(url);
|
|
1015
|
+
if (!response.ok) {
|
|
1016
|
+
return {
|
|
1017
|
+
ok: false,
|
|
1018
|
+
error: `HTTP ${response.status}`
|
|
1019
|
+
};
|
|
1020
|
+
}
|
|
1021
|
+
const bytes = new Uint8Array(await response.arrayBuffer());
|
|
1022
|
+
return {
|
|
1023
|
+
ok: true,
|
|
1024
|
+
mimeType: response.headers.get("content-type") ?? "application/octet-stream",
|
|
1025
|
+
bytes
|
|
1026
|
+
};
|
|
1027
|
+
};
|
|
1028
|
+
var cloneImagePartWithDataUrl = async (part, fetchImage) => {
|
|
1029
|
+
const url = part?.image_url?.url;
|
|
1030
|
+
if (typeof url !== "string" || !isInlineCandidate(url)) return part;
|
|
1031
|
+
const result = await fetchImage(url);
|
|
1032
|
+
if (!result.ok || !result.bytes) {
|
|
1033
|
+
throw new Error(
|
|
1034
|
+
`Failed to inline custom provider image URL ${url}: ${result.error ?? "unknown error"}`
|
|
1035
|
+
);
|
|
1036
|
+
}
|
|
1037
|
+
const mimeType = result.mimeType || "application/octet-stream";
|
|
1038
|
+
return {
|
|
1039
|
+
...part,
|
|
1040
|
+
image_url: {
|
|
1041
|
+
...part.image_url,
|
|
1042
|
+
url: `data:${mimeType};base64,${bytesToBase64(result.bytes)}`
|
|
1043
|
+
}
|
|
1044
|
+
};
|
|
1045
|
+
};
|
|
1046
|
+
var inlineImageUrlsForCustomProvider = async (bodyData, options) => {
|
|
1047
|
+
if (!options.shouldInline) return bodyData;
|
|
1048
|
+
const body = bodyData;
|
|
1049
|
+
if (!Array.isArray(body?.messages)) return bodyData;
|
|
1050
|
+
const fetchImage = options.fetchImage ?? defaultFetchImage;
|
|
1051
|
+
let changed = false;
|
|
1052
|
+
const messages = [];
|
|
1053
|
+
for (const message of body.messages) {
|
|
1054
|
+
if (!Array.isArray(message?.content)) {
|
|
1055
|
+
messages.push(message);
|
|
1056
|
+
continue;
|
|
1057
|
+
}
|
|
1058
|
+
const content = await Promise.all(
|
|
1059
|
+
message.content.map(async (part) => {
|
|
1060
|
+
if (part?.type !== "image_url") return part;
|
|
1061
|
+
return cloneImagePartWithDataUrl(part, fetchImage);
|
|
1062
|
+
})
|
|
1063
|
+
);
|
|
1064
|
+
let messageChanged = false;
|
|
1065
|
+
for (let i = 0; i < content.length; i++) {
|
|
1066
|
+
if (content[i] !== message.content[i]) {
|
|
1067
|
+
changed = true;
|
|
1068
|
+
messageChanged = true;
|
|
1069
|
+
break;
|
|
1070
|
+
}
|
|
1071
|
+
}
|
|
1072
|
+
messages.push(messageChanged ? { ...message, content } : message);
|
|
1073
|
+
}
|
|
1074
|
+
if (!changed) return bodyData;
|
|
1075
|
+
return {
|
|
1076
|
+
...body,
|
|
1077
|
+
messages
|
|
1078
|
+
};
|
|
1079
|
+
};
|
|
1080
|
+
|
|
1081
|
+
// packages/ai/chat/streamReader.ts
|
|
1082
|
+
function createAbortError(message = "The operation was aborted.") {
|
|
1083
|
+
return new DOMException(message, "AbortError");
|
|
1084
|
+
}
|
|
1085
|
+
async function readStreamChunk(reader, options) {
|
|
1086
|
+
const signal = options?.signal;
|
|
1087
|
+
if (signal?.aborted) {
|
|
1088
|
+
throw createAbortError();
|
|
1089
|
+
}
|
|
1090
|
+
const timeoutMs = options?.timeoutMs;
|
|
1091
|
+
if (!signal && !(timeoutMs && timeoutMs > 0)) {
|
|
1092
|
+
return reader.read();
|
|
1093
|
+
}
|
|
1094
|
+
let settled = false;
|
|
1095
|
+
let timeoutId;
|
|
1096
|
+
let onAbort;
|
|
1097
|
+
return new Promise((resolve, reject) => {
|
|
1098
|
+
const finish = (cb) => {
|
|
1099
|
+
if (settled) return;
|
|
1100
|
+
settled = true;
|
|
1101
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
1102
|
+
if (onAbort && signal) signal.removeEventListener("abort", onAbort);
|
|
1103
|
+
cb();
|
|
1104
|
+
};
|
|
1105
|
+
onAbort = () => {
|
|
1106
|
+
void reader.cancel().catch(() => {
|
|
1107
|
+
});
|
|
1108
|
+
finish(() => reject(createAbortError()));
|
|
1109
|
+
};
|
|
1110
|
+
signal?.addEventListener("abort", onAbort);
|
|
1111
|
+
if (timeoutMs && timeoutMs > 0) {
|
|
1112
|
+
timeoutId = setTimeout(() => {
|
|
1113
|
+
finish(
|
|
1114
|
+
() => reject(
|
|
1115
|
+
new Error(
|
|
1116
|
+
options?.timeoutErrorMessage ?? `Stream stalled for ${Math.round(timeoutMs / 1e3)}s`
|
|
1117
|
+
)
|
|
1118
|
+
)
|
|
1119
|
+
);
|
|
1120
|
+
}, timeoutMs);
|
|
1121
|
+
}
|
|
1122
|
+
reader.read().then(
|
|
1123
|
+
(result) => finish(() => resolve(result)),
|
|
1124
|
+
(err) => finish(() => reject(err))
|
|
1125
|
+
);
|
|
1126
|
+
});
|
|
1127
|
+
}
|
|
1128
|
+
|
|
1129
|
+
// packages/ai/agent/utils/imageOutput.ts
|
|
1130
|
+
var LEGACY_IMAGE_MODELS = /* @__PURE__ */ new Set([
|
|
1131
|
+
"google/gemini-3-pro-image-preview",
|
|
1132
|
+
"google/gemini-3.1-flash-image-preview"
|
|
1133
|
+
]);
|
|
1134
|
+
function supportsImageGeneration(agent) {
|
|
1135
|
+
if (agent.hasImageOutput === true) return true;
|
|
1136
|
+
if (getPublicImageAgentMode(agent) === "continuous") return true;
|
|
1137
|
+
const modelName = toTrimmedString(agent.model);
|
|
1138
|
+
if (!modelName) return false;
|
|
1139
|
+
const modelInfo = getModelInfo(modelName);
|
|
1140
|
+
if (modelInfo) {
|
|
1141
|
+
return !!(modelInfo.hasImageOutput ?? modelInfo.supportsImageOutput);
|
|
1142
|
+
}
|
|
1143
|
+
const normalized = modelName.toLowerCase();
|
|
1144
|
+
if (LEGACY_IMAGE_MODELS.has(normalized)) return true;
|
|
1145
|
+
if (agent.imageConfig?.enabled && agent.hasVision) return true;
|
|
1146
|
+
if (getPublicImageAgentMode(agent) === "continuous") return true;
|
|
1147
|
+
return normalized.includes("image-preview") || normalized.includes("flash-image") || normalized.includes("seedream") || normalized.includes("flux.");
|
|
1148
|
+
}
|
|
1149
|
+
|
|
1150
|
+
// packages/ai/chat/sendOpenAICompletionsRequest.ts
|
|
1151
|
+
function appendTextChunk(currentContentBuffer, textChunk) {
|
|
1152
|
+
if (!textChunk) return currentContentBuffer;
|
|
1153
|
+
const updatedContentBuffer = [...currentContentBuffer];
|
|
1154
|
+
const lastIndex = updatedContentBuffer.length - 1;
|
|
1155
|
+
if (lastIndex >= 0 && updatedContentBuffer[lastIndex].type === "text") {
|
|
1156
|
+
const last = updatedContentBuffer[lastIndex];
|
|
1157
|
+
updatedContentBuffer[lastIndex] = {
|
|
1158
|
+
...last,
|
|
1159
|
+
text: (last.text || "") + textChunk
|
|
1160
|
+
};
|
|
1161
|
+
} else {
|
|
1162
|
+
updatedContentBuffer.push({ type: "text", text: textChunk });
|
|
1163
|
+
}
|
|
1164
|
+
return updatedContentBuffer;
|
|
1165
|
+
}
|
|
1166
|
+
var EXPLICIT_IMAGE_TOOL_NAMES = /* @__PURE__ */ new Set([
|
|
1167
|
+
"openAIGptImage",
|
|
1168
|
+
"openAIGptImageGenerate",
|
|
1169
|
+
"chatgptWebImageGenerate",
|
|
1170
|
+
"openAIGptImageEdit",
|
|
1171
|
+
"geminiProImagePreview"
|
|
1172
|
+
]);
|
|
1173
|
+
function getStreamErrorMessage(data) {
|
|
1174
|
+
const message = asOptionalTrimmedString(data?.error?.message) ?? asOptionalTrimmedString(data?.error?.msg) ?? asOptionalTrimmedString(data?.message);
|
|
1175
|
+
if (message) return message;
|
|
1176
|
+
const code = asOptionalTrimmedString(data?.error?.code) ?? asOptionalTrimmedString(data?.code);
|
|
1177
|
+
if (code) return code;
|
|
1178
|
+
const type = asOptionalTrimmedString(data?.error?.type);
|
|
1179
|
+
if (type) return type;
|
|
1180
|
+
return "Unknown error";
|
|
1181
|
+
}
|
|
1182
|
+
function formatStreamErrorMessage(data) {
|
|
1183
|
+
const rawMessage = getStreamErrorMessage(data);
|
|
1184
|
+
if (/prohibited|violation|terms\s+of\s+service|content\s+policy|safety/i.test(
|
|
1185
|
+
rawMessage
|
|
1186
|
+
)) {
|
|
1187
|
+
return "\u5F53\u524D\u6A21\u578B\u670D\u52A1\u5546\u62D2\u7EDD\u4E86\u8FD9\u6B21\u8BF7\u6C42\u3002\u4F60\u53EF\u4EE5\u7A0D\u540E\u91CD\u8BD5\uFF0C\u6216\u5207\u6362\u5230\u5176\u4ED6\u6A21\u578B\u7EE7\u7EED\u3002";
|
|
1188
|
+
}
|
|
1189
|
+
return rawMessage;
|
|
1190
|
+
}
|
|
1191
|
+
function getChoiceFinishErrorMessage(data, choice) {
|
|
1192
|
+
if (choice?.error || choice?.message || choice?.code) {
|
|
1193
|
+
const message = formatStreamErrorMessage(choice);
|
|
1194
|
+
if (message && message !== "Unknown error") return message;
|
|
1195
|
+
}
|
|
1196
|
+
if (data?.error || data?.message || data?.code) {
|
|
1197
|
+
const message = formatStreamErrorMessage(data);
|
|
1198
|
+
if (message && message !== "Unknown error") return message;
|
|
1199
|
+
}
|
|
1200
|
+
const messageContent = asOptionalTrimmedString(choice?.message?.content);
|
|
1201
|
+
if (messageContent) return messageContent;
|
|
1202
|
+
return null;
|
|
1203
|
+
}
|
|
1204
|
+
var withImageGenerationStage = (messageMetadata, stage) => {
|
|
1205
|
+
const previousState = messageMetadata?.imageGenerationState;
|
|
1206
|
+
if (previousState?.kind !== "image_generation") {
|
|
1207
|
+
return messageMetadata;
|
|
1208
|
+
}
|
|
1209
|
+
return {
|
|
1210
|
+
...messageMetadata ?? {},
|
|
1211
|
+
imageGenerationState: {
|
|
1212
|
+
...previousState,
|
|
1213
|
+
stage
|
|
1214
|
+
}
|
|
1215
|
+
};
|
|
1216
|
+
};
|
|
1217
|
+
var shouldShowImageSavingState = (contentBuffer) => contentBuffer.some((part) => part?.type === "image_url") && !contentBuffer.some(
|
|
1218
|
+
(part) => part?.type === "text" && typeof part.text === "string" && part.text.trim().length > 0
|
|
1219
|
+
);
|
|
1220
|
+
var STREAM_READ_TIMEOUT_MS = 45e3;
|
|
1221
|
+
var logQuickChatPerfStage = (startedAt, stage, details) => {
|
|
1222
|
+
if (!startedAt) return;
|
|
1223
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
1224
|
+
console.info("[QuickChatPerf]", {
|
|
1225
|
+
stage,
|
|
1226
|
+
elapsedMs: now - startedAt,
|
|
1227
|
+
...typeof performance !== "undefined" ? { atMs: now } : {},
|
|
1228
|
+
...details ?? {}
|
|
1229
|
+
});
|
|
1230
|
+
};
|
|
1231
|
+
function createInitialStreamState() {
|
|
1232
|
+
return {
|
|
1233
|
+
contentBuffer: [],
|
|
1234
|
+
totalUsage: null,
|
|
1235
|
+
accumulatedToolCalls: [],
|
|
1236
|
+
reasoningBuffer: "",
|
|
1237
|
+
thinkState: createThinkParserState(),
|
|
1238
|
+
assistantToolCalls: void 0,
|
|
1239
|
+
hasHandedOff: false,
|
|
1240
|
+
hasProcessedToolCalls: false,
|
|
1241
|
+
alreadyFinalized: false,
|
|
1242
|
+
finishReason: null
|
|
1243
|
+
};
|
|
1244
|
+
}
|
|
1245
|
+
function buildRequestBodyWithTools(bodyData, agentConfig, disableToolsForThisRequest) {
|
|
1246
|
+
const messagesForBody = Array.isArray(bodyData?.messages) ? sanitizeOutboundMessages(bodyData.messages) : bodyData?.messages;
|
|
1247
|
+
const baseBody = messagesForBody !== bodyData?.messages ? { ...bodyData, messages: messagesForBody } : bodyData;
|
|
1248
|
+
if (disableToolsForThisRequest) return baseBody;
|
|
1249
|
+
const modelInfo = getModelInfo(agentConfig.model);
|
|
1250
|
+
if (modelInfo?.hasImageOutput) {
|
|
1251
|
+
return baseBody;
|
|
1252
|
+
}
|
|
1253
|
+
const rawTools = agentConfig.tools;
|
|
1254
|
+
if (!Array.isArray(rawTools) || rawTools.length === 0) return baseBody;
|
|
1255
|
+
const hasExplicitImageTool = rawTools.some(
|
|
1256
|
+
(toolName) => typeof toolName === "string" && EXPLICIT_IMAGE_TOOL_NAMES.has(toolName)
|
|
1257
|
+
);
|
|
1258
|
+
if (supportsImageGeneration(agentConfig) && !hasExplicitImageTool) {
|
|
1259
|
+
return baseBody;
|
|
1260
|
+
}
|
|
1261
|
+
const tools = prepareTools(rawTools, { provider: agentConfig.provider });
|
|
1262
|
+
if (!tools.length) return baseBody;
|
|
1263
|
+
return {
|
|
1264
|
+
...baseBody,
|
|
1265
|
+
tools,
|
|
1266
|
+
tool_choice: baseBody.tool_choice ?? "auto"
|
|
1267
|
+
};
|
|
1268
|
+
}
|
|
1269
|
+
async function finalizeStream(state, ctx) {
|
|
1270
|
+
if (state.hasHandedOff || state.alreadyFinalized) return state;
|
|
1271
|
+
const savingMetadata = withImageGenerationStage(
|
|
1272
|
+
ctx.messageMetadata,
|
|
1273
|
+
"saving"
|
|
1274
|
+
);
|
|
1275
|
+
if (savingMetadata && shouldShowImageSavingState(state.contentBuffer)) {
|
|
1276
|
+
ctx.dispatch(
|
|
1277
|
+
messageStreaming({
|
|
1278
|
+
id: ctx.messageId,
|
|
1279
|
+
dialogId: ctx.dialogId,
|
|
1280
|
+
dbKey: ctx.msgKey,
|
|
1281
|
+
content: "",
|
|
1282
|
+
thinkContent: state.reasoningBuffer,
|
|
1283
|
+
role: "assistant",
|
|
1284
|
+
agentKey: ctx.agentConfig.dbKey,
|
|
1285
|
+
cybotKey: ctx.agentConfig.dbKey,
|
|
1286
|
+
...asOptionalTrimmedString(ctx.agentConfig?.name) ? { agentName: asOptionalTrimmedString(ctx.agentConfig?.name) } : {},
|
|
1287
|
+
...savingMetadata ?? {}
|
|
1288
|
+
})
|
|
1289
|
+
);
|
|
1290
|
+
ctx.messageMetadata = savingMetadata;
|
|
1291
|
+
}
|
|
1292
|
+
await ctx.dispatch(
|
|
1293
|
+
messageStreamEnd({
|
|
1294
|
+
finalContentBuffer: state.contentBuffer,
|
|
1295
|
+
totalUsage: state.totalUsage,
|
|
1296
|
+
msgKey: ctx.msgKey,
|
|
1297
|
+
agentConfig: ctx.agentConfig,
|
|
1298
|
+
dialogId: ctx.dialogId,
|
|
1299
|
+
dialogKey: ctx.dialogKey,
|
|
1300
|
+
messageId: ctx.messageId,
|
|
1301
|
+
reasoningBuffer: state.reasoningBuffer,
|
|
1302
|
+
messageMetadata: ctx.messageMetadata,
|
|
1303
|
+
toolCalls: state.assistantToolCalls,
|
|
1304
|
+
spaceId: ctx.spaceId
|
|
1305
|
+
})
|
|
1306
|
+
);
|
|
1307
|
+
return {
|
|
1308
|
+
...state,
|
|
1309
|
+
alreadyFinalized: true
|
|
1310
|
+
};
|
|
1311
|
+
}
|
|
1312
|
+
var EMPTY_UPSTREAM_STREAM_MESSAGE = "\u6A21\u578B\u8FD4\u56DE\u4E86\u7A7A\u54CD\u5E94\uFF0C\u8BF7\u91CD\u8BD5\u6216\u5207\u6362\u5176\u4ED6\u6A21\u578B";
|
|
1313
|
+
function markEmptyCompletionAsError(state, ctx) {
|
|
1314
|
+
const producedNothing = state.contentBuffer.length === 0 && !state.reasoningBuffer.trim() && (state.assistantToolCalls?.length ?? 0) === 0;
|
|
1315
|
+
if (!producedNothing || state.hasHandedOff || state.alreadyFinalized) {
|
|
1316
|
+
return state;
|
|
1317
|
+
}
|
|
1318
|
+
markStreamMessageAborted(ctx, EMPTY_UPSTREAM_STREAM_MESSAGE);
|
|
1319
|
+
return {
|
|
1320
|
+
...state,
|
|
1321
|
+
contentBuffer: appendTextChunk(
|
|
1322
|
+
state.contentBuffer,
|
|
1323
|
+
`[\u9519\u8BEF: ${EMPTY_UPSTREAM_STREAM_MESSAGE}]`
|
|
1324
|
+
)
|
|
1325
|
+
};
|
|
1326
|
+
}
|
|
1327
|
+
function markStreamMessageAborted(ctx, errorMessage) {
|
|
1328
|
+
const base = ctx.messageMetadata ?? {};
|
|
1329
|
+
const previousMetadata = base.metadata && typeof base.metadata === "object" ? base.metadata : {};
|
|
1330
|
+
ctx.messageMetadata = {
|
|
1331
|
+
...base,
|
|
1332
|
+
metadata: {
|
|
1333
|
+
...previousMetadata,
|
|
1334
|
+
error: true,
|
|
1335
|
+
message: errorMessage
|
|
1336
|
+
}
|
|
1337
|
+
};
|
|
1338
|
+
}
|
|
1339
|
+
function applyUsage(state, data) {
|
|
1340
|
+
if (!data.usage) return state;
|
|
1341
|
+
return {
|
|
1342
|
+
...state,
|
|
1343
|
+
totalUsage: updateTotalUsage(state.totalUsage, data.usage)
|
|
1344
|
+
};
|
|
1345
|
+
}
|
|
1346
|
+
function applyDelta(state, delta) {
|
|
1347
|
+
let hasNewVisibleContent = false;
|
|
1348
|
+
let next = { ...state };
|
|
1349
|
+
const reasoningChunk = delta.reasoning_content ?? delta.reasoning ?? "";
|
|
1350
|
+
if (reasoningChunk) {
|
|
1351
|
+
next.reasoningBuffer = (next.reasoningBuffer || "") + reasoningChunk;
|
|
1352
|
+
}
|
|
1353
|
+
if (Array.isArray(delta.tool_calls) && delta.tool_calls.length > 0) {
|
|
1354
|
+
const accumulated = accumulateToolCallChunks(
|
|
1355
|
+
next.accumulatedToolCalls,
|
|
1356
|
+
delta.tool_calls
|
|
1357
|
+
);
|
|
1358
|
+
next = {
|
|
1359
|
+
...next,
|
|
1360
|
+
accumulatedToolCalls: accumulated,
|
|
1361
|
+
assistantToolCalls: accumulated.map((call) => ({
|
|
1362
|
+
id: call.id,
|
|
1363
|
+
type: "function",
|
|
1364
|
+
function: {
|
|
1365
|
+
name: call.function?.name,
|
|
1366
|
+
arguments: typeof call.function?.arguments === "string" ? call.function.arguments : JSON.stringify(call.function?.arguments ?? {})
|
|
1367
|
+
}
|
|
1368
|
+
}))
|
|
1369
|
+
};
|
|
1370
|
+
}
|
|
1371
|
+
const deltaAny = delta;
|
|
1372
|
+
if (Array.isArray(deltaAny.images) && deltaAny.images.length > 0) {
|
|
1373
|
+
next = {
|
|
1374
|
+
...next,
|
|
1375
|
+
contentBuffer: [...next.contentBuffer, ...deltaAny.images]
|
|
1376
|
+
};
|
|
1377
|
+
hasNewVisibleContent = true;
|
|
1378
|
+
}
|
|
1379
|
+
const contentChunk = delta.content || "";
|
|
1380
|
+
if (contentChunk) {
|
|
1381
|
+
const parsed = processThinkChunk(contentChunk, next.thinkState);
|
|
1382
|
+
next.thinkState = parsed.state;
|
|
1383
|
+
if (parsed.reasoning) {
|
|
1384
|
+
next.reasoningBuffer = (next.reasoningBuffer || "") + parsed.reasoning;
|
|
1385
|
+
}
|
|
1386
|
+
if (parsed.content) {
|
|
1387
|
+
next = {
|
|
1388
|
+
...next,
|
|
1389
|
+
contentBuffer: appendTextChunk(next.contentBuffer, parsed.content)
|
|
1390
|
+
};
|
|
1391
|
+
hasNewVisibleContent = true;
|
|
1392
|
+
}
|
|
1393
|
+
}
|
|
1394
|
+
return { state: next, hasNewVisibleContent };
|
|
1395
|
+
}
|
|
1396
|
+
function emitStreamingUpdate(hasNewVisibleContent, state, ctx) {
|
|
1397
|
+
if (!hasNewVisibleContent) return;
|
|
1398
|
+
ctx.dispatch(
|
|
1399
|
+
messageStreaming({
|
|
1400
|
+
id: ctx.messageId,
|
|
1401
|
+
dialogId: ctx.dialogId,
|
|
1402
|
+
dbKey: ctx.msgKey,
|
|
1403
|
+
content: state.contentBuffer,
|
|
1404
|
+
thinkContent: state.reasoningBuffer,
|
|
1405
|
+
role: "assistant",
|
|
1406
|
+
agentKey: ctx.agentConfig.dbKey,
|
|
1407
|
+
cybotKey: ctx.agentConfig.dbKey,
|
|
1408
|
+
...asOptionalTrimmedString(ctx.agentConfig?.name) ? { agentName: asOptionalTrimmedString(ctx.agentConfig?.name) } : {},
|
|
1409
|
+
...ctx.messageMetadata ?? {}
|
|
1410
|
+
})
|
|
1411
|
+
);
|
|
1412
|
+
}
|
|
1413
|
+
function validateAndPartitionToolCalls(state) {
|
|
1414
|
+
const validCalls = [];
|
|
1415
|
+
const invalidCalls = [];
|
|
1416
|
+
for (const call of state.accumulatedToolCalls) {
|
|
1417
|
+
if (!call) continue;
|
|
1418
|
+
const argsValid = parseToolCallArguments(call?.function?.arguments).valid;
|
|
1419
|
+
if (argsValid) {
|
|
1420
|
+
validCalls.push(call);
|
|
1421
|
+
} else {
|
|
1422
|
+
const sanitized = {
|
|
1423
|
+
...call,
|
|
1424
|
+
function: {
|
|
1425
|
+
...call.function ?? {},
|
|
1426
|
+
name: call?.function?.name ?? "",
|
|
1427
|
+
arguments: INVALID_TOOL_ARGS_REPLACEMENT
|
|
1428
|
+
}
|
|
1429
|
+
};
|
|
1430
|
+
invalidCalls.push(sanitized);
|
|
1431
|
+
}
|
|
1432
|
+
}
|
|
1433
|
+
return { validCalls, invalidCalls };
|
|
1434
|
+
}
|
|
1435
|
+
function syncAssistantToolCallsAfterSanitize(state, invalidCalls) {
|
|
1436
|
+
if (!invalidCalls.length) return state.assistantToolCalls ?? [];
|
|
1437
|
+
const invalidById = /* @__PURE__ */ new Map();
|
|
1438
|
+
for (const call of invalidCalls) {
|
|
1439
|
+
if (call?.id) invalidById.set(call.id, call);
|
|
1440
|
+
}
|
|
1441
|
+
const base = Array.isArray(state.assistantToolCalls) ? state.assistantToolCalls : state.accumulatedToolCalls.map((call) => ({
|
|
1442
|
+
id: call.id,
|
|
1443
|
+
type: "function",
|
|
1444
|
+
function: {
|
|
1445
|
+
name: call.function?.name,
|
|
1446
|
+
arguments: typeof call.function?.arguments === "string" ? call.function.arguments : JSON.stringify(call.function?.arguments ?? {})
|
|
1447
|
+
}
|
|
1448
|
+
}));
|
|
1449
|
+
return base.map((call) => {
|
|
1450
|
+
if (!call?.id) return call;
|
|
1451
|
+
const invalid = invalidById.get(call.id);
|
|
1452
|
+
if (!invalid) return call;
|
|
1453
|
+
return {
|
|
1454
|
+
...call,
|
|
1455
|
+
type: "function",
|
|
1456
|
+
function: {
|
|
1457
|
+
name: invalid.function?.name ?? call.function?.name ?? "",
|
|
1458
|
+
arguments: INVALID_TOOL_ARGS_REPLACEMENT
|
|
1459
|
+
}
|
|
1460
|
+
};
|
|
1461
|
+
});
|
|
1462
|
+
}
|
|
1463
|
+
async function persistInvalidToolCallResults(invalidCalls, ctx, startIndex) {
|
|
1464
|
+
for (let i = 0; i < invalidCalls.length; i++) {
|
|
1465
|
+
const call = invalidCalls[i];
|
|
1466
|
+
const callId = call?.id || `call_${Date.now()}_${Math.random().toString(36).slice(2)}`;
|
|
1467
|
+
const toolIndex = startIndex + i;
|
|
1468
|
+
const runningToolMessageId = `${ctx.messageId}-t${String(toolIndex).padStart(3, "0")}`;
|
|
1469
|
+
const runningToolDbKey = dialogMessageKey(ctx.dialogId, runningToolMessageId);
|
|
1470
|
+
const toolName = call?.function?.name || "unknown";
|
|
1471
|
+
const selfHealContent = buildInvalidToolCallSelfHealResult(callId, toolName);
|
|
1472
|
+
const toolMessage = {
|
|
1473
|
+
id: runningToolMessageId,
|
|
1474
|
+
dbKey: runningToolDbKey,
|
|
1475
|
+
role: "tool",
|
|
1476
|
+
content: selfHealContent,
|
|
1477
|
+
toolCallId: callId,
|
|
1478
|
+
thinkContent: "",
|
|
1479
|
+
cybotKey: ctx.agentConfig.dbKey,
|
|
1480
|
+
isStreaming: false,
|
|
1481
|
+
toolName,
|
|
1482
|
+
parentMessageId: ctx.messageId,
|
|
1483
|
+
toolPayload: {
|
|
1484
|
+
toolName,
|
|
1485
|
+
status: "failed",
|
|
1486
|
+
input: {},
|
|
1487
|
+
rawToolCall: call,
|
|
1488
|
+
error: {
|
|
1489
|
+
type: "InvalidToolCallArguments",
|
|
1490
|
+
message: "\u5DE5\u5177\u53C2\u6570 JSON \u88AB\u622A\u65AD\u6216\u975E\u6CD5\uFF0C\u5DF2\u8DF3\u8FC7\u6267\u884C\u5E76\u66FF\u6362\u4E3A\u5360\u4F4D arguments\u3002",
|
|
1491
|
+
retryable: true
|
|
1492
|
+
},
|
|
1493
|
+
summary: `\u274C ${toolName} \u53C2\u6570\u88AB\u622A\u65AD\uFF0C\u5DF2\u8DF3\u8FC7`
|
|
1494
|
+
}
|
|
1495
|
+
};
|
|
1496
|
+
ctx.dispatch(addToolMessage(toolMessage));
|
|
1497
|
+
await persistToolMessage(ctx.dispatch, toolMessage, {
|
|
1498
|
+
isStreaming: false,
|
|
1499
|
+
soft: true
|
|
1500
|
+
});
|
|
1501
|
+
}
|
|
1502
|
+
}
|
|
1503
|
+
async function processAccumulatedToolCalls(state, ctx) {
|
|
1504
|
+
if (!state.accumulatedToolCalls.length) {
|
|
1505
|
+
return {
|
|
1506
|
+
state,
|
|
1507
|
+
hasHandedOff: false,
|
|
1508
|
+
hasPendingInteraction: false
|
|
1509
|
+
};
|
|
1510
|
+
}
|
|
1511
|
+
const { validCalls, invalidCalls } = validateAndPartitionToolCalls(state);
|
|
1512
|
+
const sanitizedAssistantToolCalls = syncAssistantToolCallsAfterSanitize(
|
|
1513
|
+
state,
|
|
1514
|
+
invalidCalls
|
|
1515
|
+
);
|
|
1516
|
+
if (invalidCalls.length > 0) {
|
|
1517
|
+
await persistInvalidToolCallResults(
|
|
1518
|
+
invalidCalls,
|
|
1519
|
+
ctx,
|
|
1520
|
+
validCalls.length
|
|
1521
|
+
);
|
|
1522
|
+
}
|
|
1523
|
+
if (!validCalls.length) {
|
|
1524
|
+
const nextState2 = {
|
|
1525
|
+
...state,
|
|
1526
|
+
accumulatedToolCalls: [],
|
|
1527
|
+
assistantToolCalls: sanitizedAssistantToolCalls,
|
|
1528
|
+
hasProcessedToolCalls: true,
|
|
1529
|
+
hasHandedOff: false
|
|
1530
|
+
};
|
|
1531
|
+
return {
|
|
1532
|
+
state: nextState2,
|
|
1533
|
+
hasHandedOff: false,
|
|
1534
|
+
hasPendingInteraction: false
|
|
1535
|
+
};
|
|
1536
|
+
}
|
|
1537
|
+
const result = await ctx.dispatch(
|
|
1538
|
+
handleToolCalls({
|
|
1539
|
+
accumulatedCalls: validCalls,
|
|
1540
|
+
currentContentBuffer: state.contentBuffer,
|
|
1541
|
+
agentConfig: ctx.agentConfig,
|
|
1542
|
+
messageId: ctx.messageId,
|
|
1543
|
+
dialogId: ctx.dialogId,
|
|
1544
|
+
dialogKey: ctx.dialogKey
|
|
1545
|
+
})
|
|
1546
|
+
).unwrap();
|
|
1547
|
+
const nextState = {
|
|
1548
|
+
...state,
|
|
1549
|
+
contentBuffer: result.finalContentBuffer,
|
|
1550
|
+
accumulatedToolCalls: [],
|
|
1551
|
+
assistantToolCalls: sanitizedAssistantToolCalls,
|
|
1552
|
+
hasProcessedToolCalls: true,
|
|
1553
|
+
hasHandedOff: result.hasHandedOff
|
|
1554
|
+
};
|
|
1555
|
+
return {
|
|
1556
|
+
state: nextState,
|
|
1557
|
+
hasHandedOff: result.hasHandedOff,
|
|
1558
|
+
hasPendingInteraction: result.hasPendingInteraction
|
|
1559
|
+
};
|
|
1560
|
+
}
|
|
1561
|
+
async function readStreamChunkWithTimeout(reader, signal) {
|
|
1562
|
+
return readStreamChunk(reader, {
|
|
1563
|
+
signal,
|
|
1564
|
+
timeoutMs: STREAM_READ_TIMEOUT_MS,
|
|
1565
|
+
timeoutErrorMessage: `\u6A21\u578B\u54CD\u5E94\u6D41 ${Math.round(STREAM_READ_TIMEOUT_MS / 1e3)} \u79D2\u5185\u6CA1\u6709\u8FD4\u56DE\u65B0\u5185\u5BB9`
|
|
1566
|
+
});
|
|
1567
|
+
}
|
|
1568
|
+
async function handleStreamCompletion(state, ctx, finalizeCtx) {
|
|
1569
|
+
let hasHandedOff = false;
|
|
1570
|
+
let hasPendingInteraction = false;
|
|
1571
|
+
const flushed = flushThinkParser(state.thinkState);
|
|
1572
|
+
state.thinkState = flushed.state;
|
|
1573
|
+
if (flushed.reasoning) {
|
|
1574
|
+
state.reasoningBuffer = (state.reasoningBuffer || "") + flushed.reasoning;
|
|
1575
|
+
}
|
|
1576
|
+
if (flushed.content) {
|
|
1577
|
+
state = {
|
|
1578
|
+
...state,
|
|
1579
|
+
contentBuffer: appendTextChunk(state.contentBuffer, flushed.content)
|
|
1580
|
+
};
|
|
1581
|
+
}
|
|
1582
|
+
if (!state.hasProcessedToolCalls && state.accumulatedToolCalls.length > 0) {
|
|
1583
|
+
if (state.totalUsage) {
|
|
1584
|
+
ctx.dispatch(tokenUsageLiveUpdate({
|
|
1585
|
+
input_tokens: state.totalUsage.prompt_tokens ?? state.totalUsage.input_tokens,
|
|
1586
|
+
output_tokens: state.totalUsage.completion_tokens ?? state.totalUsage.output_tokens,
|
|
1587
|
+
cost: state.totalUsage.cost,
|
|
1588
|
+
dialogKey: ctx.dialogKey
|
|
1589
|
+
}));
|
|
1590
|
+
}
|
|
1591
|
+
const toolResult = await processAccumulatedToolCalls(state, {
|
|
1592
|
+
dispatch: ctx.dispatch,
|
|
1593
|
+
agentConfig: ctx.agentConfig,
|
|
1594
|
+
dialogId: ctx.dialogId,
|
|
1595
|
+
dialogKey: ctx.dialogKey,
|
|
1596
|
+
messageId: ctx.messageId
|
|
1597
|
+
});
|
|
1598
|
+
let next = toolResult.state;
|
|
1599
|
+
hasHandedOff ||= toolResult.hasHandedOff;
|
|
1600
|
+
hasPendingInteraction ||= toolResult.hasPendingInteraction;
|
|
1601
|
+
next = await finalizeStream(next, finalizeCtx);
|
|
1602
|
+
return { state: next, hasHandedOff, hasPendingInteraction };
|
|
1603
|
+
}
|
|
1604
|
+
state = markEmptyCompletionAsError(state, finalizeCtx);
|
|
1605
|
+
const finalized = await finalizeStream(state, finalizeCtx);
|
|
1606
|
+
return { state: finalized, hasHandedOff, hasPendingInteraction };
|
|
1607
|
+
}
|
|
1608
|
+
var sendOpenAICompletionsRequest = async ({
|
|
1609
|
+
bodyData,
|
|
1610
|
+
agentConfig,
|
|
1611
|
+
thunkApi,
|
|
1612
|
+
dialogKey,
|
|
1613
|
+
parentMessageId,
|
|
1614
|
+
messageMetadata,
|
|
1615
|
+
disableToolsForThisRequest = false,
|
|
1616
|
+
quickChatPerfStartedAt
|
|
1617
|
+
}) => {
|
|
1618
|
+
const { dispatch, getState, signal: thunkSignal } = thunkApi;
|
|
1619
|
+
const dialogId = extractCustomId(dialogKey);
|
|
1620
|
+
const controller = new AbortController();
|
|
1621
|
+
thunkSignal.addEventListener("abort", () => controller.abort());
|
|
1622
|
+
const signal = controller.signal;
|
|
1623
|
+
const streamSpaceId = selectCurrentSpaceId(getState()) || void 0;
|
|
1624
|
+
let messageId;
|
|
1625
|
+
let msgKey;
|
|
1626
|
+
if (parentMessageId) {
|
|
1627
|
+
messageId = parentMessageId;
|
|
1628
|
+
msgKey = `msg:${dialogId}:${messageId}`;
|
|
1629
|
+
} else {
|
|
1630
|
+
const newIds = createDialogMessageKeyAndId(dialogId);
|
|
1631
|
+
messageId = newIds.messageId;
|
|
1632
|
+
msgKey = newIds.key;
|
|
1633
|
+
}
|
|
1634
|
+
dispatch(addActiveController({ messageId, controller, dialogKey }));
|
|
1635
|
+
const requestBody = await inlineImageUrlsForCustomProvider(
|
|
1636
|
+
buildRequestBodyWithTools(
|
|
1637
|
+
bodyData,
|
|
1638
|
+
agentConfig,
|
|
1639
|
+
disableToolsForThisRequest
|
|
1640
|
+
),
|
|
1641
|
+
{
|
|
1642
|
+
shouldInline: shouldInlineImageUrlsForAgent(agentConfig)
|
|
1643
|
+
}
|
|
1644
|
+
);
|
|
1645
|
+
let streamState = createInitialStreamState();
|
|
1646
|
+
let parseSSE = createSSEParser();
|
|
1647
|
+
let reader;
|
|
1648
|
+
const finalizeCtx = {
|
|
1649
|
+
dispatch,
|
|
1650
|
+
msgKey,
|
|
1651
|
+
dialogId,
|
|
1652
|
+
dialogKey,
|
|
1653
|
+
messageId,
|
|
1654
|
+
agentConfig,
|
|
1655
|
+
spaceId: streamSpaceId,
|
|
1656
|
+
messageMetadata
|
|
1657
|
+
};
|
|
1658
|
+
let hasHandedOffOverall = false;
|
|
1659
|
+
let hasPendingInteractionOverall = false;
|
|
1660
|
+
let lastFinishReason = null;
|
|
1661
|
+
let activeMessageMetadata = messageMetadata;
|
|
1662
|
+
let emptyCompletionRetryUsed = false;
|
|
1663
|
+
const buildMeta = () => ({
|
|
1664
|
+
hasToolCalls: Array.isArray(streamState.assistantToolCalls) && streamState.assistantToolCalls.length > 0,
|
|
1665
|
+
hasPendingInteraction: hasPendingInteractionOverall,
|
|
1666
|
+
hasHandedOff: hasHandedOffOverall,
|
|
1667
|
+
finishReason: lastFinishReason,
|
|
1668
|
+
messageId,
|
|
1669
|
+
usage: streamState.totalUsage ?? void 0
|
|
1670
|
+
});
|
|
1671
|
+
try {
|
|
1672
|
+
if (!parentMessageId) {
|
|
1673
|
+
dispatch(
|
|
1674
|
+
messageStreaming({
|
|
1675
|
+
id: messageId,
|
|
1676
|
+
dialogId,
|
|
1677
|
+
dbKey: msgKey,
|
|
1678
|
+
content: "",
|
|
1679
|
+
role: "assistant",
|
|
1680
|
+
agentKey: agentConfig.dbKey,
|
|
1681
|
+
cybotKey: agentConfig.dbKey,
|
|
1682
|
+
...asOptionalTrimmedString(agentConfig?.name) ? { agentName: asOptionalTrimmedString(agentConfig?.name) } : {},
|
|
1683
|
+
...activeMessageMetadata ?? {},
|
|
1684
|
+
isStreaming: true
|
|
1685
|
+
})
|
|
1686
|
+
);
|
|
1687
|
+
}
|
|
1688
|
+
const api = getApiEndpoint(agentConfig);
|
|
1689
|
+
const token = selectIdentityToken(getState()) ?? "";
|
|
1690
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-completions-fetch-starting", {
|
|
1691
|
+
api,
|
|
1692
|
+
dialogKey
|
|
1693
|
+
});
|
|
1694
|
+
const response = await performFetchRequest({
|
|
1695
|
+
agentConfig,
|
|
1696
|
+
api,
|
|
1697
|
+
bodyData: requestBody,
|
|
1698
|
+
currentServer: selectCurrentServer(getState()),
|
|
1699
|
+
signal,
|
|
1700
|
+
token,
|
|
1701
|
+
dialogId
|
|
1702
|
+
});
|
|
1703
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-completions-fetch-response", {
|
|
1704
|
+
ok: response.ok,
|
|
1705
|
+
status: response.status,
|
|
1706
|
+
dialogKey
|
|
1707
|
+
});
|
|
1708
|
+
activeMessageMetadata = withImageGenerationStage(
|
|
1709
|
+
activeMessageMetadata,
|
|
1710
|
+
"generating"
|
|
1711
|
+
);
|
|
1712
|
+
finalizeCtx.messageMetadata = activeMessageMetadata;
|
|
1713
|
+
if (!parentMessageId && activeMessageMetadata?.imageGenerationState) {
|
|
1714
|
+
dispatch(
|
|
1715
|
+
messageStreaming({
|
|
1716
|
+
id: messageId,
|
|
1717
|
+
dialogId,
|
|
1718
|
+
dbKey: msgKey,
|
|
1719
|
+
content: "",
|
|
1720
|
+
role: "assistant",
|
|
1721
|
+
agentKey: agentConfig.dbKey,
|
|
1722
|
+
cybotKey: agentConfig.dbKey,
|
|
1723
|
+
...asOptionalTrimmedString(agentConfig?.name) ? { agentName: asOptionalTrimmedString(agentConfig?.name) } : {},
|
|
1724
|
+
...activeMessageMetadata ?? {},
|
|
1725
|
+
isStreaming: true
|
|
1726
|
+
})
|
|
1727
|
+
);
|
|
1728
|
+
}
|
|
1729
|
+
if (!response.ok) {
|
|
1730
|
+
const errorMessage = await parseApiError(response);
|
|
1731
|
+
streamState = {
|
|
1732
|
+
...streamState,
|
|
1733
|
+
contentBuffer: appendTextChunk(
|
|
1734
|
+
streamState.contentBuffer,
|
|
1735
|
+
`[\u9519\u8BEF: ${errorMessage}]`
|
|
1736
|
+
)
|
|
1737
|
+
};
|
|
1738
|
+
streamState = await finalizeStream(streamState, finalizeCtx);
|
|
1739
|
+
return buildMeta();
|
|
1740
|
+
}
|
|
1741
|
+
reader = response.body?.getReader();
|
|
1742
|
+
if (!reader) {
|
|
1743
|
+
streamState = markEmptyCompletionAsError(streamState, finalizeCtx);
|
|
1744
|
+
streamState = await finalizeStream(streamState, finalizeCtx);
|
|
1745
|
+
return buildMeta();
|
|
1746
|
+
}
|
|
1747
|
+
let decoder = new TextDecoder();
|
|
1748
|
+
let loggedFirstStreamChunk = false;
|
|
1749
|
+
let loggedFirstParsedEvent = false;
|
|
1750
|
+
let loggedFirstVisibleDelta = false;
|
|
1751
|
+
while (true) {
|
|
1752
|
+
const { done, value } = await readStreamChunkWithTimeout(reader, signal);
|
|
1753
|
+
if (done) {
|
|
1754
|
+
const producedNothing = streamState.contentBuffer.length === 0 && !streamState.reasoningBuffer.trim() && (streamState.assistantToolCalls?.length ?? 0) === 0;
|
|
1755
|
+
if (producedNothing && !emptyCompletionRetryUsed && !streamState.hasHandedOff && !streamState.alreadyFinalized && !signal.aborted) {
|
|
1756
|
+
emptyCompletionRetryUsed = true;
|
|
1757
|
+
const retryResponse = await performFetchRequest({
|
|
1758
|
+
agentConfig,
|
|
1759
|
+
api,
|
|
1760
|
+
bodyData: {
|
|
1761
|
+
...requestBody,
|
|
1762
|
+
messages: [
|
|
1763
|
+
...requestBody.messages ?? [],
|
|
1764
|
+
{ role: "user", content: EMPTY_ASSISTANT_REPAIR_PROMPT }
|
|
1765
|
+
]
|
|
1766
|
+
},
|
|
1767
|
+
currentServer: selectCurrentServer(getState()),
|
|
1768
|
+
signal,
|
|
1769
|
+
token,
|
|
1770
|
+
dialogId
|
|
1771
|
+
});
|
|
1772
|
+
if (retryResponse.ok && retryResponse.body) {
|
|
1773
|
+
streamState = createInitialStreamState();
|
|
1774
|
+
parseSSE = createSSEParser();
|
|
1775
|
+
decoder = new TextDecoder();
|
|
1776
|
+
reader = retryResponse.body.getReader();
|
|
1777
|
+
continue;
|
|
1778
|
+
}
|
|
1779
|
+
}
|
|
1780
|
+
const completion = await handleStreamCompletion(
|
|
1781
|
+
streamState,
|
|
1782
|
+
{
|
|
1783
|
+
dispatch,
|
|
1784
|
+
dialogId,
|
|
1785
|
+
dialogKey: finalizeCtx.dialogKey,
|
|
1786
|
+
messageId,
|
|
1787
|
+
agentConfig
|
|
1788
|
+
},
|
|
1789
|
+
finalizeCtx
|
|
1790
|
+
);
|
|
1791
|
+
streamState = completion.state;
|
|
1792
|
+
hasHandedOffOverall ||= completion.hasHandedOff;
|
|
1793
|
+
hasPendingInteractionOverall ||= completion.hasPendingInteraction;
|
|
1794
|
+
break;
|
|
1795
|
+
}
|
|
1796
|
+
if (!loggedFirstStreamChunk) {
|
|
1797
|
+
loggedFirstStreamChunk = true;
|
|
1798
|
+
logQuickChatPerfStage(
|
|
1799
|
+
quickChatPerfStartedAt,
|
|
1800
|
+
"openai-completions-first-stream-chunk",
|
|
1801
|
+
{ dialogKey, byteLength: value.byteLength }
|
|
1802
|
+
);
|
|
1803
|
+
}
|
|
1804
|
+
const chunk = decoder.decode(value, { stream: true });
|
|
1805
|
+
const parsedResults = parseSSE(chunk);
|
|
1806
|
+
if (parsedResults.length > 0 && !loggedFirstParsedEvent) {
|
|
1807
|
+
loggedFirstParsedEvent = true;
|
|
1808
|
+
logQuickChatPerfStage(
|
|
1809
|
+
quickChatPerfStartedAt,
|
|
1810
|
+
"openai-completions-first-sse-event",
|
|
1811
|
+
{ dialogKey, eventCount: parsedResults.length }
|
|
1812
|
+
);
|
|
1813
|
+
}
|
|
1814
|
+
for (const parsedData of parsedResults) {
|
|
1815
|
+
const dataList = Array.isArray(parsedData) ? parsedData : [parsedData];
|
|
1816
|
+
for (const data of dataList) {
|
|
1817
|
+
streamState = applyUsage(streamState, data);
|
|
1818
|
+
if (data === "[DONE]") {
|
|
1819
|
+
continue;
|
|
1820
|
+
}
|
|
1821
|
+
if (data.error) {
|
|
1822
|
+
const errorMsg = `Error: ${formatStreamErrorMessage(data)}`;
|
|
1823
|
+
streamState = {
|
|
1824
|
+
...streamState,
|
|
1825
|
+
contentBuffer: appendTextChunk(
|
|
1826
|
+
streamState.contentBuffer,
|
|
1827
|
+
`
|
|
1828
|
+
[API Error] ${errorMsg}`
|
|
1829
|
+
)
|
|
1830
|
+
};
|
|
1831
|
+
await reader.cancel();
|
|
1832
|
+
break;
|
|
1833
|
+
}
|
|
1834
|
+
const choice = data.choices?.[0];
|
|
1835
|
+
if (!choice) continue;
|
|
1836
|
+
const delta = choice.delta || {};
|
|
1837
|
+
const { state: updatedState, hasNewVisibleContent } = applyDelta(
|
|
1838
|
+
streamState,
|
|
1839
|
+
delta
|
|
1840
|
+
);
|
|
1841
|
+
streamState = updatedState;
|
|
1842
|
+
if (hasNewVisibleContent && !loggedFirstVisibleDelta) {
|
|
1843
|
+
loggedFirstVisibleDelta = true;
|
|
1844
|
+
logQuickChatPerfStage(
|
|
1845
|
+
quickChatPerfStartedAt,
|
|
1846
|
+
"openai-completions-first-visible-delta",
|
|
1847
|
+
{ dialogKey }
|
|
1848
|
+
);
|
|
1849
|
+
}
|
|
1850
|
+
emitStreamingUpdate(hasNewVisibleContent, streamState, {
|
|
1851
|
+
dispatch,
|
|
1852
|
+
agentConfig,
|
|
1853
|
+
messageId,
|
|
1854
|
+
msgKey,
|
|
1855
|
+
dialogId,
|
|
1856
|
+
messageMetadata: activeMessageMetadata
|
|
1857
|
+
});
|
|
1858
|
+
const finishReason = choice.finish_reason;
|
|
1859
|
+
if (finishReason) {
|
|
1860
|
+
lastFinishReason = finishReason;
|
|
1861
|
+
streamState.finishReason = finishReason;
|
|
1862
|
+
if (finishReason === "tool_calls") {
|
|
1863
|
+
if (streamState.totalUsage) {
|
|
1864
|
+
dispatch(tokenUsageLiveUpdate({
|
|
1865
|
+
input_tokens: streamState.totalUsage.prompt_tokens ?? streamState.totalUsage.input_tokens,
|
|
1866
|
+
output_tokens: streamState.totalUsage.completion_tokens ?? streamState.totalUsage.output_tokens,
|
|
1867
|
+
cost: streamState.totalUsage.cost,
|
|
1868
|
+
dialogKey
|
|
1869
|
+
}));
|
|
1870
|
+
}
|
|
1871
|
+
const toolResult = await processAccumulatedToolCalls(
|
|
1872
|
+
streamState,
|
|
1873
|
+
{
|
|
1874
|
+
dispatch,
|
|
1875
|
+
agentConfig,
|
|
1876
|
+
dialogId,
|
|
1877
|
+
dialogKey,
|
|
1878
|
+
messageId,
|
|
1879
|
+
messageMetadata
|
|
1880
|
+
}
|
|
1881
|
+
);
|
|
1882
|
+
streamState = toolResult.state;
|
|
1883
|
+
hasHandedOffOverall ||= toolResult.hasHandedOff;
|
|
1884
|
+
hasPendingInteractionOverall ||= toolResult.hasPendingInteraction;
|
|
1885
|
+
streamState = await finalizeStream(streamState, finalizeCtx);
|
|
1886
|
+
} else if (finishReason !== "stop") {
|
|
1887
|
+
const finishErrorMessage = finishReason === "error" ? getChoiceFinishErrorMessage(data, choice) : null;
|
|
1888
|
+
streamState = {
|
|
1889
|
+
...streamState,
|
|
1890
|
+
contentBuffer: appendTextChunk(
|
|
1891
|
+
streamState.contentBuffer,
|
|
1892
|
+
finishErrorMessage ? `
|
|
1893
|
+
[API Error] Error: ${finishErrorMessage}` : finishReason === "error" ? "\n[API Error] Error: \u6A21\u578B\u54CD\u5E94\u4EE5 error \u7ED3\u675F\uFF0C\u4F46\u4E0A\u6E38\u672A\u8FD4\u56DE\u5177\u4F53\u9519\u8BEF\u3002\u8BF7\u91CD\u8BD5\uFF0C\u6216\u5207\u6362\u5230\u5176\u4ED6\u652F\u6301\u56FE\u7247\u8F93\u5165\u7684\u6A21\u578B\u3002" : `
|
|
1894
|
+
[\u6D41\u7ED3\u675F\u539F\u56E0: ${finishReason}]`
|
|
1895
|
+
)
|
|
1896
|
+
};
|
|
1897
|
+
}
|
|
1898
|
+
}
|
|
1899
|
+
}
|
|
1900
|
+
}
|
|
1901
|
+
}
|
|
1902
|
+
} catch (error) {
|
|
1903
|
+
let errorText;
|
|
1904
|
+
const isAbort = isAbortError(error);
|
|
1905
|
+
if (isAbort) {
|
|
1906
|
+
errorText = "\n[\u7528\u6237\u4E2D\u65AD]";
|
|
1907
|
+
} else {
|
|
1908
|
+
errorText = `
|
|
1909
|
+
[\u9519\u8BEF: ${toErrorMessage(error)}]`;
|
|
1910
|
+
}
|
|
1911
|
+
console.error("[SSE] sendOpenAICompletionsRequest error:", error);
|
|
1912
|
+
streamState = {
|
|
1913
|
+
...streamState,
|
|
1914
|
+
contentBuffer: appendTextChunk(streamState.contentBuffer, errorText)
|
|
1915
|
+
};
|
|
1916
|
+
if (!isAbort) {
|
|
1917
|
+
markStreamMessageAborted(
|
|
1918
|
+
finalizeCtx,
|
|
1919
|
+
toErrorMessage(error)
|
|
1920
|
+
);
|
|
1921
|
+
}
|
|
1922
|
+
streamState = await finalizeStream(streamState, finalizeCtx);
|
|
1923
|
+
} finally {
|
|
1924
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-completions-stream-finished", {
|
|
1925
|
+
dialogKey
|
|
1926
|
+
});
|
|
1927
|
+
dispatch(removeActiveController({ messageId, dialogKey }));
|
|
1928
|
+
try {
|
|
1929
|
+
await reader?.cancel();
|
|
1930
|
+
} catch (_e) {
|
|
1931
|
+
}
|
|
1932
|
+
}
|
|
1933
|
+
return buildMeta();
|
|
1934
|
+
};
|
|
1935
|
+
|
|
1936
|
+
export {
|
|
1937
|
+
filterAndCleanMessages,
|
|
1938
|
+
persistToolMessages,
|
|
1939
|
+
handleToolCalls,
|
|
1940
|
+
parseApiError,
|
|
1941
|
+
updateTotalUsage,
|
|
1942
|
+
INVALID_TOOL_ARGS_REPLACEMENT,
|
|
1943
|
+
parseToolCallArguments,
|
|
1944
|
+
buildInvalidToolCallSelfHealResult,
|
|
1945
|
+
sanitizeOutboundResponsesInput,
|
|
1946
|
+
readStreamChunk,
|
|
1947
|
+
sendOpenAICompletionsRequest
|
|
1948
|
+
};
|