nolo-cli 0.1.22 → 0.1.26
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +25 -3
- package/agent-runtime/agentRecordConfig.ts +4 -0
- package/agent-runtime/hostAdapter.ts +2 -0
- package/agent-runtime/hybridRecordStore.ts +30 -9
- package/agent-runtime/index.ts +7 -0
- package/agent-runtime/localLoop.ts +14 -4
- package/agent-runtime/localToolPolicy.ts +22 -10
- package/agent-runtime/localWorkspaceTools.ts +140 -374
- package/agent-runtime/platformChatProvider.ts +3 -0
- package/agent-runtime/runtimeToolPolicy.ts +133 -0
- package/agent-runtime/types.ts +47 -0
- package/agentAliases.ts +1 -1
- package/agentCommandSupport.ts +77 -0
- package/agentInternalCommandEntries.ts +34 -0
- package/agentListCommands.ts +123 -0
- package/agentListHelpers.ts +187 -0
- package/agentMachineCommands.ts +402 -0
- package/agentRecordCommands.ts +141 -0
- package/agentRecordHelpers.ts +395 -0
- package/agentRunCommand.ts +75 -3
- package/agentRuntimeCommands.ts +7 -773
- package/ai/agent/_executeModel.ts +29 -3
- package/ai/agent/buildSystemPrompt.ts +17 -0
- package/ai/agent/cliChatClient.ts +38 -4
- package/ai/agent/cliExecutor.ts +304 -25
- package/ai/agent/createAgentSchema.ts +2 -2
- package/ai/agent/guidedCreation/assistant.ts +82 -0
- package/ai/agent/guidedCreation/capabilities.ts +84 -0
- package/ai/agent/guidedCreation/draft.ts +62 -0
- package/ai/agent/guidedCreation/types.ts +38 -0
- package/ai/agent/machineRunPermissions.ts +21 -3
- package/ai/agent/runtimeGuidance.ts +1 -1
- package/ai/agent/server/fetchPublicAgents.ts +6 -1
- package/ai/agent/streamAgentChatTurn.ts +593 -36
- package/ai/agent/web/useGuidedAgentCreation.ts +217 -0
- package/ai/ai.locale.ts +14 -2
- package/ai/chat/fetchUtils.native.ts +2 -0
- package/ai/chat/fetchUtils.ts +2 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +82 -1
- package/ai/chat/sendOpenAIResponseRequest.ts +65 -0
- package/ai/llm/getPricing.ts +14 -0
- package/ai/llm/kimi.ts +4 -1
- package/ai/llm/modelAvatar.ts +1 -0
- package/ai/llm/providers.ts +4 -1
- package/ai/llm/reasoningModels.ts +1 -0
- package/ai/taskRun/taskRunProtocol.ts +25 -10
- package/ai/token/modelUsageQuery.ts +17 -0
- package/ai/tools/agent/agentTools.ts +38 -0
- package/ai/tools/agent/prepareAgentDraftTool.ts +159 -0
- package/ai/tools/agent/startAgentDialogTool.ts +53 -0
- package/ai/tools/checkEnvTool.ts +52 -20
- package/ai/tools/convertMarxistsBookTool.ts +103 -0
- package/ai/tools/{execBashTool.ts → execShellTool.ts} +44 -161
- package/ai/tools/index.ts +24 -14
- package/ai/tools/marxistsOfflineBook.ts +253 -0
- package/ai/tools/modelUsageTools.ts +9 -2
- package/ai/tools/readTool.ts +31 -0
- package/ai/tools/toolApiClient.ts +35 -1
- package/ai/tools/toolNameAliases.ts +13 -0
- package/cliCommandAdapters.ts +28 -0
- package/cliCommandDispatch.ts +66 -0
- package/cliCommandFactories.ts +34 -0
- package/cliCommandTypes.ts +49 -0
- package/cliEnvHelpers.ts +44 -0
- package/client/agentRun.test.ts +330 -24
- package/client/agentRun.ts +117 -35
- package/client/hybridRecordStore.test.ts +42 -0
- package/client/hybridRecordStore.ts +11 -0
- package/client/localRuntimeAdapter.test.ts +191 -21
- package/client/localRuntimeAdapter.ts +99 -10
- package/client/localRuntimeDryRun.test.ts +5 -2
- package/client/taskRunPrompt.ts +1 -0
- package/commandRegistry.ts +11 -61
- package/connector-experimental/capabilities.ts +1 -0
- package/database/actions/common.ts +4 -3
- package/database/config.ts +19 -0
- package/database/server/coreDataProxy.ts +43 -26
- package/index.ts +54 -191
- package/internalCommandEntries.ts +12 -0
- package/machineCommands.ts +688 -64
- package/offlineMarxistsAgentCommand.ts +314 -0
- package/package.json +22 -2
- package/render/canvas/canvasEditContext.ts +127 -0
- package/render/canvas/canvasRuntime.ts +57 -0
- package/render/canvas/canvasSnapshotParser.ts +76 -0
- package/render/canvas/canvasTree.ts +308 -0
- package/render/canvas/types.ts +46 -0
- package/render/layout/deleteBehavior.ts +52 -0
- package/render/layout/mainLayoutSidebar.ts +17 -0
- package/render/layout/mainLayoutViewMode.ts +56 -0
- package/render/layout/topbarUtils.ts +87 -0
- package/render/layout/useDevReloadPending.ts +30 -0
- package/render/page/createPageAction.ts +183 -0
- package/render/page/docSlice.ts +468 -0
- package/render/page/server/createPage.ts +174 -0
- package/render/page/server/handleCreatePage.ts +91 -0
- package/render/page/server/index.ts +4 -0
- package/render/page/types.ts +17 -0
- package/render/page/useKeyboardSave.ts +48 -0
- package/render/styles/zIndex.ts +12 -0
- package/render/surf/WeatherIconStyles.ts +17 -0
- package/render/surf/color.ts +9 -0
- package/render/surf/config.ts +46 -0
- package/render/surf/screens/style.ts +1 -0
- package/render/surf/styles/ToggleButtonStyles.ts +8 -0
- package/render/surf/utils/groupedWeatherData.ts +32 -0
- package/render/surf/weatherUtils.ts +50 -0
- package/render/table/activityColumns.ts +6 -0
- package/render/table/createTableAction.ts +270 -0
- package/render/table/deleteTableAction.ts +129 -0
- package/render/table/fetchAndCacheTableRows.ts +174 -0
- package/render/table/tableSlice.ts +1106 -0
- package/render/table/tableView.ts +289 -0
- package/render/table/toolValueUtils.ts +363 -0
- package/render/table/types.ts +252 -0
- package/render/table/useCreateTable.ts +72 -0
- package/render/table/useTable.ts +61 -0
- package/render/table/utils/tableSerialization.ts +50 -0
- package/render/web/elements/artifactPreviewCode.ts +43 -0
- package/render/web/elements/artifactRuntimePreload.ts +52 -0
- package/render/web/elements/codeBlockAutoPreview.ts +10 -0
- package/render/web/elements/mermaidPreview.ts +21 -0
- package/render/web/ui/useInlineEdit.ts +135 -0
- package/runtimeDoctorCommands.ts +118 -0
- package/scriptCommandEntries.ts +71 -0
- package/systemInternalCommandEntries.ts +71 -0
- package/tableCommands.ts +42 -5
- package/workflowInternalCommandEntries.ts +11 -0
|
@@ -4,7 +4,7 @@ import { createDialogMessageKeyAndId } from "database/keys";
|
|
|
4
4
|
import { DataType } from "create/types";
|
|
5
5
|
|
|
6
6
|
import type { RootState } from "app/store";
|
|
7
|
-
import { patch, read } from "database/dbSlice";
|
|
7
|
+
import { patch, read, selectById } from "database/dbSlice";
|
|
8
8
|
import { generateRequestBody } from "ai/llm/generateRequestBody";
|
|
9
9
|
import {
|
|
10
10
|
selectCurrentDialogConfig,
|
|
@@ -26,7 +26,7 @@ import {
|
|
|
26
26
|
getFullChatContextKeys,
|
|
27
27
|
deduplicateContextKeys,
|
|
28
28
|
} from "ai/agent/getFullChatContextKeys";
|
|
29
|
-
import type { Agent } from "app/types";
|
|
29
|
+
import type { Agent, DialogConfig } from "app/types";
|
|
30
30
|
import { isResponseAPIModel } from "ai/llm/isResponseAPIModel";
|
|
31
31
|
import { getModelContextWindow } from "ai/llm/getModelContextWindow";
|
|
32
32
|
import { resolveAgentImageInputSupport } from "ai/llm/agentCapabilities";
|
|
@@ -56,7 +56,11 @@ import {
|
|
|
56
56
|
import { buildCliPrompt } from "./cliPrompt";
|
|
57
57
|
import { createCliChatTurnStream } from "./cliChatClient";
|
|
58
58
|
import { getCliChatSession, startCliChatSession } from "./cliChatClient";
|
|
59
|
-
import {
|
|
59
|
+
import {
|
|
60
|
+
messageStreamEnd,
|
|
61
|
+
messageStreaming,
|
|
62
|
+
prepareAndPersistUserMessage,
|
|
63
|
+
} from "chat/messages/messageSlice";
|
|
60
64
|
import { selectCurrentToken, selectUserId } from "auth/authSlice";
|
|
61
65
|
import { persistMessageWithFixedId } from "./persistMessageWithFixedId";
|
|
62
66
|
import { updateTotalUsage } from "../chat/updateTotalUsage";
|
|
@@ -65,6 +69,11 @@ import {
|
|
|
65
69
|
extractAgentRuntimeServerBase,
|
|
66
70
|
normalizeServerOrigin,
|
|
67
71
|
} from "./runtimeServerBase";
|
|
72
|
+
import { getIsDesktopApp } from "app/utils/env";
|
|
73
|
+
import { runDesktopAgentRuntimeTurn } from "app/utils/desktopAgentRuntimeTurnClient";
|
|
74
|
+
|
|
75
|
+
const BUILTIN_NOLO_AGENT_ID = "01NOLOAPPBLD000000019KCKT0";
|
|
76
|
+
const BUILTIN_NOLO_AGENT_KEY = `agent-pub-${BUILTIN_NOLO_AGENT_ID}`;
|
|
68
77
|
|
|
69
78
|
const buildParallelMessageMetadata = (
|
|
70
79
|
agentConfig: Pick<Agent, "dbKey" | "name">,
|
|
@@ -106,6 +115,25 @@ const filterMessagesForParallelBranch = (
|
|
|
106
115
|
});
|
|
107
116
|
};
|
|
108
117
|
|
|
118
|
+
const isDesktopBuiltinNoloAgent = (
|
|
119
|
+
requestedAgentKey: string,
|
|
120
|
+
agentConfig: Partial<Agent> | null | undefined,
|
|
121
|
+
) => {
|
|
122
|
+
if (!getIsDesktopApp()) return false;
|
|
123
|
+
const candidates = [
|
|
124
|
+
requestedAgentKey,
|
|
125
|
+
agentConfig?.dbKey,
|
|
126
|
+
agentConfig?.agentKey,
|
|
127
|
+
agentConfig?.id,
|
|
128
|
+
];
|
|
129
|
+
return candidates.some((value) =>
|
|
130
|
+
typeof value === "string" &&
|
|
131
|
+
(value === BUILTIN_NOLO_AGENT_KEY ||
|
|
132
|
+
value === BUILTIN_NOLO_AGENT_ID ||
|
|
133
|
+
value.endsWith(`-${BUILTIN_NOLO_AGENT_ID}`))
|
|
134
|
+
);
|
|
135
|
+
};
|
|
136
|
+
|
|
109
137
|
const formatMachineAgentRunError = async (response: Response): Promise<string> => {
|
|
110
138
|
const errorText = await response.text();
|
|
111
139
|
let payload: any = null;
|
|
@@ -146,8 +174,174 @@ export interface StreamAgentChatTurnArgs {
|
|
|
146
174
|
isStreaming?: boolean;
|
|
147
175
|
parentMessageId?: string;
|
|
148
176
|
runtimeOptions?: AgentRuntimeOptions;
|
|
177
|
+
quickChatPerfStartedAt?: number;
|
|
149
178
|
}
|
|
150
179
|
|
|
180
|
+
const QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS = 10_000;
|
|
181
|
+
const QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS = 5_000;
|
|
182
|
+
const EMPTY_DYNAMIC_CONTEXTS = {
|
|
183
|
+
currentInputContext: null,
|
|
184
|
+
historyContext: "",
|
|
185
|
+
editingContext: null,
|
|
186
|
+
appWorkingMemory: null,
|
|
187
|
+
dialogSummary: null,
|
|
188
|
+
proactiveSummary: null,
|
|
189
|
+
referenceKeys: [],
|
|
190
|
+
};
|
|
191
|
+
|
|
192
|
+
function appendCliCapabilityWarnings(content: string, warnings: string[]): string {
|
|
193
|
+
if (!warnings.length) return content;
|
|
194
|
+
const warningBlock = `\n\n[CLI 能力提示]\n${warnings.map((warning) => `- ${warning}`).join("\n")}`;
|
|
195
|
+
return `${content}${warningBlock}`;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
const logQuickChatPerfStage = (
|
|
199
|
+
startedAt: number | undefined,
|
|
200
|
+
stage: string,
|
|
201
|
+
details: Record<string, unknown> = {}
|
|
202
|
+
) => {
|
|
203
|
+
if (!startedAt) return;
|
|
204
|
+
const now = typeof performance !== "undefined" ? performance.now() : Date.now();
|
|
205
|
+
console.info("[QuickChatPerf]", {
|
|
206
|
+
stage,
|
|
207
|
+
elapsedMs: now - startedAt,
|
|
208
|
+
...(typeof performance !== "undefined" ? { atMs: now } : {}),
|
|
209
|
+
...details,
|
|
210
|
+
});
|
|
211
|
+
};
|
|
212
|
+
|
|
213
|
+
const readAgentConfigForTurn = async (
|
|
214
|
+
dispatch: any,
|
|
215
|
+
agentKey: string,
|
|
216
|
+
quickChatPerfStartedAt?: number,
|
|
217
|
+
): Promise<Agent> => {
|
|
218
|
+
const readPromise = dispatch(read({ dbKey: agentKey })).unwrap() as Promise<Agent>;
|
|
219
|
+
if (!quickChatPerfStartedAt) return readPromise;
|
|
220
|
+
|
|
221
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
222
|
+
try {
|
|
223
|
+
return await Promise.race([
|
|
224
|
+
readPromise,
|
|
225
|
+
new Promise<Agent>((_, reject) => {
|
|
226
|
+
timeoutId = setTimeout(() => {
|
|
227
|
+
logQuickChatPerfStage(
|
|
228
|
+
quickChatPerfStartedAt,
|
|
229
|
+
"quick-chat-agent-config-read-timeout",
|
|
230
|
+
{
|
|
231
|
+
agentKey,
|
|
232
|
+
timeoutMs: QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS,
|
|
233
|
+
},
|
|
234
|
+
);
|
|
235
|
+
reject(
|
|
236
|
+
new Error("读取 Agent 配置超时,未能启动模型回复。"),
|
|
237
|
+
);
|
|
238
|
+
}, QUICK_CHAT_AGENT_CONFIG_READ_TIMEOUT_MS);
|
|
239
|
+
}),
|
|
240
|
+
]);
|
|
241
|
+
} finally {
|
|
242
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
243
|
+
}
|
|
244
|
+
};
|
|
245
|
+
|
|
246
|
+
const buildDynamicContextsForTurn = async (
|
|
247
|
+
state: RootState,
|
|
248
|
+
dispatch: any,
|
|
249
|
+
agentConfig: Agent,
|
|
250
|
+
userInput: string | any[],
|
|
251
|
+
runtimeOptions: AgentRuntimeOptions | undefined,
|
|
252
|
+
mergedContentCache: Map<string, any>,
|
|
253
|
+
dialogKey: string | undefined,
|
|
254
|
+
quickChatPerfStartedAt?: number,
|
|
255
|
+
) => {
|
|
256
|
+
if (!quickChatPerfStartedAt) {
|
|
257
|
+
return buildDynamicContexts(
|
|
258
|
+
state,
|
|
259
|
+
dispatch,
|
|
260
|
+
agentConfig,
|
|
261
|
+
userInput,
|
|
262
|
+
runtimeOptions,
|
|
263
|
+
mergedContentCache,
|
|
264
|
+
dialogKey,
|
|
265
|
+
);
|
|
266
|
+
}
|
|
267
|
+
|
|
268
|
+
let timeoutId: ReturnType<typeof setTimeout> | undefined;
|
|
269
|
+
try {
|
|
270
|
+
const timeoutPromise = new Promise<typeof EMPTY_DYNAMIC_CONTEXTS>((resolve) => {
|
|
271
|
+
timeoutId = setTimeout(() => {
|
|
272
|
+
logQuickChatPerfStage(
|
|
273
|
+
quickChatPerfStartedAt,
|
|
274
|
+
"quick-chat-dynamic-context-timeout",
|
|
275
|
+
{
|
|
276
|
+
agentKey: agentConfig.dbKey,
|
|
277
|
+
dialogKey,
|
|
278
|
+
timeoutMs: QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS,
|
|
279
|
+
},
|
|
280
|
+
);
|
|
281
|
+
resolve(EMPTY_DYNAMIC_CONTEXTS);
|
|
282
|
+
}, QUICK_CHAT_DYNAMIC_CONTEXT_TIMEOUT_MS);
|
|
283
|
+
});
|
|
284
|
+
|
|
285
|
+
logQuickChatPerfStage(
|
|
286
|
+
quickChatPerfStartedAt,
|
|
287
|
+
"stream-agent-dynamic-context-starting",
|
|
288
|
+
{
|
|
289
|
+
responseApi: isResponseAPIModel(agentConfig),
|
|
290
|
+
},
|
|
291
|
+
);
|
|
292
|
+
const contextPromise = buildDynamicContexts(
|
|
293
|
+
state,
|
|
294
|
+
dispatch,
|
|
295
|
+
agentConfig,
|
|
296
|
+
userInput,
|
|
297
|
+
runtimeOptions,
|
|
298
|
+
mergedContentCache,
|
|
299
|
+
dialogKey,
|
|
300
|
+
);
|
|
301
|
+
|
|
302
|
+
return await Promise.race([
|
|
303
|
+
contextPromise,
|
|
304
|
+
timeoutPromise,
|
|
305
|
+
]);
|
|
306
|
+
} finally {
|
|
307
|
+
if (timeoutId) clearTimeout(timeoutId);
|
|
308
|
+
}
|
|
309
|
+
};
|
|
310
|
+
|
|
311
|
+
const finalizeQuickChatAgentTurnFailure = async (
|
|
312
|
+
dispatch: any,
|
|
313
|
+
dialogKey: string,
|
|
314
|
+
agentKey: string,
|
|
315
|
+
error: unknown,
|
|
316
|
+
) => {
|
|
317
|
+
const dialogId = extractCustomId(dialogKey);
|
|
318
|
+
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
319
|
+
const reason =
|
|
320
|
+
error instanceof Error && error.message.trim()
|
|
321
|
+
? error.message.trim()
|
|
322
|
+
: "未能启动模型回复,请重试。";
|
|
323
|
+
|
|
324
|
+
await dispatch(
|
|
325
|
+
messageStreamEnd({
|
|
326
|
+
finalContentBuffer: [
|
|
327
|
+
{
|
|
328
|
+
type: "text",
|
|
329
|
+
text: `[错误: ${reason}]`,
|
|
330
|
+
},
|
|
331
|
+
],
|
|
332
|
+
totalUsage: null,
|
|
333
|
+
msgKey,
|
|
334
|
+
agentConfig: {
|
|
335
|
+
dbKey: agentKey,
|
|
336
|
+
},
|
|
337
|
+
dialogId,
|
|
338
|
+
dialogKey,
|
|
339
|
+
messageId,
|
|
340
|
+
reasoningBuffer: "",
|
|
341
|
+
})
|
|
342
|
+
).unwrap?.();
|
|
343
|
+
};
|
|
344
|
+
|
|
151
345
|
const normalizeAgentRunUserInput = (userInput: string | any[]) => {
|
|
152
346
|
if (typeof userInput === "string") {
|
|
153
347
|
return userInput;
|
|
@@ -166,6 +360,75 @@ const normalizeAgentRunUserInput = (userInput: string | any[]) => {
|
|
|
166
360
|
});
|
|
167
361
|
};
|
|
168
362
|
|
|
363
|
+
const isSimpleTextInput = (userInput: string | any[]) => {
|
|
364
|
+
if (typeof userInput === "string") return true;
|
|
365
|
+
if (!Array.isArray(userInput)) return false;
|
|
366
|
+
return userInput.every(
|
|
367
|
+
(part) =>
|
|
368
|
+
part &&
|
|
369
|
+
typeof part === "object" &&
|
|
370
|
+
part.type === "text" &&
|
|
371
|
+
typeof part.text === "string",
|
|
372
|
+
);
|
|
373
|
+
};
|
|
374
|
+
|
|
375
|
+
const canUseQuickChatEmptyDynamicContexts = (
|
|
376
|
+
quickChatPerfStartedAt: number | undefined,
|
|
377
|
+
userInput: string | any[],
|
|
378
|
+
runtimeOptions: AgentRuntimeOptions | undefined,
|
|
379
|
+
dialogConfig: DialogConfig | null,
|
|
380
|
+
) => {
|
|
381
|
+
if (!quickChatPerfStartedAt) return false;
|
|
382
|
+
if (runtimeOptions) return false;
|
|
383
|
+
if (!isSimpleTextInput(userInput)) return false;
|
|
384
|
+
if (dialogConfig?.referenceKeys?.length) return false;
|
|
385
|
+
return true;
|
|
386
|
+
};
|
|
387
|
+
|
|
388
|
+
const QUICK_CHAT_DIRECT_ANSWER_PATTERN =
|
|
389
|
+
/(只回复|只输出|只回答|直接回复|直接回答|不要解释|无需解释|不用解释|简短回答|一句话)/i;
|
|
390
|
+
const QUICK_CHAT_TOOL_INTENT_PATTERN =
|
|
391
|
+
/(调用|转交|agent|助手|应用|网页|页面|图表|图片|生成图|画图|删除|清理|空间|商品|链接|https?:\/\/|www\.|@)/i;
|
|
392
|
+
|
|
393
|
+
const shouldDisableQuickChatToolsForDirectAnswer = (
|
|
394
|
+
quickChatPerfStartedAt: number | undefined,
|
|
395
|
+
userInput: string | any[],
|
|
396
|
+
runtimeOptions: AgentRuntimeOptions | undefined,
|
|
397
|
+
dialogConfig: DialogConfig | null,
|
|
398
|
+
) => {
|
|
399
|
+
if (!canUseQuickChatEmptyDynamicContexts(
|
|
400
|
+
quickChatPerfStartedAt,
|
|
401
|
+
userInput,
|
|
402
|
+
runtimeOptions,
|
|
403
|
+
dialogConfig,
|
|
404
|
+
)) {
|
|
405
|
+
return false;
|
|
406
|
+
}
|
|
407
|
+
const text = extractAgentRunUserText(userInput);
|
|
408
|
+
if (!text || text.length > 500) return false;
|
|
409
|
+
if (!QUICK_CHAT_DIRECT_ANSWER_PATTERN.test(text)) return false;
|
|
410
|
+
return !QUICK_CHAT_TOOL_INTENT_PATTERN.test(text);
|
|
411
|
+
};
|
|
412
|
+
|
|
413
|
+
const classifyQuickChatAccessError = (accessError: string) => {
|
|
414
|
+
if (accessError.includes("获取用户余额")) return "balance-loading";
|
|
415
|
+
if (accessError.includes("余额")) return "balance";
|
|
416
|
+
if (accessError.includes("白名单")) return "whitelist";
|
|
417
|
+
if (accessError.includes("定价")) return "pricing";
|
|
418
|
+
return "unknown";
|
|
419
|
+
};
|
|
420
|
+
|
|
421
|
+
const isUsableAgentConfig = (value: unknown): value is Agent =>
|
|
422
|
+
!!value &&
|
|
423
|
+
typeof value === "object" &&
|
|
424
|
+
(value as any).__ssrPreviewOnly !== true &&
|
|
425
|
+
typeof (value as Agent).dbKey === "string" &&
|
|
426
|
+
!!(value as Agent).dbKey &&
|
|
427
|
+
typeof (value as Agent).model === "string" &&
|
|
428
|
+
!!(value as Agent).model &&
|
|
429
|
+
typeof (value as Agent).provider === "string" &&
|
|
430
|
+
!!(value as Agent).provider;
|
|
431
|
+
|
|
169
432
|
const extractAgentRunUserText = (userInput: string | any[]) => {
|
|
170
433
|
if (typeof userInput === "string") {
|
|
171
434
|
return userInput;
|
|
@@ -180,17 +443,6 @@ const extractAgentRunUserText = (userInput: string | any[]) => {
|
|
|
180
443
|
.trim();
|
|
181
444
|
};
|
|
182
445
|
|
|
183
|
-
const requiresServerResolvedProviderConfig = (agentConfig: Agent): boolean => {
|
|
184
|
-
if (agentConfig.useServerProxy !== true) return false;
|
|
185
|
-
|
|
186
|
-
const provider = String(agentConfig.provider ?? "").toLowerCase();
|
|
187
|
-
const apiSource = String((agentConfig as any).apiSource ?? "").toLowerCase();
|
|
188
|
-
const isCustomProvider = provider === "custom" || apiSource === "custom";
|
|
189
|
-
if (!isCustomProvider) return false;
|
|
190
|
-
|
|
191
|
-
return !agentConfig.customProviderUrl?.trim() || !agentConfig.apiKey?.trim();
|
|
192
|
-
};
|
|
193
|
-
|
|
194
446
|
const hasAgentRunUserInputContent = (userInput: string | any[]) => {
|
|
195
447
|
if (typeof userInput === "string") {
|
|
196
448
|
return userInput.trim().length > 0;
|
|
@@ -209,7 +461,14 @@ export const streamAgentChatTurnHandler = async (
|
|
|
209
461
|
args: StreamAgentChatTurnArgs,
|
|
210
462
|
thunkApi: any,
|
|
211
463
|
) => {
|
|
212
|
-
const {
|
|
464
|
+
const {
|
|
465
|
+
agentKey,
|
|
466
|
+
userInput,
|
|
467
|
+
dialogKey: explicitDialogKey,
|
|
468
|
+
parentMessageId,
|
|
469
|
+
runtimeOptions,
|
|
470
|
+
quickChatPerfStartedAt,
|
|
471
|
+
} = args;
|
|
213
472
|
const { getState, dispatch, rejectWithValue } = thunkApi;
|
|
214
473
|
const state = getState() as RootState;
|
|
215
474
|
|
|
@@ -223,15 +482,34 @@ export const streamAgentChatTurnHandler = async (
|
|
|
223
482
|
let runtimeDialogKey: string | null = explicitDialogKey ?? null;
|
|
224
483
|
let remoteTransientMessageId: string | null = null;
|
|
225
484
|
let remoteTransientMessageFinalized = false;
|
|
485
|
+
let modelRequestStarted = false;
|
|
226
486
|
|
|
227
487
|
try {
|
|
228
488
|
let totalTurnUsage: any = null;
|
|
229
489
|
const agentRunUserInput = normalizeAgentRunUserInput(userInput);
|
|
230
|
-
|
|
231
|
-
|
|
490
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-entered", {
|
|
491
|
+
agentKey,
|
|
492
|
+
dialogKey: explicitDialogKey ?? null,
|
|
493
|
+
});
|
|
494
|
+
// 1. 读取 Agent 配置。Quick Chat 会提前预热默认 agent;命中 Redux DB 缓存时避免重复读。
|
|
495
|
+
const cachedAgentConfig = selectById(getState() as RootState, agentKey);
|
|
496
|
+
const agentConfig = isUsableAgentConfig(cachedAgentConfig)
|
|
497
|
+
? cachedAgentConfig
|
|
498
|
+
: await readAgentConfigForTurn(
|
|
499
|
+
dispatch,
|
|
500
|
+
agentKey,
|
|
501
|
+
quickChatPerfStartedAt,
|
|
502
|
+
);
|
|
232
503
|
if (!agentConfig) {
|
|
233
504
|
return rejectWithValue(`Agent config not found for ID: ${agentKey}`);
|
|
234
505
|
}
|
|
506
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-config-read", {
|
|
507
|
+
agentKey,
|
|
508
|
+
model: agentConfig.model,
|
|
509
|
+
provider: agentConfig.provider,
|
|
510
|
+
apiSource: agentConfig.apiSource,
|
|
511
|
+
source: cachedAgentConfig === agentConfig ? "cache" : "read",
|
|
512
|
+
});
|
|
235
513
|
|
|
236
514
|
// ── CLI Agent 专用路由 ────────────────────────────────────────────────
|
|
237
515
|
// CLI 共享 prompt / model 这些入口能力,但不复用本地 tool-call 循环。
|
|
@@ -330,6 +608,8 @@ export const streamAgentChatTurnHandler = async (
|
|
|
330
608
|
userInput: agentRunUserInput,
|
|
331
609
|
messages: cleanedMessages,
|
|
332
610
|
stream: true,
|
|
611
|
+
persistDialog: false,
|
|
612
|
+
clientDialogId: dialogId,
|
|
333
613
|
runtimeContext: {
|
|
334
614
|
surface: "web",
|
|
335
615
|
host: "browser",
|
|
@@ -449,6 +729,14 @@ export const streamAgentChatTurnHandler = async (
|
|
|
449
729
|
cliProvider: agentConfig.cliProvider || "copilot",
|
|
450
730
|
model: agentConfig.model || undefined,
|
|
451
731
|
systemPrompt: agentConfig.prompt || undefined,
|
|
732
|
+
reasoningEffort: agentConfig.reasoning_effort || undefined,
|
|
733
|
+
temperature: agentConfig.temperature,
|
|
734
|
+
topP: agentConfig.top_p,
|
|
735
|
+
frequencyPenalty: agentConfig.frequency_penalty,
|
|
736
|
+
presencePenalty: agentConfig.presence_penalty,
|
|
737
|
+
maxTokens: agentConfig.max_tokens,
|
|
738
|
+
enableThinking: agentConfig.enableThinking,
|
|
739
|
+
thinkingBudget: agentConfig.thinkingBudget,
|
|
452
740
|
},
|
|
453
741
|
);
|
|
454
742
|
|
|
@@ -520,6 +808,7 @@ export const streamAgentChatTurnHandler = async (
|
|
|
520
808
|
}
|
|
521
809
|
|
|
522
810
|
let accumulated = "";
|
|
811
|
+
let cliCapabilityWarnings: string[] = [];
|
|
523
812
|
const decoder = new TextDecoder();
|
|
524
813
|
const buildCliAssistantMessage = () => ({
|
|
525
814
|
id: messageId,
|
|
@@ -579,7 +868,9 @@ export const streamAgentChatTurnHandler = async (
|
|
|
579
868
|
...cliMessageMetadata,
|
|
580
869
|
}));
|
|
581
870
|
}
|
|
582
|
-
|
|
871
|
+
if (payload.done && Array.isArray(payload.warnings)) {
|
|
872
|
+
cliCapabilityWarnings = payload.warnings.filter((warning): warning is string => typeof warning === "string" && warning.trim().length > 0);
|
|
873
|
+
}
|
|
583
874
|
} catch {
|
|
584
875
|
// 忽略解析失败的行
|
|
585
876
|
}
|
|
@@ -591,6 +882,18 @@ export const streamAgentChatTurnHandler = async (
|
|
|
591
882
|
return;
|
|
592
883
|
}
|
|
593
884
|
|
|
885
|
+
if (cliCapabilityWarnings.length > 0) {
|
|
886
|
+
accumulated = appendCliCapabilityWarnings(accumulated, cliCapabilityWarnings);
|
|
887
|
+
dispatch(messageStreaming({
|
|
888
|
+
id: messageId,
|
|
889
|
+
dialogId,
|
|
890
|
+
dbKey: msgKey,
|
|
891
|
+
content: accumulated,
|
|
892
|
+
role: "assistant",
|
|
893
|
+
...cliMessageMetadata,
|
|
894
|
+
}));
|
|
895
|
+
}
|
|
896
|
+
|
|
594
897
|
// 持久化最终消息:用已有 ID,避免 prepareAndPersistMessage 重新生成 ID 导致重复
|
|
595
898
|
await persistMessageWithFixedId(dispatch, buildCliAssistantMessage());
|
|
596
899
|
} finally {
|
|
@@ -616,6 +919,53 @@ export const streamAgentChatTurnHandler = async (
|
|
|
616
919
|
}
|
|
617
920
|
runtimeDialogKey = dialogKey;
|
|
618
921
|
const dialogId = extractCustomId(dialogKey);
|
|
922
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dialog-resolved", {
|
|
923
|
+
dialogKey,
|
|
924
|
+
dialogId,
|
|
925
|
+
});
|
|
926
|
+
|
|
927
|
+
if (isDesktopBuiltinNoloAgent(agentKey, agentConfig)) {
|
|
928
|
+
const { key: msgKey, messageId } = createDialogMessageKeyAndId(dialogId);
|
|
929
|
+
const desktopMessageMetadata = buildParallelMessageMetadata(
|
|
930
|
+
agentConfig,
|
|
931
|
+
runtimeOptions,
|
|
932
|
+
);
|
|
933
|
+
remoteTransientMessageId = messageId;
|
|
934
|
+
loopKey = `loop:${dialogId}`;
|
|
935
|
+
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
936
|
+
dispatch(messageStreaming({
|
|
937
|
+
id: messageId,
|
|
938
|
+
dialogId,
|
|
939
|
+
dbKey: msgKey,
|
|
940
|
+
content: "",
|
|
941
|
+
role: "assistant",
|
|
942
|
+
...desktopMessageMetadata,
|
|
943
|
+
}));
|
|
944
|
+
|
|
945
|
+
const localResult = await runDesktopAgentRuntimeTurn({
|
|
946
|
+
agentRef: agentConfig.dbKey || agentKey,
|
|
947
|
+
input: userInput,
|
|
948
|
+
continueDialogId: dialogId,
|
|
949
|
+
});
|
|
950
|
+
if (!localResult.ok) {
|
|
951
|
+
dispatch(removeTransientMessage(messageId));
|
|
952
|
+
remoteTransientMessageFinalized = true;
|
|
953
|
+
return rejectWithValue(localResult.error);
|
|
954
|
+
}
|
|
955
|
+
|
|
956
|
+
dispatch(messageStreaming({
|
|
957
|
+
id: messageId,
|
|
958
|
+
dialogId,
|
|
959
|
+
dbKey: msgKey,
|
|
960
|
+
content: localResult.result.content || "",
|
|
961
|
+
role: "assistant",
|
|
962
|
+
...desktopMessageMetadata,
|
|
963
|
+
}));
|
|
964
|
+
remoteTransientMessageFinalized = true;
|
|
965
|
+
return {
|
|
966
|
+
usage: localResult.result.usage ?? undefined,
|
|
967
|
+
};
|
|
968
|
+
}
|
|
619
969
|
|
|
620
970
|
const userInputText = extractAgentRunUserText(userInput);
|
|
621
971
|
|
|
@@ -625,12 +975,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
625
975
|
: null;
|
|
626
976
|
const currentServer = selectCurrentServer(state);
|
|
627
977
|
const declaredRuntimeServerBase = extractAgentRuntimeServerBase(agentConfig);
|
|
628
|
-
const needsServerResolvedProviderConfig =
|
|
629
|
-
requiresServerResolvedProviderConfig(agentConfig);
|
|
630
978
|
const requestedServerBase =
|
|
631
979
|
explicitServerBase ??
|
|
632
980
|
declaredRuntimeServerBase ??
|
|
633
|
-
|
|
981
|
+
null;
|
|
634
982
|
const normalizedRequestedServerBase =
|
|
635
983
|
requestedServerBase && normalizeServerOrigin(requestedServerBase);
|
|
636
984
|
const normalizedCurrentServer = normalizeServerOrigin(
|
|
@@ -643,12 +991,13 @@ export const streamAgentChatTurnHandler = async (
|
|
|
643
991
|
!runtimeOptions?.imageConfigOverride;
|
|
644
992
|
if (requestedServerBase && canAutoRouteRemotely) {
|
|
645
993
|
if (
|
|
646
|
-
!needsServerResolvedProviderConfig &&
|
|
647
994
|
normalizedRequestedServerBase &&
|
|
648
995
|
normalizedCurrentServer &&
|
|
649
996
|
normalizedRequestedServerBase === normalizedCurrentServer
|
|
650
997
|
) {
|
|
651
|
-
//
|
|
998
|
+
// Same server as the current workspace; keep the UI-managed
|
|
999
|
+
// chat/tool loop and let /api/chat hydrate redacted provider
|
|
1000
|
+
// credentials server-side when needed.
|
|
652
1001
|
} else {
|
|
653
1002
|
const token = selectCurrentToken(state);
|
|
654
1003
|
const authHeader = token ? `Bearer ${token}` : "";
|
|
@@ -711,6 +1060,8 @@ export const streamAgentChatTurnHandler = async (
|
|
|
711
1060
|
userInput: agentRunUserInput,
|
|
712
1061
|
messages: cleanedMessages,
|
|
713
1062
|
stream: true,
|
|
1063
|
+
persistDialog: false,
|
|
1064
|
+
clientDialogId: dialogId,
|
|
714
1065
|
runtimeContext: {
|
|
715
1066
|
surface: "web",
|
|
716
1067
|
host: "browser",
|
|
@@ -811,6 +1162,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
811
1162
|
recommendedSkillHints: referenceRecommendedSkillHints,
|
|
812
1163
|
skillPromptPatches: referenceSkillPromptPatches,
|
|
813
1164
|
} = await resolveReferenceAssets(agentConfig.references, dispatch);
|
|
1165
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-references-resolved", {
|
|
1166
|
+
referenceCount: normalizedReferences?.length ?? 0,
|
|
1167
|
+
referencedToolCount: referenceTools?.length ?? 0,
|
|
1168
|
+
});
|
|
814
1169
|
|
|
815
1170
|
const agentConfigWithReferences = {
|
|
816
1171
|
...agentConfig,
|
|
@@ -906,6 +1261,13 @@ export const streamAgentChatTurnHandler = async (
|
|
|
906
1261
|
currentDialog ?? undefined,
|
|
907
1262
|
mergedContentCache,
|
|
908
1263
|
);
|
|
1264
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-static-context-ready", {
|
|
1265
|
+
model: agentConfigForCall.model,
|
|
1266
|
+
responseApi: true,
|
|
1267
|
+
});
|
|
1268
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-post-static-entered", {
|
|
1269
|
+
responseApi: true,
|
|
1270
|
+
});
|
|
909
1271
|
|
|
910
1272
|
let appendTempUserInput = true;
|
|
911
1273
|
let currentParentMessageId = parentMessageId ?? undefined;
|
|
@@ -914,11 +1276,25 @@ export const streamAgentChatTurnHandler = async (
|
|
|
914
1276
|
if (w) w.__LOOP_STOP_REASON__ = null;
|
|
915
1277
|
|
|
916
1278
|
loopKey = `loop:${dialogId}`;
|
|
1279
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-adding", {
|
|
1280
|
+
responseApi: true,
|
|
1281
|
+
});
|
|
917
1282
|
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
1283
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-added", {
|
|
1284
|
+
responseApi: true,
|
|
1285
|
+
});
|
|
918
1286
|
|
|
919
1287
|
for (;;) {
|
|
1288
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-entered", {
|
|
1289
|
+
responseApi: true,
|
|
1290
|
+
});
|
|
920
1291
|
const requestParentMessageId = currentParentMessageId;
|
|
921
1292
|
if (loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
1293
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-aborted-before-context", {
|
|
1294
|
+
loopControllerAborted: loopController.signal.aborted,
|
|
1295
|
+
thunkSignalAborted: thunkApi.signal.aborted,
|
|
1296
|
+
responseApi: true,
|
|
1297
|
+
});
|
|
922
1298
|
if (w) w.__LOOP_STOP_REASON__ = "aborted";
|
|
923
1299
|
break;
|
|
924
1300
|
}
|
|
@@ -926,6 +1302,11 @@ export const streamAgentChatTurnHandler = async (
|
|
|
926
1302
|
const loopState = getState() as RootState;
|
|
927
1303
|
const now = Date.now();
|
|
928
1304
|
if (now - startTime > MAX_TIME_MS) {
|
|
1305
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-timeout-before-context", {
|
|
1306
|
+
maxTimeMs: MAX_TIME_MS,
|
|
1307
|
+
elapsedMs: now - startTime,
|
|
1308
|
+
responseApi: true,
|
|
1309
|
+
});
|
|
929
1310
|
if (w) w.__LOOP_STOP_REASON__ = "timeout";
|
|
930
1311
|
break;
|
|
931
1312
|
}
|
|
@@ -935,18 +1316,63 @@ export const streamAgentChatTurnHandler = async (
|
|
|
935
1316
|
loopState,
|
|
936
1317
|
);
|
|
937
1318
|
if (accessError) {
|
|
1319
|
+
const accessErrorReason = classifyQuickChatAccessError(accessError);
|
|
1320
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-before-context", {
|
|
1321
|
+
hasAccessError: true,
|
|
1322
|
+
reason: accessErrorReason,
|
|
1323
|
+
responseApi: true,
|
|
1324
|
+
});
|
|
1325
|
+
if (quickChatPerfStartedAt && runtimeDialogKey && !modelRequestStarted) {
|
|
1326
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-finalized", {
|
|
1327
|
+
reason: accessErrorReason,
|
|
1328
|
+
responseApi: true,
|
|
1329
|
+
});
|
|
1330
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
1331
|
+
dispatch,
|
|
1332
|
+
runtimeDialogKey,
|
|
1333
|
+
agentKey,
|
|
1334
|
+
new Error(accessError),
|
|
1335
|
+
);
|
|
1336
|
+
}
|
|
938
1337
|
return rejectWithValue(accessError);
|
|
939
1338
|
}
|
|
940
1339
|
|
|
941
|
-
const
|
|
942
|
-
|
|
943
|
-
dispatch,
|
|
944
|
-
agentConfigForCall,
|
|
1340
|
+
const willSkipDynamicContext = canUseQuickChatEmptyDynamicContexts(
|
|
1341
|
+
quickChatPerfStartedAt,
|
|
945
1342
|
userInput,
|
|
946
1343
|
runtimeOptions,
|
|
947
|
-
|
|
948
|
-
dialogKey,
|
|
1344
|
+
currentDialog,
|
|
949
1345
|
);
|
|
1346
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
1347
|
+
hasRuntimeOptions: !!runtimeOptions,
|
|
1348
|
+
isSimpleTextInput: isSimpleTextInput(userInput),
|
|
1349
|
+
referenceKeyCount: currentDialog?.referenceKeys?.length ?? 0,
|
|
1350
|
+
willSkipDynamicContext,
|
|
1351
|
+
responseApi: true,
|
|
1352
|
+
});
|
|
1353
|
+
|
|
1354
|
+
const dynamicContexts = willSkipDynamicContext
|
|
1355
|
+
? (logQuickChatPerfStage(
|
|
1356
|
+
quickChatPerfStartedAt,
|
|
1357
|
+
"stream-agent-dynamic-context-skipped",
|
|
1358
|
+
{
|
|
1359
|
+
reason: "simple-quick-chat-first-turn",
|
|
1360
|
+
responseApi: true,
|
|
1361
|
+
},
|
|
1362
|
+
), EMPTY_DYNAMIC_CONTEXTS)
|
|
1363
|
+
: await buildDynamicContextsForTurn(
|
|
1364
|
+
loopState,
|
|
1365
|
+
dispatch,
|
|
1366
|
+
agentConfigForCall,
|
|
1367
|
+
userInput,
|
|
1368
|
+
runtimeOptions,
|
|
1369
|
+
mergedContentCache,
|
|
1370
|
+
dialogKey,
|
|
1371
|
+
quickChatPerfStartedAt,
|
|
1372
|
+
);
|
|
1373
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
1374
|
+
responseApi: true,
|
|
1375
|
+
});
|
|
950
1376
|
const contexts = mergeContexts(staticContexts, dynamicContexts);
|
|
951
1377
|
|
|
952
1378
|
const rawMessages = filterMessagesForParallelBranch(
|
|
@@ -1014,6 +1440,12 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1014
1440
|
userInput: userInputText,
|
|
1015
1441
|
contexts,
|
|
1016
1442
|
});
|
|
1443
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-starting", {
|
|
1444
|
+
responseApi: true,
|
|
1445
|
+
dynamicMessageCount: dynamicMessages.length,
|
|
1446
|
+
stableMessageCount: stableMessages.length,
|
|
1447
|
+
});
|
|
1448
|
+
modelRequestStarted = true;
|
|
1017
1449
|
|
|
1018
1450
|
const meta: CompletionMeta = await sendOpenAIResponseRequest({
|
|
1019
1451
|
bodyData,
|
|
@@ -1025,6 +1457,13 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1025
1457
|
agentConfigForCall,
|
|
1026
1458
|
runtimeOptions,
|
|
1027
1459
|
),
|
|
1460
|
+
quickChatPerfStartedAt,
|
|
1461
|
+
});
|
|
1462
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-finished", {
|
|
1463
|
+
responseApi: true,
|
|
1464
|
+
hasToolCalls: meta.hasToolCalls,
|
|
1465
|
+
hasHandedOff: meta.hasHandedOff,
|
|
1466
|
+
hasPendingInteraction: meta.hasPendingInteraction,
|
|
1028
1467
|
});
|
|
1029
1468
|
|
|
1030
1469
|
appendTempUserInput = false;
|
|
@@ -1093,6 +1532,13 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1093
1532
|
currentDialog ?? undefined,
|
|
1094
1533
|
mergedContentCache,
|
|
1095
1534
|
);
|
|
1535
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-static-context-ready", {
|
|
1536
|
+
model: agentConfigForCall.model,
|
|
1537
|
+
responseApi: false,
|
|
1538
|
+
});
|
|
1539
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-post-static-entered", {
|
|
1540
|
+
responseApi: false,
|
|
1541
|
+
});
|
|
1096
1542
|
|
|
1097
1543
|
let appendTempUserInput = true;
|
|
1098
1544
|
let currentParentMessageId = parentMessageId ?? undefined;
|
|
@@ -1102,13 +1548,27 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1102
1548
|
|
|
1103
1549
|
if (!isRespModel) {
|
|
1104
1550
|
loopKey = `loop:${dialogId}`;
|
|
1551
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-adding", {
|
|
1552
|
+
responseApi: false,
|
|
1553
|
+
});
|
|
1105
1554
|
dispatch(addActiveController({ messageId: loopKey, controller: loopController, dialogKey }));
|
|
1555
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-active-controller-added", {
|
|
1556
|
+
responseApi: false,
|
|
1557
|
+
});
|
|
1106
1558
|
}
|
|
1107
1559
|
|
|
1108
1560
|
for (;;) {
|
|
1561
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-entered", {
|
|
1562
|
+
responseApi: false,
|
|
1563
|
+
});
|
|
1109
1564
|
const requestParentMessageId = currentParentMessageId;
|
|
1110
1565
|
// 每轮开始前检查是否已中止
|
|
1111
1566
|
if (loopController.signal.aborted || thunkApi.signal.aborted) {
|
|
1567
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-aborted-before-context", {
|
|
1568
|
+
loopControllerAborted: loopController.signal.aborted,
|
|
1569
|
+
thunkSignalAborted: thunkApi.signal.aborted,
|
|
1570
|
+
responseApi: false,
|
|
1571
|
+
});
|
|
1112
1572
|
if (w) w.__LOOP_STOP_REASON__ = "aborted";
|
|
1113
1573
|
break;
|
|
1114
1574
|
}
|
|
@@ -1116,6 +1576,11 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1116
1576
|
const loopState = getState() as RootState;
|
|
1117
1577
|
const now = Date.now();
|
|
1118
1578
|
if (now - startTime > MAX_TIME_MS) {
|
|
1579
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-loop-timeout-before-context", {
|
|
1580
|
+
maxTimeMs: MAX_TIME_MS,
|
|
1581
|
+
elapsedMs: now - startTime,
|
|
1582
|
+
responseApi: false,
|
|
1583
|
+
});
|
|
1119
1584
|
if (w) w.__LOOP_STOP_REASON__ = "timeout";
|
|
1120
1585
|
break;
|
|
1121
1586
|
}
|
|
@@ -1126,19 +1591,64 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1126
1591
|
loopState,
|
|
1127
1592
|
);
|
|
1128
1593
|
if (accessError) {
|
|
1594
|
+
const accessErrorReason = classifyQuickChatAccessError(accessError);
|
|
1595
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-before-context", {
|
|
1596
|
+
hasAccessError: true,
|
|
1597
|
+
reason: accessErrorReason,
|
|
1598
|
+
responseApi: false,
|
|
1599
|
+
});
|
|
1600
|
+
if (quickChatPerfStartedAt && runtimeDialogKey && !modelRequestStarted) {
|
|
1601
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-access-error-finalized", {
|
|
1602
|
+
reason: accessErrorReason,
|
|
1603
|
+
responseApi: false,
|
|
1604
|
+
});
|
|
1605
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
1606
|
+
dispatch,
|
|
1607
|
+
runtimeDialogKey,
|
|
1608
|
+
agentKey,
|
|
1609
|
+
new Error(accessError),
|
|
1610
|
+
);
|
|
1611
|
+
}
|
|
1129
1612
|
return rejectWithValue(accessError);
|
|
1130
1613
|
}
|
|
1131
1614
|
|
|
1132
|
-
|
|
1133
|
-
|
|
1134
|
-
loopState,
|
|
1135
|
-
dispatch,
|
|
1136
|
-
agentConfigForCall,
|
|
1615
|
+
const willSkipDynamicContext = canUseQuickChatEmptyDynamicContexts(
|
|
1616
|
+
quickChatPerfStartedAt,
|
|
1137
1617
|
userInput,
|
|
1138
1618
|
runtimeOptions,
|
|
1139
|
-
|
|
1140
|
-
dialogKey,
|
|
1619
|
+
currentDialog,
|
|
1141
1620
|
);
|
|
1621
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-decision", {
|
|
1622
|
+
hasRuntimeOptions: !!runtimeOptions,
|
|
1623
|
+
isSimpleTextInput: isSimpleTextInput(userInput),
|
|
1624
|
+
referenceKeyCount: currentDialog?.referenceKeys?.length ?? 0,
|
|
1625
|
+
willSkipDynamicContext,
|
|
1626
|
+
responseApi: false,
|
|
1627
|
+
});
|
|
1628
|
+
|
|
1629
|
+
// 🚀 优化:每轮只构建动态上下文(currentInput、history、editingContext、dialogSummary)
|
|
1630
|
+
const dynamicContexts = willSkipDynamicContext
|
|
1631
|
+
? (logQuickChatPerfStage(
|
|
1632
|
+
quickChatPerfStartedAt,
|
|
1633
|
+
"stream-agent-dynamic-context-skipped",
|
|
1634
|
+
{
|
|
1635
|
+
reason: "simple-quick-chat-first-turn",
|
|
1636
|
+
responseApi: false,
|
|
1637
|
+
},
|
|
1638
|
+
), EMPTY_DYNAMIC_CONTEXTS)
|
|
1639
|
+
: await buildDynamicContextsForTurn(
|
|
1640
|
+
loopState,
|
|
1641
|
+
dispatch,
|
|
1642
|
+
agentConfigForCall,
|
|
1643
|
+
userInput,
|
|
1644
|
+
runtimeOptions,
|
|
1645
|
+
mergedContentCache,
|
|
1646
|
+
dialogKey,
|
|
1647
|
+
quickChatPerfStartedAt,
|
|
1648
|
+
);
|
|
1649
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
1650
|
+
responseApi: false,
|
|
1651
|
+
});
|
|
1142
1652
|
|
|
1143
1653
|
// 合并静态和动态上下文
|
|
1144
1654
|
const contexts = mergeContexts(staticContexts, dynamicContexts);
|
|
@@ -1209,6 +1719,32 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1209
1719
|
userInput: userInputText,
|
|
1210
1720
|
contexts,
|
|
1211
1721
|
});
|
|
1722
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-starting", {
|
|
1723
|
+
responseApi: false,
|
|
1724
|
+
dynamicMessageCount: dynamicMessages.length,
|
|
1725
|
+
stableMessageCount: stableMessages.length,
|
|
1726
|
+
});
|
|
1727
|
+
modelRequestStarted = true;
|
|
1728
|
+
|
|
1729
|
+
const disableToolsForThisRequest =
|
|
1730
|
+
shouldDisableQuickChatToolsForDirectAnswer(
|
|
1731
|
+
quickChatPerfStartedAt,
|
|
1732
|
+
userInput,
|
|
1733
|
+
runtimeOptions,
|
|
1734
|
+
currentDialog,
|
|
1735
|
+
);
|
|
1736
|
+
if (disableToolsForThisRequest) {
|
|
1737
|
+
logQuickChatPerfStage(
|
|
1738
|
+
quickChatPerfStartedAt,
|
|
1739
|
+
"stream-agent-tools-disabled-for-direct-answer",
|
|
1740
|
+
{
|
|
1741
|
+
responseApi: false,
|
|
1742
|
+
toolCount: Array.isArray(agentConfigForCall.tools)
|
|
1743
|
+
? agentConfigForCall.tools.length
|
|
1744
|
+
: 0,
|
|
1745
|
+
},
|
|
1746
|
+
);
|
|
1747
|
+
}
|
|
1212
1748
|
|
|
1213
1749
|
const meta: CompletionMeta = await sendOpenAICompletionsRequest({
|
|
1214
1750
|
bodyData,
|
|
@@ -1220,6 +1756,14 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1220
1756
|
agentConfigForCall,
|
|
1221
1757
|
runtimeOptions,
|
|
1222
1758
|
),
|
|
1759
|
+
disableToolsForThisRequest,
|
|
1760
|
+
quickChatPerfStartedAt,
|
|
1761
|
+
});
|
|
1762
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-finished", {
|
|
1763
|
+
responseApi: false,
|
|
1764
|
+
hasToolCalls: meta.hasToolCalls,
|
|
1765
|
+
hasHandedOff: meta.hasHandedOff,
|
|
1766
|
+
hasPendingInteraction: meta.hasPendingInteraction,
|
|
1223
1767
|
});
|
|
1224
1768
|
|
|
1225
1769
|
appendTempUserInput = false;
|
|
@@ -1299,6 +1843,19 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1299
1843
|
dispatch(removeTransientMessage(remoteTransientMessageId));
|
|
1300
1844
|
remoteTransientMessageFinalized = true;
|
|
1301
1845
|
}
|
|
1846
|
+
if (
|
|
1847
|
+
quickChatPerfStartedAt &&
|
|
1848
|
+
runtimeDialogKey &&
|
|
1849
|
+
!modelRequestStarted &&
|
|
1850
|
+
!remoteTransientMessageId
|
|
1851
|
+
) {
|
|
1852
|
+
await finalizeQuickChatAgentTurnFailure(
|
|
1853
|
+
dispatch,
|
|
1854
|
+
runtimeDialogKey,
|
|
1855
|
+
agentKey,
|
|
1856
|
+
error,
|
|
1857
|
+
);
|
|
1858
|
+
}
|
|
1302
1859
|
|
|
1303
1860
|
return rejectWithValue(
|
|
1304
1861
|
error?.message ||
|