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.
Files changed (183) hide show
  1. package/_executeModel-UD44KGZ4.js +203 -0
  2. package/action-ZJWE3Y5S.js +28 -0
  3. package/addContentAction-TVDBNJM5.js +26 -0
  4. package/addDialogAgentAction-EKH774VT.js +68 -0
  5. package/agentDeleteCommand-TCPEFJTF.js +327 -0
  6. package/agentEmailCommands-CZ3UILZ4.js +626 -0
  7. package/agentListCommands-SJKO2FFB.js +223 -0
  8. package/agentMachineCommands-PSNABISZ.js +425 -0
  9. package/agentPullCommand-O2MTUAFR.js +122 -0
  10. package/agentRecordCommands-MH2JKFHS.js +94 -0
  11. package/agentRunCommand-4KH6MIZR.js +110 -0
  12. package/agentRunControl-K4VKNEHF.js +57 -0
  13. package/agentRuntimeLocal-SKHJUWSS.js +104 -0
  14. package/agentSlice-3WXXN4VK.js +42 -0
  15. package/authCommand-PXFX6JDT.js +581 -0
  16. package/authCommands-356AYWEK.js +35 -0
  17. package/chunk-2EISRLRC.js +238 -0
  18. package/chunk-2FTTDCD4.js +675 -0
  19. package/chunk-2K2ECKSG.js +222 -0
  20. package/chunk-2PUGVR6E.js +19 -0
  21. package/chunk-3ASDTTEI.js +1774 -0
  22. package/chunk-3EUW3WEO.js +8 -0
  23. package/chunk-3LY4F7SV.js +9293 -0
  24. package/chunk-3TZDXYSF.js +71 -0
  25. package/chunk-3WYQEUBI.js +34 -0
  26. package/chunk-4CQLVGRV.js +50 -0
  27. package/chunk-4IJYA27C.js +342 -0
  28. package/chunk-4K2KKCSD.js +577 -0
  29. package/chunk-4SN6J65J.js +38 -0
  30. package/chunk-4VNS5WPM.js +42 -0
  31. package/chunk-4ZDM73SD.js +34 -0
  32. package/chunk-5CS33IBL.js +301 -0
  33. package/chunk-5KLGBAAR.js +386 -0
  34. package/chunk-5MIDPSSK.js +206 -0
  35. package/chunk-5MMAK75Q.js +15 -0
  36. package/chunk-5PJ3VWCS.js +360 -0
  37. package/chunk-63SLEOLK.js +175 -0
  38. package/chunk-63W5N6P3.js +150 -0
  39. package/chunk-6GANEXVO.js +231 -0
  40. package/chunk-6HYVJV7B.js +102 -0
  41. package/chunk-6WD3OOJS.js +20 -0
  42. package/chunk-7OOXPE3N.js +628 -0
  43. package/chunk-7QRKCBOP.js +1078 -0
  44. package/chunk-ATNTHIZI.js +13 -0
  45. package/chunk-AUQ747NZ.js +902 -0
  46. package/chunk-AVMKZHBC.js +85 -0
  47. package/chunk-AZPCASRE.js +36 -0
  48. package/chunk-BBYVOSU2.js +792 -0
  49. package/chunk-C3RUNRHT.js +51 -0
  50. package/chunk-CF2WZF4N.js +134 -0
  51. package/chunk-CF7PE763.js +10 -0
  52. package/chunk-CKOJQDKR.js +198 -0
  53. package/chunk-COMXDWNM.js +807 -0
  54. package/chunk-CPB2YM4D.js +171 -0
  55. package/chunk-CWMSB67P.js +34 -0
  56. package/chunk-DCOA4QSN.js +105 -0
  57. package/chunk-DFFIZ25G.js +30 -0
  58. package/chunk-DWLEOOGR.js +8 -0
  59. package/chunk-DZ3XJVET.js +231 -0
  60. package/chunk-EHLHSX7R.js +1433 -0
  61. package/chunk-ELRRLDWI.js +478 -0
  62. package/chunk-EXTIQUCI.js +482 -0
  63. package/chunk-EYEEHCU4.js +3066 -0
  64. package/chunk-G3SUCT4W.js +8 -0
  65. package/chunk-G43KEVSC.js +1447 -0
  66. package/chunk-GAKEBQD6.js +30 -0
  67. package/chunk-GBRG75BQ.js +86 -0
  68. package/chunk-GG4AJPSR.js +191 -0
  69. package/chunk-H33743XH.js +25 -0
  70. package/chunk-HNMXYYJR.js +12 -0
  71. package/chunk-I42VC5KR.js +15 -0
  72. package/chunk-IDZTNRP2.js +85 -0
  73. package/chunk-ISNCLGUZ.js +146 -0
  74. package/chunk-IWX4UW2E.js +548 -0
  75. package/chunk-JBTJAPUF.js +35 -0
  76. package/chunk-JEXVIMYZ.js +55 -0
  77. package/chunk-JRKKFRAV.js +537 -0
  78. package/chunk-JZPBVBIY.js +94 -0
  79. package/chunk-KKY4B2Q2.js +21 -0
  80. package/chunk-KOCA5FCS.js +4750 -0
  81. package/chunk-KQHGZWSV.js +63 -0
  82. package/chunk-KTVVPRJS.js +138 -0
  83. package/chunk-KUG52IAP.js +8 -0
  84. package/chunk-KX6YBIHG.js +8 -0
  85. package/chunk-LARL2V6G.js +9017 -0
  86. package/chunk-LCBAMY2M.js +60 -0
  87. package/chunk-LM6IYQ3F.js +16 -0
  88. package/chunk-M76ECQRD.js +13658 -0
  89. package/chunk-MGOJ5R6J.js +113 -0
  90. package/chunk-MKMWT7SA.js +27196 -0
  91. package/chunk-MRC4Q34M.js +188 -0
  92. package/chunk-OLZBQWZU.js +6 -0
  93. package/chunk-OPZYGHUC.js +89 -0
  94. package/chunk-OWOHNGCN.js +50 -0
  95. package/chunk-PAKN26FP.js +239 -0
  96. package/chunk-PCWC3JTN.js +91 -0
  97. package/chunk-PG5PNA7Y.js +939 -0
  98. package/chunk-Q2YZULBH.js +104 -0
  99. package/chunk-QSJ3HFEF.js +8 -0
  100. package/chunk-QYXLLLGL.js +4519 -0
  101. package/chunk-ROWQEJRP.js +139 -0
  102. package/chunk-S7Z2HMCN.js +25 -0
  103. package/chunk-SBR32ZZS.js +1901 -0
  104. package/chunk-SH2CFQOH.js +3391 -0
  105. package/chunk-SL35K7AH.js +226 -0
  106. package/chunk-TILFE6LV.js +381 -0
  107. package/chunk-TSS3XJY4.js +2052 -0
  108. package/chunk-U4TRT5V4.js +246 -0
  109. package/chunk-U6VC4MU4.js +8 -0
  110. package/chunk-UAD2WT54.js +64 -0
  111. package/chunk-UUJDGGAK.js +421 -0
  112. package/chunk-VF4K2QE6.js +21 -0
  113. package/chunk-VHBWHNVT.js +59 -0
  114. package/chunk-VIDPVFP3.js +20 -0
  115. package/chunk-VP4WJWKW.js +8882 -0
  116. package/chunk-W4NVOSFF.js +37 -0
  117. package/chunk-WKM6FH35.js +190 -0
  118. package/chunk-WWC7I6TA.js +8 -0
  119. package/chunk-X7W4TNB4.js +1550 -0
  120. package/chunk-XE635NI4.js +1948 -0
  121. package/chunk-XKIMRZO2.js +221 -0
  122. package/chunk-XS466O7S.js +933 -0
  123. package/chunk-Y5GCAMGP.js +9 -0
  124. package/chunk-YBWOP46I.js +3272 -0
  125. package/chunk-YPRZSPF3.js +148 -0
  126. package/chunk-YYUCRDOK.js +69 -0
  127. package/chunk-Z4NR32XR.js +50 -0
  128. package/chunk-ZIG5LJ7X.js +24765 -0
  129. package/chunk-ZNFJWEN4.js +56 -0
  130. package/chunk-ZQ2UM4KG.js +2878 -0
  131. package/cliExecutor-DNE7TCC4.js +36 -0
  132. package/clipboardy-BVE3LVEH.js +12001 -0
  133. package/createAgentAutomationAction-63OIP6X5.js +866 -0
  134. package/createDialogAction-ZLYZDYBB.js +54 -0
  135. package/createPageAction-AL5FSDCK.js +171 -0
  136. package/db-HCJ5PRB5.js +271 -0
  137. package/dbSlice-PR44QPXY.js +60 -0
  138. package/deleteFile-4SKGPHOZ.js +41 -0
  139. package/dialogCommands-F6YI5742.js +63 -0
  140. package/dialogSlice-JSBNZRLR.js +148 -0
  141. package/docCreateCommands-4CTO7ERT.js +219 -0
  142. package/docDeleteCommands-VBWERDRG.js +180 -0
  143. package/docReadCommands-KNP4MFYB.js +133 -0
  144. package/docStore-ADP5VYY4.js +98 -0
  145. package/docUpdateCommands-4BCBJQHA.js +317 -0
  146. package/fetchMessages-U6ZHN56A.js +17 -0
  147. package/getPricing-3E6UNPKL.js +30 -0
  148. package/handleSendMessageAction-IJDQ6ARA.js +185 -0
  149. package/index.js +188 -112943
  150. package/lib-FYYBQIXS.js +19839 -0
  151. package/localDialogRead-Z5UTAJPU.js +26 -0
  152. package/localFirstLog-DHNOY2NA.js +19 -0
  153. package/localRuntimeAuthority-TEGCOWX4.js +471 -0
  154. package/localRuntimeDb-LGDF6FQP.js +12 -0
  155. package/machineCommands-EA3M4XTN.js +779 -0
  156. package/machineInfo-M6KB6HZN.js +10 -0
  157. package/machineWsRunDispatch-NAPKE5ZA.js +110 -0
  158. package/memoryCommands-O4MIPIWQ.js +476 -0
  159. package/models-4OPOOLFS.js +9 -0
  160. package/offlineMarxistsAgentCommand-BCPJITJL.js +242 -0
  161. package/package.json +3 -25
  162. package/read-RIJCIVKX.js +17 -0
  163. package/readDocTool-GZD2N3LO.js +16 -0
  164. package/readlineWorkspace-KMWT4T4S.js +5509 -0
  165. package/removeDialogAgentAction-GJQJNDW6.js +73 -0
  166. package/runAgentClientLoop-U5Q6NDTJ.js +291 -0
  167. package/runtimeDoctorCommands-LRHLPDRC.js +289 -0
  168. package/setDialogExtraReferencesAction-M7OQG54N.js +60 -0
  169. package/setPrimaryDialogAgentAction-46UNFJDL.js +80 -0
  170. package/settingSlice-FVUUH4JA.js +199 -0
  171. package/skillSummaryMarker-42HKSXYY.js +9 -0
  172. package/slateToRenderMarkdown-Y5IYN73U.js +7 -0
  173. package/slateUtils-2M7MUTWX.js +19 -0
  174. package/spaceCommands-E2CFRPZJ.js +454 -0
  175. package/spaceSlice-L3R6IMXE.js +147 -0
  176. package/streamAgentChatTurn-UBDAOQWE.js +4528 -0
  177. package/tableCommands-GOFP7QKA.js +1286 -0
  178. package/toolApiClient-QI3YBNWB.js +26 -0
  179. package/tools-TMJXZTCA.js +85 -0
  180. package/tuiSummaryLlmCaller-RV3THLZF.js +78 -0
  181. package/understanding-GQKNDDWM.js +271 -0
  182. package/updateCommands-VOSPKXFF.js +31 -0
  183. 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
+ };