nolo-cli 0.1.21 → 0.1.23
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/agent-runtime/agentRecordConfig.ts +4 -0
- package/agent-runtime/hostAdapter.ts +2 -0
- package/agent-runtime/index.ts +7 -0
- package/agent-runtime/localLoop.ts +2 -0
- package/agent-runtime/platformChatProvider.ts +3 -0
- package/agent-runtime/runtimeToolPolicy.ts +92 -0
- package/agent-runtime/types.ts +42 -0
- package/agentRunCommand.ts +74 -1
- package/agentRuntimeCommands.ts +17 -89
- package/ai/agent/streamAgentChatTurn.ts +104 -20
- package/ai/chat/fetchUtils.native.ts +2 -0
- package/ai/chat/fetchUtils.ts +2 -0
- package/ai/chat/sendOpenAICompletionsRequest.ts +56 -0
- package/ai/chat/sendOpenAIResponseRequest.ts +64 -0
- package/ai/llm/kimi.ts +1 -1
- package/ai/llm/providers.ts +3 -0
- package/ai/llm/reasoningModels.ts +1 -0
- package/ai/skills/skillDocProtocol.ts +95 -3
- package/ai/taskRun/taskRunProtocol.ts +1 -0
- package/ai/tools/agent/agentTools.ts +17 -0
- package/ai/tools/agent/startAgentDialogTool.ts +53 -0
- package/ai/tools/modelUsageTools.ts +5 -0
- package/client/agentRun.test.ts +257 -7
- package/client/agentRun.ts +133 -34
- package/client/localRuntimeAdapter.test.ts +2 -0
- package/client/localRuntimeAdapter.ts +15 -2
- package/database/actions/common.ts +4 -3
- package/database/config.ts +19 -0
- package/machineCommands.ts +400 -45
- package/package.json +4 -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/tableCommands.ts +42 -5
|
@@ -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,
|
|
@@ -146,8 +146,23 @@ export interface StreamAgentChatTurnArgs {
|
|
|
146
146
|
isStreaming?: boolean;
|
|
147
147
|
parentMessageId?: string;
|
|
148
148
|
runtimeOptions?: AgentRuntimeOptions;
|
|
149
|
+
quickChatPerfStartedAt?: number;
|
|
149
150
|
}
|
|
150
151
|
|
|
152
|
+
const logQuickChatPerfStage = (
|
|
153
|
+
startedAt: number | undefined,
|
|
154
|
+
stage: string,
|
|
155
|
+
details: Record<string, unknown> = {}
|
|
156
|
+
) => {
|
|
157
|
+
if (!startedAt) return;
|
|
158
|
+
console.info("[QuickChatPerf]", {
|
|
159
|
+
stage,
|
|
160
|
+
elapsedMs: Date.now() - startedAt,
|
|
161
|
+
...(typeof performance !== "undefined" ? { atMs: performance.now() } : {}),
|
|
162
|
+
...details,
|
|
163
|
+
});
|
|
164
|
+
};
|
|
165
|
+
|
|
151
166
|
const normalizeAgentRunUserInput = (userInput: string | any[]) => {
|
|
152
167
|
if (typeof userInput === "string") {
|
|
153
168
|
return userInput;
|
|
@@ -166,6 +181,16 @@ const normalizeAgentRunUserInput = (userInput: string | any[]) => {
|
|
|
166
181
|
});
|
|
167
182
|
};
|
|
168
183
|
|
|
184
|
+
const isUsableAgentConfig = (value: unknown): value is Agent =>
|
|
185
|
+
!!value &&
|
|
186
|
+
typeof value === "object" &&
|
|
187
|
+
typeof (value as Agent).dbKey === "string" &&
|
|
188
|
+
!!(value as Agent).dbKey &&
|
|
189
|
+
typeof (value as Agent).model === "string" &&
|
|
190
|
+
!!(value as Agent).model &&
|
|
191
|
+
typeof (value as Agent).provider === "string" &&
|
|
192
|
+
!!(value as Agent).provider;
|
|
193
|
+
|
|
169
194
|
const extractAgentRunUserText = (userInput: string | any[]) => {
|
|
170
195
|
if (typeof userInput === "string") {
|
|
171
196
|
return userInput;
|
|
@@ -180,17 +205,6 @@ const extractAgentRunUserText = (userInput: string | any[]) => {
|
|
|
180
205
|
.trim();
|
|
181
206
|
};
|
|
182
207
|
|
|
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
208
|
const hasAgentRunUserInputContent = (userInput: string | any[]) => {
|
|
195
209
|
if (typeof userInput === "string") {
|
|
196
210
|
return userInput.trim().length > 0;
|
|
@@ -209,7 +223,14 @@ export const streamAgentChatTurnHandler = async (
|
|
|
209
223
|
args: StreamAgentChatTurnArgs,
|
|
210
224
|
thunkApi: any,
|
|
211
225
|
) => {
|
|
212
|
-
const {
|
|
226
|
+
const {
|
|
227
|
+
agentKey,
|
|
228
|
+
userInput,
|
|
229
|
+
dialogKey: explicitDialogKey,
|
|
230
|
+
parentMessageId,
|
|
231
|
+
runtimeOptions,
|
|
232
|
+
quickChatPerfStartedAt,
|
|
233
|
+
} = args;
|
|
213
234
|
const { getState, dispatch, rejectWithValue } = thunkApi;
|
|
214
235
|
const state = getState() as RootState;
|
|
215
236
|
|
|
@@ -227,11 +248,25 @@ export const streamAgentChatTurnHandler = async (
|
|
|
227
248
|
try {
|
|
228
249
|
let totalTurnUsage: any = null;
|
|
229
250
|
const agentRunUserInput = normalizeAgentRunUserInput(userInput);
|
|
230
|
-
|
|
231
|
-
|
|
251
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-entered", {
|
|
252
|
+
agentKey,
|
|
253
|
+
dialogKey: explicitDialogKey ?? null,
|
|
254
|
+
});
|
|
255
|
+
// 1. 读取 Agent 配置。Quick Chat 会提前预热默认 agent;命中 Redux DB 缓存时避免重复读。
|
|
256
|
+
const cachedAgentConfig = selectById(getState() as RootState, agentKey);
|
|
257
|
+
const agentConfig = isUsableAgentConfig(cachedAgentConfig)
|
|
258
|
+
? cachedAgentConfig
|
|
259
|
+
: ((await dispatch(read({ dbKey: agentKey })).unwrap()) as Agent);
|
|
232
260
|
if (!agentConfig) {
|
|
233
261
|
return rejectWithValue(`Agent config not found for ID: ${agentKey}`);
|
|
234
262
|
}
|
|
263
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-config-read", {
|
|
264
|
+
agentKey,
|
|
265
|
+
model: agentConfig.model,
|
|
266
|
+
provider: agentConfig.provider,
|
|
267
|
+
apiSource: agentConfig.apiSource,
|
|
268
|
+
source: cachedAgentConfig === agentConfig ? "cache" : "read",
|
|
269
|
+
});
|
|
235
270
|
|
|
236
271
|
// ── CLI Agent 专用路由 ────────────────────────────────────────────────
|
|
237
272
|
// CLI 共享 prompt / model 这些入口能力,但不复用本地 tool-call 循环。
|
|
@@ -330,6 +365,8 @@ export const streamAgentChatTurnHandler = async (
|
|
|
330
365
|
userInput: agentRunUserInput,
|
|
331
366
|
messages: cleanedMessages,
|
|
332
367
|
stream: true,
|
|
368
|
+
persistDialog: false,
|
|
369
|
+
clientDialogId: dialogId,
|
|
333
370
|
runtimeContext: {
|
|
334
371
|
surface: "web",
|
|
335
372
|
host: "browser",
|
|
@@ -616,6 +653,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
616
653
|
}
|
|
617
654
|
runtimeDialogKey = dialogKey;
|
|
618
655
|
const dialogId = extractCustomId(dialogKey);
|
|
656
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dialog-resolved", {
|
|
657
|
+
dialogKey,
|
|
658
|
+
dialogId,
|
|
659
|
+
});
|
|
619
660
|
|
|
620
661
|
const userInputText = extractAgentRunUserText(userInput);
|
|
621
662
|
|
|
@@ -625,12 +666,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
625
666
|
: null;
|
|
626
667
|
const currentServer = selectCurrentServer(state);
|
|
627
668
|
const declaredRuntimeServerBase = extractAgentRuntimeServerBase(agentConfig);
|
|
628
|
-
const needsServerResolvedProviderConfig =
|
|
629
|
-
requiresServerResolvedProviderConfig(agentConfig);
|
|
630
669
|
const requestedServerBase =
|
|
631
670
|
explicitServerBase ??
|
|
632
671
|
declaredRuntimeServerBase ??
|
|
633
|
-
|
|
672
|
+
null;
|
|
634
673
|
const normalizedRequestedServerBase =
|
|
635
674
|
requestedServerBase && normalizeServerOrigin(requestedServerBase);
|
|
636
675
|
const normalizedCurrentServer = normalizeServerOrigin(
|
|
@@ -643,12 +682,13 @@ export const streamAgentChatTurnHandler = async (
|
|
|
643
682
|
!runtimeOptions?.imageConfigOverride;
|
|
644
683
|
if (requestedServerBase && canAutoRouteRemotely) {
|
|
645
684
|
if (
|
|
646
|
-
!needsServerResolvedProviderConfig &&
|
|
647
685
|
normalizedRequestedServerBase &&
|
|
648
686
|
normalizedCurrentServer &&
|
|
649
687
|
normalizedRequestedServerBase === normalizedCurrentServer
|
|
650
688
|
) {
|
|
651
|
-
//
|
|
689
|
+
// Same server as the current workspace; keep the UI-managed
|
|
690
|
+
// chat/tool loop and let /api/chat hydrate redacted provider
|
|
691
|
+
// credentials server-side when needed.
|
|
652
692
|
} else {
|
|
653
693
|
const token = selectCurrentToken(state);
|
|
654
694
|
const authHeader = token ? `Bearer ${token}` : "";
|
|
@@ -711,6 +751,8 @@ export const streamAgentChatTurnHandler = async (
|
|
|
711
751
|
userInput: agentRunUserInput,
|
|
712
752
|
messages: cleanedMessages,
|
|
713
753
|
stream: true,
|
|
754
|
+
persistDialog: false,
|
|
755
|
+
clientDialogId: dialogId,
|
|
714
756
|
runtimeContext: {
|
|
715
757
|
surface: "web",
|
|
716
758
|
host: "browser",
|
|
@@ -811,6 +853,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
811
853
|
recommendedSkillHints: referenceRecommendedSkillHints,
|
|
812
854
|
skillPromptPatches: referenceSkillPromptPatches,
|
|
813
855
|
} = await resolveReferenceAssets(agentConfig.references, dispatch);
|
|
856
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-references-resolved", {
|
|
857
|
+
referenceCount: normalizedReferences?.length ?? 0,
|
|
858
|
+
referencedToolCount: referenceTools?.length ?? 0,
|
|
859
|
+
});
|
|
814
860
|
|
|
815
861
|
const agentConfigWithReferences = {
|
|
816
862
|
...agentConfig,
|
|
@@ -906,6 +952,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
906
952
|
currentDialog ?? undefined,
|
|
907
953
|
mergedContentCache,
|
|
908
954
|
);
|
|
955
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-static-context-ready", {
|
|
956
|
+
model: agentConfigForCall.model,
|
|
957
|
+
responseApi: true,
|
|
958
|
+
});
|
|
909
959
|
|
|
910
960
|
let appendTempUserInput = true;
|
|
911
961
|
let currentParentMessageId = parentMessageId ?? undefined;
|
|
@@ -947,6 +997,9 @@ export const streamAgentChatTurnHandler = async (
|
|
|
947
997
|
mergedContentCache,
|
|
948
998
|
dialogKey,
|
|
949
999
|
);
|
|
1000
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
1001
|
+
responseApi: true,
|
|
1002
|
+
});
|
|
950
1003
|
const contexts = mergeContexts(staticContexts, dynamicContexts);
|
|
951
1004
|
|
|
952
1005
|
const rawMessages = filterMessagesForParallelBranch(
|
|
@@ -1014,6 +1067,11 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1014
1067
|
userInput: userInputText,
|
|
1015
1068
|
contexts,
|
|
1016
1069
|
});
|
|
1070
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-starting", {
|
|
1071
|
+
responseApi: true,
|
|
1072
|
+
dynamicMessageCount: dynamicMessages.length,
|
|
1073
|
+
stableMessageCount: stableMessages.length,
|
|
1074
|
+
});
|
|
1017
1075
|
|
|
1018
1076
|
const meta: CompletionMeta = await sendOpenAIResponseRequest({
|
|
1019
1077
|
bodyData,
|
|
@@ -1025,6 +1083,13 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1025
1083
|
agentConfigForCall,
|
|
1026
1084
|
runtimeOptions,
|
|
1027
1085
|
),
|
|
1086
|
+
quickChatPerfStartedAt,
|
|
1087
|
+
});
|
|
1088
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-finished", {
|
|
1089
|
+
responseApi: true,
|
|
1090
|
+
hasToolCalls: meta.hasToolCalls,
|
|
1091
|
+
hasHandedOff: meta.hasHandedOff,
|
|
1092
|
+
hasPendingInteraction: meta.hasPendingInteraction,
|
|
1028
1093
|
});
|
|
1029
1094
|
|
|
1030
1095
|
appendTempUserInput = false;
|
|
@@ -1093,6 +1158,10 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1093
1158
|
currentDialog ?? undefined,
|
|
1094
1159
|
mergedContentCache,
|
|
1095
1160
|
);
|
|
1161
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-static-context-ready", {
|
|
1162
|
+
model: agentConfigForCall.model,
|
|
1163
|
+
responseApi: false,
|
|
1164
|
+
});
|
|
1096
1165
|
|
|
1097
1166
|
let appendTempUserInput = true;
|
|
1098
1167
|
let currentParentMessageId = parentMessageId ?? undefined;
|
|
@@ -1139,6 +1208,9 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1139
1208
|
mergedContentCache,
|
|
1140
1209
|
dialogKey,
|
|
1141
1210
|
);
|
|
1211
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-dynamic-context-ready", {
|
|
1212
|
+
responseApi: false,
|
|
1213
|
+
});
|
|
1142
1214
|
|
|
1143
1215
|
// 合并静态和动态上下文
|
|
1144
1216
|
const contexts = mergeContexts(staticContexts, dynamicContexts);
|
|
@@ -1209,6 +1281,11 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1209
1281
|
userInput: userInputText,
|
|
1210
1282
|
contexts,
|
|
1211
1283
|
});
|
|
1284
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-starting", {
|
|
1285
|
+
responseApi: false,
|
|
1286
|
+
dynamicMessageCount: dynamicMessages.length,
|
|
1287
|
+
stableMessageCount: stableMessages.length,
|
|
1288
|
+
});
|
|
1212
1289
|
|
|
1213
1290
|
const meta: CompletionMeta = await sendOpenAICompletionsRequest({
|
|
1214
1291
|
bodyData,
|
|
@@ -1220,6 +1297,13 @@ export const streamAgentChatTurnHandler = async (
|
|
|
1220
1297
|
agentConfigForCall,
|
|
1221
1298
|
runtimeOptions,
|
|
1222
1299
|
),
|
|
1300
|
+
quickChatPerfStartedAt,
|
|
1301
|
+
});
|
|
1302
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "stream-agent-model-request-finished", {
|
|
1303
|
+
responseApi: false,
|
|
1304
|
+
hasToolCalls: meta.hasToolCalls,
|
|
1305
|
+
hasHandedOff: meta.hasHandedOff,
|
|
1306
|
+
hasPendingInteraction: meta.hasPendingInteraction,
|
|
1223
1307
|
});
|
|
1224
1308
|
|
|
1225
1309
|
appendTempUserInput = false;
|
|
@@ -42,7 +42,9 @@ const buildProxyPayload = (
|
|
|
42
42
|
...bodyData,
|
|
43
43
|
url: api,
|
|
44
44
|
provider,
|
|
45
|
+
agentKey: agentConfig.dbKey,
|
|
45
46
|
...(apiSource ? { apiSource } : {}),
|
|
47
|
+
...((agentConfig as any).apiKeyHeader ? { apiKeyHeader: (agentConfig as any).apiKeyHeader } : {}),
|
|
46
48
|
KEY: apiKey,
|
|
47
49
|
};
|
|
48
50
|
};
|
package/ai/chat/fetchUtils.ts
CHANGED
|
@@ -40,7 +40,9 @@ const buildProxyPayload = (
|
|
|
40
40
|
...bodyData,
|
|
41
41
|
url: api,
|
|
42
42
|
provider,
|
|
43
|
+
agentKey: agentConfig.dbKey,
|
|
43
44
|
...(apiSource ? { apiSource } : {}),
|
|
45
|
+
...((agentConfig as any).apiKeyHeader ? { apiKeyHeader: (agentConfig as any).apiKeyHeader } : {}),
|
|
44
46
|
KEY: apiKey,
|
|
45
47
|
};
|
|
46
48
|
};
|
|
@@ -160,6 +160,20 @@ export type CompletionMeta = {
|
|
|
160
160
|
usage?: any;
|
|
161
161
|
};
|
|
162
162
|
|
|
163
|
+
const logQuickChatPerfStage = (
|
|
164
|
+
startedAt: number | undefined,
|
|
165
|
+
stage: string,
|
|
166
|
+
details?: Record<string, unknown>
|
|
167
|
+
) => {
|
|
168
|
+
if (!startedAt) return;
|
|
169
|
+
console.info("[QuickChatPerf]", {
|
|
170
|
+
stage,
|
|
171
|
+
elapsedMs: Date.now() - startedAt,
|
|
172
|
+
...(typeof performance !== "undefined" ? { atMs: performance.now() } : {}),
|
|
173
|
+
...(details ?? {}),
|
|
174
|
+
});
|
|
175
|
+
};
|
|
176
|
+
|
|
163
177
|
/**
|
|
164
178
|
* 初始化流式状态
|
|
165
179
|
*/
|
|
@@ -455,6 +469,7 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
455
469
|
parentMessageId,
|
|
456
470
|
messageMetadata,
|
|
457
471
|
disableToolsForThisRequest = false,
|
|
472
|
+
quickChatPerfStartedAt,
|
|
458
473
|
}: {
|
|
459
474
|
bodyData: any;
|
|
460
475
|
agentConfig: any;
|
|
@@ -463,6 +478,7 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
463
478
|
parentMessageId?: string;
|
|
464
479
|
messageMetadata?: Partial<Message>;
|
|
465
480
|
disableToolsForThisRequest?: boolean;
|
|
481
|
+
quickChatPerfStartedAt?: number;
|
|
466
482
|
}): Promise<CompletionMeta> => {
|
|
467
483
|
const { dispatch, getState, signal: thunkSignal } = thunkApi;
|
|
468
484
|
|
|
@@ -549,6 +565,10 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
549
565
|
|
|
550
566
|
const api = getApiEndpoint(agentConfig);
|
|
551
567
|
const token = selectCurrentToken(getState() as RootState);
|
|
568
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-completions-fetch-starting", {
|
|
569
|
+
api,
|
|
570
|
+
dialogKey,
|
|
571
|
+
});
|
|
552
572
|
const response = await performFetchRequest({
|
|
553
573
|
agentConfig,
|
|
554
574
|
api,
|
|
@@ -557,6 +577,11 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
557
577
|
signal,
|
|
558
578
|
token,
|
|
559
579
|
});
|
|
580
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-completions-fetch-response", {
|
|
581
|
+
ok: response.ok,
|
|
582
|
+
status: response.status,
|
|
583
|
+
dialogKey,
|
|
584
|
+
});
|
|
560
585
|
|
|
561
586
|
if (!response.ok) {
|
|
562
587
|
const errorMessage = await parseApiError(response);
|
|
@@ -578,6 +603,9 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
578
603
|
}
|
|
579
604
|
|
|
580
605
|
const decoder = new TextDecoder();
|
|
606
|
+
let loggedFirstStreamChunk = false;
|
|
607
|
+
let loggedFirstParsedEvent = false;
|
|
608
|
+
let loggedFirstVisibleDelta = false;
|
|
581
609
|
|
|
582
610
|
while (true) {
|
|
583
611
|
const { done, value } = await reader.read();
|
|
@@ -602,8 +630,25 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
602
630
|
break;
|
|
603
631
|
}
|
|
604
632
|
|
|
633
|
+
if (!loggedFirstStreamChunk) {
|
|
634
|
+
loggedFirstStreamChunk = true;
|
|
635
|
+
logQuickChatPerfStage(
|
|
636
|
+
quickChatPerfStartedAt,
|
|
637
|
+
"openai-completions-first-stream-chunk",
|
|
638
|
+
{ dialogKey, byteLength: value.byteLength }
|
|
639
|
+
);
|
|
640
|
+
}
|
|
641
|
+
|
|
605
642
|
const chunk = decoder.decode(value, { stream: true });
|
|
606
643
|
const parsedResults = parseSSE(chunk);
|
|
644
|
+
if (parsedResults.length > 0 && !loggedFirstParsedEvent) {
|
|
645
|
+
loggedFirstParsedEvent = true;
|
|
646
|
+
logQuickChatPerfStage(
|
|
647
|
+
quickChatPerfStartedAt,
|
|
648
|
+
"openai-completions-first-sse-event",
|
|
649
|
+
{ dialogKey, eventCount: parsedResults.length }
|
|
650
|
+
);
|
|
651
|
+
}
|
|
607
652
|
|
|
608
653
|
for (const parsedData of parsedResults) {
|
|
609
654
|
const dataList = Array.isArray(parsedData) ? parsedData : [parsedData];
|
|
@@ -641,6 +686,14 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
641
686
|
delta
|
|
642
687
|
);
|
|
643
688
|
streamState = updatedState;
|
|
689
|
+
if (hasNewVisibleContent && !loggedFirstVisibleDelta) {
|
|
690
|
+
loggedFirstVisibleDelta = true;
|
|
691
|
+
logQuickChatPerfStage(
|
|
692
|
+
quickChatPerfStartedAt,
|
|
693
|
+
"openai-completions-first-visible-delta",
|
|
694
|
+
{ dialogKey }
|
|
695
|
+
);
|
|
696
|
+
}
|
|
644
697
|
|
|
645
698
|
emitStreamingUpdate(hasNewVisibleContent, streamState, {
|
|
646
699
|
dispatch,
|
|
@@ -712,6 +765,9 @@ export const sendOpenAICompletionsRequest = async ({
|
|
|
712
765
|
};
|
|
713
766
|
streamState = await finalizeStream(streamState, finalizeCtx);
|
|
714
767
|
} finally {
|
|
768
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-completions-stream-finished", {
|
|
769
|
+
dialogKey,
|
|
770
|
+
});
|
|
715
771
|
dispatch(removeActiveController({ messageId, dialogKey }));
|
|
716
772
|
try {
|
|
717
773
|
await reader?.cancel();
|
|
@@ -42,6 +42,20 @@ const shouldEnableBuiltInImageGeneration = (agentConfig: any): boolean =>
|
|
|
42
42
|
!getModelInfo(String(agentConfig?.model || ""))?.hasImageOutput &&
|
|
43
43
|
!!agentConfig?.imageConfig?.enabled;
|
|
44
44
|
|
|
45
|
+
const logQuickChatPerfStage = (
|
|
46
|
+
startedAt: number | undefined,
|
|
47
|
+
stage: string,
|
|
48
|
+
details?: Record<string, unknown>
|
|
49
|
+
) => {
|
|
50
|
+
if (!startedAt) return;
|
|
51
|
+
console.info("[QuickChatPerf]", {
|
|
52
|
+
stage,
|
|
53
|
+
elapsedMs: Date.now() - startedAt,
|
|
54
|
+
...(typeof performance !== "undefined" ? { atMs: performance.now() } : {}),
|
|
55
|
+
...(details ?? {}),
|
|
56
|
+
});
|
|
57
|
+
};
|
|
58
|
+
|
|
45
59
|
type StreamState = {
|
|
46
60
|
content: string;
|
|
47
61
|
contentBuffer: Array<
|
|
@@ -140,6 +154,7 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
140
154
|
dialogKey,
|
|
141
155
|
parentMessageId,
|
|
142
156
|
messageMetadata,
|
|
157
|
+
quickChatPerfStartedAt,
|
|
143
158
|
}: {
|
|
144
159
|
bodyData: any;
|
|
145
160
|
agentConfig: any;
|
|
@@ -147,6 +162,7 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
147
162
|
dialogKey: string;
|
|
148
163
|
parentMessageId?: string;
|
|
149
164
|
messageMetadata?: Partial<Message>;
|
|
165
|
+
quickChatPerfStartedAt?: number;
|
|
150
166
|
}): Promise<CompletionMeta> => {
|
|
151
167
|
const { dispatch, getState, signal: thunkSignal } = thunkApi;
|
|
152
168
|
const dialogId = extractCustomId(dialogKey);
|
|
@@ -354,6 +370,10 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
354
370
|
|
|
355
371
|
const api = getApiEndpoint(agentConfig);
|
|
356
372
|
const token = selectCurrentToken(getState() as RootState);
|
|
373
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-response-fetch-starting", {
|
|
374
|
+
api,
|
|
375
|
+
dialogKey,
|
|
376
|
+
});
|
|
357
377
|
const response = await performFetchRequest({
|
|
358
378
|
agentConfig,
|
|
359
379
|
api,
|
|
@@ -362,6 +382,11 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
362
382
|
signal,
|
|
363
383
|
token,
|
|
364
384
|
});
|
|
385
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-response-fetch-response", {
|
|
386
|
+
ok: response.ok,
|
|
387
|
+
status: response.status,
|
|
388
|
+
dialogKey,
|
|
389
|
+
});
|
|
365
390
|
|
|
366
391
|
if (!response.ok) {
|
|
367
392
|
const errorMessage = await parseApiError(response);
|
|
@@ -379,13 +404,33 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
379
404
|
const parseSSE = createSSEParser();
|
|
380
405
|
const decoder = new TextDecoder();
|
|
381
406
|
let finishReason: string | null = null;
|
|
407
|
+
let loggedFirstStreamChunk = false;
|
|
408
|
+
let loggedFirstParsedEvent = false;
|
|
409
|
+
let loggedFirstVisibleDelta = false;
|
|
382
410
|
|
|
383
411
|
while (true) {
|
|
384
412
|
const { done, value } = await reader.read();
|
|
385
413
|
if (done) break;
|
|
386
414
|
|
|
415
|
+
if (!loggedFirstStreamChunk) {
|
|
416
|
+
loggedFirstStreamChunk = true;
|
|
417
|
+
logQuickChatPerfStage(
|
|
418
|
+
quickChatPerfStartedAt,
|
|
419
|
+
"openai-response-first-stream-chunk",
|
|
420
|
+
{ dialogKey, byteLength: value.byteLength }
|
|
421
|
+
);
|
|
422
|
+
}
|
|
423
|
+
|
|
387
424
|
const chunk = decoder.decode(value, { stream: true });
|
|
388
425
|
const events = parseSSE(chunk);
|
|
426
|
+
if (events.length > 0 && !loggedFirstParsedEvent) {
|
|
427
|
+
loggedFirstParsedEvent = true;
|
|
428
|
+
logQuickChatPerfStage(
|
|
429
|
+
quickChatPerfStartedAt,
|
|
430
|
+
"openai-response-first-sse-event",
|
|
431
|
+
{ dialogKey, eventCount: events.length }
|
|
432
|
+
);
|
|
433
|
+
}
|
|
389
434
|
const eventList = Array.isArray(events) ? events : [events];
|
|
390
435
|
|
|
391
436
|
for (const event of eventList) {
|
|
@@ -404,6 +449,14 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
404
449
|
if (event.delta) {
|
|
405
450
|
state.content += event.delta;
|
|
406
451
|
state.contentBuffer = seg(state.content);
|
|
452
|
+
if (!loggedFirstVisibleDelta) {
|
|
453
|
+
loggedFirstVisibleDelta = true;
|
|
454
|
+
logQuickChatPerfStage(
|
|
455
|
+
quickChatPerfStartedAt,
|
|
456
|
+
"openai-response-first-visible-delta",
|
|
457
|
+
{ dialogKey }
|
|
458
|
+
);
|
|
459
|
+
}
|
|
407
460
|
flush();
|
|
408
461
|
}
|
|
409
462
|
break;
|
|
@@ -427,6 +480,14 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
427
480
|
if (itemText) {
|
|
428
481
|
state.content = itemText;
|
|
429
482
|
state.contentBuffer = seg(state.content);
|
|
483
|
+
if (!loggedFirstVisibleDelta) {
|
|
484
|
+
loggedFirstVisibleDelta = true;
|
|
485
|
+
logQuickChatPerfStage(
|
|
486
|
+
quickChatPerfStartedAt,
|
|
487
|
+
"openai-response-first-visible-delta",
|
|
488
|
+
{ dialogKey }
|
|
489
|
+
);
|
|
490
|
+
}
|
|
430
491
|
flush();
|
|
431
492
|
}
|
|
432
493
|
}
|
|
@@ -506,6 +567,9 @@ export const sendOpenAIResponseRequest = async ({
|
|
|
506
567
|
await finalize();
|
|
507
568
|
return buildMeta(false, false, "error");
|
|
508
569
|
} finally {
|
|
570
|
+
logQuickChatPerfStage(quickChatPerfStartedAt, "openai-response-stream-finished", {
|
|
571
|
+
dialogKey,
|
|
572
|
+
});
|
|
509
573
|
dispatch(removeActiveController({ messageId, dialogKey }));
|
|
510
574
|
await safeCancel(reader);
|
|
511
575
|
}
|
package/ai/llm/kimi.ts
CHANGED
|
@@ -2,7 +2,7 @@ export const FIREWORKS_KIMI_LATEST_MODEL = "accounts/fireworks/models/kimi-lates
|
|
|
2
2
|
export const FIREWORKS_KIMI_CURRENT_MODEL = "accounts/fireworks/models/kimi-k2p6";
|
|
3
3
|
export const DEEPINFRA_KIMI_FALLBACK_MODEL = "moonshotai/Kimi-K2.6";
|
|
4
4
|
export const OPENROUTER_KIMI_FALLBACK_MODEL = "moonshotai/kimi-k2.6";
|
|
5
|
-
export const KIMI_PLATFORM_FALLBACK_STATUSES = [402, 429, 500, 502, 503, 504];
|
|
5
|
+
export const KIMI_PLATFORM_FALLBACK_STATUSES = [401, 402, 429, 500, 502, 503, 504];
|
|
6
6
|
|
|
7
7
|
export const isFireworksKimiModel = (model?: string | null): boolean =>
|
|
8
8
|
model === FIREWORKS_KIMI_LATEST_MODEL || model === FIREWORKS_KIMI_CURRENT_MODEL;
|
package/ai/llm/providers.ts
CHANGED
|
@@ -276,6 +276,9 @@ export function getApiEndpoint(agent: Agent): string {
|
|
|
276
276
|
effectiveProvider.toLowerCase() === "custom" ||
|
|
277
277
|
(agent as any).apiSource === "custom"
|
|
278
278
|
) {
|
|
279
|
+
if (agent.useServerProxy) {
|
|
280
|
+
return "";
|
|
281
|
+
}
|
|
279
282
|
throw new Error(
|
|
280
283
|
"Custom provider URL is required when apiSource is 'custom'."
|
|
281
284
|
);
|