@lvce-editor/chat-view 2.0.0 → 2.2.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/chatViewWorkerMain.js +325 -24
- package/package.json +1 -1
|
@@ -1027,7 +1027,7 @@ const createMockRpc = ({
|
|
|
1027
1027
|
};
|
|
1028
1028
|
|
|
1029
1029
|
const rpcs = Object.create(null);
|
|
1030
|
-
const set$
|
|
1030
|
+
const set$4 = (id, rpc) => {
|
|
1031
1031
|
rpcs[id] = rpc;
|
|
1032
1032
|
};
|
|
1033
1033
|
const get$2 = id => {
|
|
@@ -1060,7 +1060,7 @@ const create$2 = rpcId => {
|
|
|
1060
1060
|
const mockRpc = createMockRpc({
|
|
1061
1061
|
commandMap
|
|
1062
1062
|
});
|
|
1063
|
-
set$
|
|
1063
|
+
set$4(rpcId, mockRpc);
|
|
1064
1064
|
// @ts-ignore
|
|
1065
1065
|
mockRpc[Symbol.dispose] = () => {
|
|
1066
1066
|
remove(rpcId);
|
|
@@ -1069,11 +1069,15 @@ const create$2 = rpcId => {
|
|
|
1069
1069
|
return mockRpc;
|
|
1070
1070
|
},
|
|
1071
1071
|
set(rpc) {
|
|
1072
|
-
set$
|
|
1072
|
+
set$4(rpcId, rpc);
|
|
1073
1073
|
}
|
|
1074
1074
|
};
|
|
1075
1075
|
};
|
|
1076
1076
|
|
|
1077
|
+
const {
|
|
1078
|
+
set: set$3
|
|
1079
|
+
} = create$2(6002);
|
|
1080
|
+
|
|
1077
1081
|
const ClientX = 'event.clientX';
|
|
1078
1082
|
const ClientY = 'event.clientY';
|
|
1079
1083
|
const Key = 'event.key';
|
|
@@ -1310,6 +1314,9 @@ const chats = () => {
|
|
|
1310
1314
|
const newChat = () => {
|
|
1311
1315
|
return i18nString('New Chat');
|
|
1312
1316
|
};
|
|
1317
|
+
const debug = () => {
|
|
1318
|
+
return i18nString('Debug');
|
|
1319
|
+
};
|
|
1313
1320
|
const backToChats = () => {
|
|
1314
1321
|
return i18nString('Back to chats');
|
|
1315
1322
|
};
|
|
@@ -2006,7 +2013,7 @@ const now$1 = () => {
|
|
|
2006
2013
|
return new Date().toISOString();
|
|
2007
2014
|
};
|
|
2008
2015
|
const isSameMessage$1 = (a, b) => {
|
|
2009
|
-
return a.id === b.id && a.inProgress === b.inProgress && a.role === b.role && a.text === b.text && a.time === b.time;
|
|
2016
|
+
return a.id === b.id && a.inProgress === b.inProgress && a.role === b.role && a.text === b.text && a.time === b.time && JSON.stringify(a.toolCalls || []) === JSON.stringify(b.toolCalls || []);
|
|
2010
2017
|
};
|
|
2011
2018
|
const canAppendMessages$1 = (previousMessages, nextMessages) => {
|
|
2012
2019
|
if (nextMessages.length < previousMessages.length) {
|
|
@@ -2078,6 +2085,7 @@ const getMutationEvents$1 = (previous, next) => {
|
|
|
2078
2085
|
text: nextMessage.text,
|
|
2079
2086
|
time: nextMessage.time,
|
|
2080
2087
|
timestamp,
|
|
2088
|
+
toolCalls: nextMessage.toolCalls,
|
|
2081
2089
|
type: 'chat-message-updated'
|
|
2082
2090
|
});
|
|
2083
2091
|
}
|
|
@@ -2132,7 +2140,8 @@ const replaySession$1 = (id, summary, events) => {
|
|
|
2132
2140
|
...message,
|
|
2133
2141
|
inProgress: event.inProgress,
|
|
2134
2142
|
text: event.text,
|
|
2135
|
-
time: event.time
|
|
2143
|
+
time: event.time,
|
|
2144
|
+
toolCalls: event.toolCalls
|
|
2136
2145
|
};
|
|
2137
2146
|
});
|
|
2138
2147
|
continue;
|
|
@@ -2311,7 +2320,7 @@ const now = () => {
|
|
|
2311
2320
|
return new Date().toISOString();
|
|
2312
2321
|
};
|
|
2313
2322
|
const isSameMessage = (a, b) => {
|
|
2314
|
-
return a.id === b.id && a.inProgress === b.inProgress && a.role === b.role && a.text === b.text && a.time === b.time;
|
|
2323
|
+
return a.id === b.id && a.inProgress === b.inProgress && a.role === b.role && a.text === b.text && a.time === b.time && JSON.stringify(a.toolCalls || []) === JSON.stringify(b.toolCalls || []);
|
|
2315
2324
|
};
|
|
2316
2325
|
const canAppendMessages = (previousMessages, nextMessages) => {
|
|
2317
2326
|
if (nextMessages.length < previousMessages.length) {
|
|
@@ -2383,6 +2392,7 @@ const getMutationEvents = (previous, next) => {
|
|
|
2383
2392
|
text: nextMessage.text,
|
|
2384
2393
|
time: nextMessage.time,
|
|
2385
2394
|
timestamp,
|
|
2395
|
+
toolCalls: nextMessage.toolCalls,
|
|
2386
2396
|
type: 'chat-message-updated'
|
|
2387
2397
|
});
|
|
2388
2398
|
}
|
|
@@ -2431,7 +2441,8 @@ const replaySession = (id, title, events) => {
|
|
|
2431
2441
|
...message,
|
|
2432
2442
|
inProgress: event.inProgress,
|
|
2433
2443
|
text: event.text,
|
|
2434
|
-
time: event.time
|
|
2444
|
+
time: event.time,
|
|
2445
|
+
toolCalls: event.toolCalls
|
|
2435
2446
|
};
|
|
2436
2447
|
});
|
|
2437
2448
|
continue;
|
|
@@ -3084,7 +3095,59 @@ const parseSseEvent = eventChunk => {
|
|
|
3084
3095
|
}
|
|
3085
3096
|
return dataLines;
|
|
3086
3097
|
};
|
|
3087
|
-
const
|
|
3098
|
+
const updateToolCallAccumulator = (accumulator, chunk) => {
|
|
3099
|
+
let changed = false;
|
|
3100
|
+
const nextAccumulator = {
|
|
3101
|
+
...accumulator
|
|
3102
|
+
};
|
|
3103
|
+
for (const item of chunk) {
|
|
3104
|
+
if (!item || typeof item !== 'object') {
|
|
3105
|
+
continue;
|
|
3106
|
+
}
|
|
3107
|
+
const index = Reflect.get(item, 'index');
|
|
3108
|
+
if (typeof index !== 'number') {
|
|
3109
|
+
continue;
|
|
3110
|
+
}
|
|
3111
|
+
const current = nextAccumulator[index] || {
|
|
3112
|
+
arguments: '',
|
|
3113
|
+
name: ''
|
|
3114
|
+
};
|
|
3115
|
+
const id = Reflect.get(item, 'id');
|
|
3116
|
+
const toolFunction = Reflect.get(item, 'function');
|
|
3117
|
+
let {
|
|
3118
|
+
name
|
|
3119
|
+
} = current;
|
|
3120
|
+
let args = current.arguments;
|
|
3121
|
+
if (toolFunction && typeof toolFunction === 'object') {
|
|
3122
|
+
const deltaName = Reflect.get(toolFunction, 'name');
|
|
3123
|
+
const deltaArguments = Reflect.get(toolFunction, 'arguments');
|
|
3124
|
+
if (typeof deltaName === 'string' && deltaName) {
|
|
3125
|
+
name = deltaName;
|
|
3126
|
+
}
|
|
3127
|
+
if (typeof deltaArguments === 'string') {
|
|
3128
|
+
args += deltaArguments;
|
|
3129
|
+
}
|
|
3130
|
+
}
|
|
3131
|
+
const next = {
|
|
3132
|
+
arguments: args,
|
|
3133
|
+
id: typeof id === 'string' ? id : current.id,
|
|
3134
|
+
name
|
|
3135
|
+
};
|
|
3136
|
+
if (JSON.stringify(next) !== JSON.stringify(current)) {
|
|
3137
|
+
nextAccumulator[index] = next;
|
|
3138
|
+
changed = true;
|
|
3139
|
+
}
|
|
3140
|
+
}
|
|
3141
|
+
if (!changed) {
|
|
3142
|
+
return undefined;
|
|
3143
|
+
}
|
|
3144
|
+
const toolCalls = Object.entries(nextAccumulator).toSorted((a, b) => Number(a[0]) - Number(b[0])).map(entry => entry[1]).filter(toolCall => !!toolCall.name);
|
|
3145
|
+
return {
|
|
3146
|
+
nextAccumulator,
|
|
3147
|
+
toolCalls
|
|
3148
|
+
};
|
|
3149
|
+
};
|
|
3150
|
+
const parseOpenApiStream = async (response, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished) => {
|
|
3088
3151
|
if (!response.body) {
|
|
3089
3152
|
return {
|
|
3090
3153
|
details: 'request-failed',
|
|
@@ -3096,6 +3159,7 @@ const parseOpenApiStream = async (response, onTextChunk) => {
|
|
|
3096
3159
|
let remainder = '';
|
|
3097
3160
|
let text = '';
|
|
3098
3161
|
let done = false;
|
|
3162
|
+
let toolCallAccumulator = {};
|
|
3099
3163
|
while (!done) {
|
|
3100
3164
|
const {
|
|
3101
3165
|
done: streamDone,
|
|
@@ -3121,6 +3185,9 @@ const parseOpenApiStream = async (response, onTextChunk) => {
|
|
|
3121
3185
|
}
|
|
3122
3186
|
for (const line of dataLines) {
|
|
3123
3187
|
if (line === '[DONE]') {
|
|
3188
|
+
if (onEventStreamFinished) {
|
|
3189
|
+
await onEventStreamFinished();
|
|
3190
|
+
}
|
|
3124
3191
|
done = true;
|
|
3125
3192
|
break;
|
|
3126
3193
|
}
|
|
@@ -3133,6 +3200,9 @@ const parseOpenApiStream = async (response, onTextChunk) => {
|
|
|
3133
3200
|
if (!parsed || typeof parsed !== 'object') {
|
|
3134
3201
|
continue;
|
|
3135
3202
|
}
|
|
3203
|
+
if (onDataEvent) {
|
|
3204
|
+
await onDataEvent(parsed);
|
|
3205
|
+
}
|
|
3136
3206
|
const choices = Reflect.get(parsed, 'choices');
|
|
3137
3207
|
if (!Array.isArray(choices)) {
|
|
3138
3208
|
continue;
|
|
@@ -3145,6 +3215,14 @@ const parseOpenApiStream = async (response, onTextChunk) => {
|
|
|
3145
3215
|
if (!delta || typeof delta !== 'object') {
|
|
3146
3216
|
continue;
|
|
3147
3217
|
}
|
|
3218
|
+
const toolCalls = Reflect.get(delta, 'tool_calls');
|
|
3219
|
+
const updatedToolCallResult = Array.isArray(toolCalls) ? updateToolCallAccumulator(toolCallAccumulator, toolCalls) : undefined;
|
|
3220
|
+
if (updatedToolCallResult) {
|
|
3221
|
+
toolCallAccumulator = updatedToolCallResult.nextAccumulator;
|
|
3222
|
+
}
|
|
3223
|
+
if (updatedToolCallResult && onToolCallsChunk) {
|
|
3224
|
+
await onToolCallsChunk(updatedToolCallResult.toolCalls);
|
|
3225
|
+
}
|
|
3148
3226
|
const content = Reflect.get(delta, 'content');
|
|
3149
3227
|
const chunkText = getStreamChunkText(content);
|
|
3150
3228
|
if (!chunkText) {
|
|
@@ -3161,6 +3239,9 @@ const parseOpenApiStream = async (response, onTextChunk) => {
|
|
|
3161
3239
|
const dataLines = parseSseEvent(remainder);
|
|
3162
3240
|
for (const line of dataLines) {
|
|
3163
3241
|
if (line === '[DONE]') {
|
|
3242
|
+
if (onEventStreamFinished) {
|
|
3243
|
+
await onEventStreamFinished();
|
|
3244
|
+
}
|
|
3164
3245
|
continue;
|
|
3165
3246
|
}
|
|
3166
3247
|
let parsed;
|
|
@@ -3172,6 +3253,9 @@ const parseOpenApiStream = async (response, onTextChunk) => {
|
|
|
3172
3253
|
if (!parsed || typeof parsed !== 'object') {
|
|
3173
3254
|
continue;
|
|
3174
3255
|
}
|
|
3256
|
+
if (onDataEvent) {
|
|
3257
|
+
await onDataEvent(parsed);
|
|
3258
|
+
}
|
|
3175
3259
|
const choices = Reflect.get(parsed, 'choices');
|
|
3176
3260
|
if (!Array.isArray(choices)) {
|
|
3177
3261
|
continue;
|
|
@@ -3184,6 +3268,14 @@ const parseOpenApiStream = async (response, onTextChunk) => {
|
|
|
3184
3268
|
if (!delta || typeof delta !== 'object') {
|
|
3185
3269
|
continue;
|
|
3186
3270
|
}
|
|
3271
|
+
const toolCalls = Reflect.get(delta, 'tool_calls');
|
|
3272
|
+
const updatedToolCallResult = Array.isArray(toolCalls) ? updateToolCallAccumulator(toolCallAccumulator, toolCalls) : undefined;
|
|
3273
|
+
if (updatedToolCallResult) {
|
|
3274
|
+
toolCallAccumulator = updatedToolCallResult.nextAccumulator;
|
|
3275
|
+
}
|
|
3276
|
+
if (updatedToolCallResult && onToolCallsChunk) {
|
|
3277
|
+
await onToolCallsChunk(updatedToolCallResult.toolCalls);
|
|
3278
|
+
}
|
|
3187
3279
|
const content = Reflect.get(delta, 'content');
|
|
3188
3280
|
const chunkText = getStreamChunkText(content);
|
|
3189
3281
|
if (!chunkText) {
|
|
@@ -3226,7 +3318,10 @@ const getOpenApiErrorDetails = async response => {
|
|
|
3226
3318
|
const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApiApiBaseUrl, assetDir, platform, options) => {
|
|
3227
3319
|
const {
|
|
3228
3320
|
includeObfuscation = false,
|
|
3321
|
+
onDataEvent,
|
|
3322
|
+
onEventStreamFinished,
|
|
3229
3323
|
onTextChunk,
|
|
3324
|
+
onToolCallsChunk,
|
|
3230
3325
|
stream
|
|
3231
3326
|
} = options ?? {
|
|
3232
3327
|
stream: false
|
|
@@ -3271,7 +3366,7 @@ const getOpenApiAssistantText = async (messages, modelId, openApiApiKey, openApi
|
|
|
3271
3366
|
};
|
|
3272
3367
|
}
|
|
3273
3368
|
if (stream) {
|
|
3274
|
-
return parseOpenApiStream(response, onTextChunk);
|
|
3369
|
+
return parseOpenApiStream(response, onTextChunk, onToolCallsChunk, onDataEvent, onEventStreamFinished);
|
|
3275
3370
|
}
|
|
3276
3371
|
let parsed;
|
|
3277
3372
|
try {
|
|
@@ -3685,11 +3780,14 @@ const isOpenRouterModel = (selectedModelId, models) => {
|
|
|
3685
3780
|
|
|
3686
3781
|
const getAiResponse = async ({
|
|
3687
3782
|
assetDir,
|
|
3783
|
+
messageId,
|
|
3688
3784
|
messages,
|
|
3689
3785
|
mockApiCommandId,
|
|
3690
3786
|
models,
|
|
3691
|
-
|
|
3787
|
+
onDataEvent,
|
|
3788
|
+
onEventStreamFinished,
|
|
3692
3789
|
onTextChunk,
|
|
3790
|
+
onToolCallsChunk,
|
|
3693
3791
|
openApiApiBaseUrl,
|
|
3694
3792
|
openApiApiKey,
|
|
3695
3793
|
openRouterApiBaseUrl,
|
|
@@ -3718,7 +3816,10 @@ const getAiResponse = async ({
|
|
|
3718
3816
|
} else if (openApiApiKey) {
|
|
3719
3817
|
const result = await getOpenApiAssistantText(messages, getOpenApiModelId(selectedModelId), openApiApiKey, openApiApiBaseUrl, assetDir, platform, {
|
|
3720
3818
|
includeObfuscation: passIncludeObfuscation,
|
|
3819
|
+
onDataEvent,
|
|
3820
|
+
onEventStreamFinished,
|
|
3721
3821
|
onTextChunk,
|
|
3822
|
+
onToolCallsChunk,
|
|
3722
3823
|
stream: streamingEnabled
|
|
3723
3824
|
});
|
|
3724
3825
|
if (result.type === 'success') {
|
|
@@ -3766,7 +3867,7 @@ const getAiResponse = async ({
|
|
|
3766
3867
|
minute: '2-digit'
|
|
3767
3868
|
});
|
|
3768
3869
|
return {
|
|
3769
|
-
id:
|
|
3870
|
+
id: messageId || crypto.randomUUID(),
|
|
3770
3871
|
role: 'assistant',
|
|
3771
3872
|
text,
|
|
3772
3873
|
time: assistantTime
|
|
@@ -3821,7 +3922,6 @@ const handleClickSaveOpenApiApiKey = async state => {
|
|
|
3821
3922
|
messages: retryMessages,
|
|
3822
3923
|
mockApiCommandId: updatedState.mockApiCommandId,
|
|
3823
3924
|
models: updatedState.models,
|
|
3824
|
-
nextMessageId: updatedState.nextMessageId,
|
|
3825
3925
|
openApiApiBaseUrl: updatedState.openApiApiBaseUrl,
|
|
3826
3926
|
openApiApiKey: updatedState.openApiApiKey,
|
|
3827
3927
|
openRouterApiBaseUrl: updatedState.openRouterApiBaseUrl,
|
|
@@ -3901,7 +4001,6 @@ const handleClickSaveOpenRouterApiKey = async state => {
|
|
|
3901
4001
|
messages: retryMessages,
|
|
3902
4002
|
mockApiCommandId: updatedState.mockApiCommandId,
|
|
3903
4003
|
models: updatedState.models,
|
|
3904
|
-
nextMessageId: updatedState.nextMessageId,
|
|
3905
4004
|
openApiApiBaseUrl: updatedState.openApiApiBaseUrl,
|
|
3906
4005
|
openApiApiKey: updatedState.openApiApiKey,
|
|
3907
4006
|
openRouterApiBaseUrl: updatedState.openRouterApiBaseUrl,
|
|
@@ -3958,6 +4057,25 @@ const updateMessageTextInSelectedSession = (sessions, selectedSessionId, message
|
|
|
3958
4057
|
};
|
|
3959
4058
|
});
|
|
3960
4059
|
};
|
|
4060
|
+
const updateMessageToolCallsInSelectedSession = (sessions, selectedSessionId, messageId, toolCalls) => {
|
|
4061
|
+
return sessions.map(session => {
|
|
4062
|
+
if (session.id !== selectedSessionId) {
|
|
4063
|
+
return session;
|
|
4064
|
+
}
|
|
4065
|
+
return {
|
|
4066
|
+
...session,
|
|
4067
|
+
messages: session.messages.map(message => {
|
|
4068
|
+
if (message.id !== messageId) {
|
|
4069
|
+
return message;
|
|
4070
|
+
}
|
|
4071
|
+
return {
|
|
4072
|
+
...message,
|
|
4073
|
+
toolCalls
|
|
4074
|
+
};
|
|
4075
|
+
})
|
|
4076
|
+
};
|
|
4077
|
+
});
|
|
4078
|
+
};
|
|
3961
4079
|
const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTextChunkState) => {
|
|
3962
4080
|
const selectedSession = handleTextChunkState.latestState.sessions.find(session => session.id === handleTextChunkState.latestState.selectedSessionId);
|
|
3963
4081
|
if (!selectedSession) {
|
|
@@ -3994,6 +4112,34 @@ const handleTextChunkFunction = async (uid, assistantMessageId, chunk, handleTex
|
|
|
3994
4112
|
previousState: nextState
|
|
3995
4113
|
};
|
|
3996
4114
|
};
|
|
4115
|
+
const handleToolCallsChunkFunction = async (uid, assistantMessageId, toolCalls, handleTextChunkState) => {
|
|
4116
|
+
const selectedSession = handleTextChunkState.latestState.sessions.find(session => session.id === handleTextChunkState.latestState.selectedSessionId);
|
|
4117
|
+
if (!selectedSession) {
|
|
4118
|
+
return {
|
|
4119
|
+
latestState: handleTextChunkState.latestState,
|
|
4120
|
+
previousState: handleTextChunkState.previousState
|
|
4121
|
+
};
|
|
4122
|
+
}
|
|
4123
|
+
const assistantMessage = selectedSession.messages.find(message => message.id === assistantMessageId);
|
|
4124
|
+
if (!assistantMessage) {
|
|
4125
|
+
return {
|
|
4126
|
+
latestState: handleTextChunkState.latestState,
|
|
4127
|
+
previousState: handleTextChunkState.previousState
|
|
4128
|
+
};
|
|
4129
|
+
}
|
|
4130
|
+
const updatedSessions = updateMessageToolCallsInSelectedSession(handleTextChunkState.latestState.sessions, handleTextChunkState.latestState.selectedSessionId, assistantMessageId, toolCalls);
|
|
4131
|
+
const nextState = {
|
|
4132
|
+
...handleTextChunkState.latestState,
|
|
4133
|
+
sessions: updatedSessions
|
|
4134
|
+
};
|
|
4135
|
+
set(uid, handleTextChunkState.previousState, nextState);
|
|
4136
|
+
// @ts-ignore
|
|
4137
|
+
await invoke('Chat.rerender');
|
|
4138
|
+
return {
|
|
4139
|
+
latestState: nextState,
|
|
4140
|
+
previousState: nextState
|
|
4141
|
+
};
|
|
4142
|
+
};
|
|
3997
4143
|
|
|
3998
4144
|
const appendMessageToSelectedSession = (sessions, selectedSessionId, message) => {
|
|
3999
4145
|
return sessions.map(session => {
|
|
@@ -4034,13 +4180,14 @@ const handleSubmit = async state => {
|
|
|
4034
4180
|
hour: '2-digit',
|
|
4035
4181
|
minute: '2-digit'
|
|
4036
4182
|
});
|
|
4183
|
+
const userMessageId = crypto.randomUUID();
|
|
4037
4184
|
const userMessage = {
|
|
4038
|
-
id:
|
|
4185
|
+
id: userMessageId,
|
|
4039
4186
|
role: 'user',
|
|
4040
4187
|
text: userText,
|
|
4041
4188
|
time: userTime
|
|
4042
4189
|
};
|
|
4043
|
-
const assistantMessageId =
|
|
4190
|
+
const assistantMessageId = crypto.randomUUID();
|
|
4044
4191
|
const assistantTime = new Date().toLocaleTimeString([], {
|
|
4045
4192
|
hour: '2-digit',
|
|
4046
4193
|
minute: '2-digit'
|
|
@@ -4127,11 +4274,30 @@ const handleSubmit = async state => {
|
|
|
4127
4274
|
} : undefined;
|
|
4128
4275
|
const assistantMessage = await getAiResponse({
|
|
4129
4276
|
assetDir,
|
|
4277
|
+
messageId: assistantMessageId,
|
|
4130
4278
|
messages,
|
|
4131
4279
|
mockApiCommandId,
|
|
4132
4280
|
models,
|
|
4133
|
-
|
|
4281
|
+
onDataEvent: async value => {
|
|
4282
|
+
await appendChatViewEvent({
|
|
4283
|
+
sessionId: optimisticState.selectedSessionId,
|
|
4284
|
+
timestamp: new Date().toISOString(),
|
|
4285
|
+
type: 'sse-response-part',
|
|
4286
|
+
value
|
|
4287
|
+
});
|
|
4288
|
+
},
|
|
4289
|
+
onEventStreamFinished: async () => {
|
|
4290
|
+
await appendChatViewEvent({
|
|
4291
|
+
sessionId: optimisticState.selectedSessionId,
|
|
4292
|
+
timestamp: new Date().toISOString(),
|
|
4293
|
+
type: 'event-stream-finished',
|
|
4294
|
+
value: '[DONE]'
|
|
4295
|
+
});
|
|
4296
|
+
},
|
|
4134
4297
|
onTextChunk: handleTextChunkFunctionRef,
|
|
4298
|
+
onToolCallsChunk: async toolCalls => {
|
|
4299
|
+
handleTextChunkState = await handleToolCallsChunkFunction(state.uid, assistantMessageId, toolCalls, handleTextChunkState);
|
|
4300
|
+
},
|
|
4135
4301
|
openApiApiBaseUrl,
|
|
4136
4302
|
openApiApiKey,
|
|
4137
4303
|
openRouterApiBaseUrl,
|
|
@@ -4177,6 +4343,7 @@ const Send = 'send';
|
|
|
4177
4343
|
const Back = 'back';
|
|
4178
4344
|
const Model = 'model';
|
|
4179
4345
|
const CreateSession = 'create-session';
|
|
4346
|
+
const SessionDebug = 'session-debug';
|
|
4180
4347
|
const Settings = 'settings';
|
|
4181
4348
|
const CloseChat = 'close-chat';
|
|
4182
4349
|
const SessionDelete = 'SessionDelete';
|
|
@@ -4321,6 +4488,11 @@ const handleClickNew = async state => {
|
|
|
4321
4488
|
return createSession(state);
|
|
4322
4489
|
};
|
|
4323
4490
|
|
|
4491
|
+
const handleClickSessionDebug = async state => {
|
|
4492
|
+
await invoke('Main.openUri', `chat-debug://${state.selectedSessionId}`);
|
|
4493
|
+
return state;
|
|
4494
|
+
};
|
|
4495
|
+
|
|
4324
4496
|
const handleClickSettings = async () => {
|
|
4325
4497
|
// TODO
|
|
4326
4498
|
await invoke('Main.openUri', 'app://settings.json');
|
|
@@ -4377,7 +4549,7 @@ const handleInputFocus = async (state, name) => {
|
|
|
4377
4549
|
focused: true
|
|
4378
4550
|
};
|
|
4379
4551
|
}
|
|
4380
|
-
if (name === CreateSession || name === Settings || name === CloseChat || name === Back) {
|
|
4552
|
+
if (name === CreateSession || name === SessionDebug || name === Settings || name === CloseChat || name === Back) {
|
|
4381
4553
|
return {
|
|
4382
4554
|
...state,
|
|
4383
4555
|
focus: 'header',
|
|
@@ -4866,6 +5038,7 @@ const HandleSubmit = 19;
|
|
|
4866
5038
|
const HandleModelChange = 20;
|
|
4867
5039
|
const HandleChatListScroll = 21;
|
|
4868
5040
|
const HandleMessagesScroll = 22;
|
|
5041
|
+
const HandleClickSessionDebug = 23;
|
|
4869
5042
|
|
|
4870
5043
|
const getModelLabel = model => {
|
|
4871
5044
|
if (model.provider === 'openRouter') {
|
|
@@ -5013,6 +5186,11 @@ const getHeaderActionVirtualDom = item => {
|
|
|
5013
5186
|
|
|
5014
5187
|
const getChatHeaderActionsDom = () => {
|
|
5015
5188
|
const items = [{
|
|
5189
|
+
icon: 'MaskIcon MaskIconDebugPause',
|
|
5190
|
+
name: SessionDebug,
|
|
5191
|
+
onClick: HandleClickSessionDebug,
|
|
5192
|
+
title: debug()
|
|
5193
|
+
}, {
|
|
5016
5194
|
icon: 'MaskIcon MaskIconAdd',
|
|
5017
5195
|
name: CreateSession,
|
|
5018
5196
|
onClick: HandleClickNew,
|
|
@@ -5117,6 +5295,126 @@ const getMissingOpenRouterApiKeyDom = (openRouterApiKeyInput, openRouterApiKeySt
|
|
|
5117
5295
|
});
|
|
5118
5296
|
};
|
|
5119
5297
|
|
|
5298
|
+
const orderedListItemRegex = /^\s*\d+\.\s+(.*)$/;
|
|
5299
|
+
const parseMessageContent = rawMessage => {
|
|
5300
|
+
if (rawMessage === '') {
|
|
5301
|
+
return [{
|
|
5302
|
+
text: '',
|
|
5303
|
+
type: 'text'
|
|
5304
|
+
}];
|
|
5305
|
+
}
|
|
5306
|
+
const lines = rawMessage.split(/\r?\n/);
|
|
5307
|
+
const nodes = [];
|
|
5308
|
+
let paragraphLines = [];
|
|
5309
|
+
let listItems = [];
|
|
5310
|
+
const flushParagraph = () => {
|
|
5311
|
+
if (paragraphLines.length === 0) {
|
|
5312
|
+
return;
|
|
5313
|
+
}
|
|
5314
|
+
nodes.push({
|
|
5315
|
+
text: paragraphLines.join('\n'),
|
|
5316
|
+
type: 'text'
|
|
5317
|
+
});
|
|
5318
|
+
paragraphLines = [];
|
|
5319
|
+
};
|
|
5320
|
+
const flushList = () => {
|
|
5321
|
+
if (listItems.length === 0) {
|
|
5322
|
+
return;
|
|
5323
|
+
}
|
|
5324
|
+
nodes.push({
|
|
5325
|
+
items: listItems,
|
|
5326
|
+
type: 'list'
|
|
5327
|
+
});
|
|
5328
|
+
listItems = [];
|
|
5329
|
+
};
|
|
5330
|
+
for (const line of lines) {
|
|
5331
|
+
if (!line.trim()) {
|
|
5332
|
+
flushList();
|
|
5333
|
+
flushParagraph();
|
|
5334
|
+
continue;
|
|
5335
|
+
}
|
|
5336
|
+
const match = line.match(orderedListItemRegex);
|
|
5337
|
+
if (match) {
|
|
5338
|
+
flushParagraph();
|
|
5339
|
+
listItems.push({
|
|
5340
|
+
text: match[1],
|
|
5341
|
+
type: 'list-item'
|
|
5342
|
+
});
|
|
5343
|
+
continue;
|
|
5344
|
+
}
|
|
5345
|
+
flushList();
|
|
5346
|
+
paragraphLines.push(line);
|
|
5347
|
+
}
|
|
5348
|
+
flushList();
|
|
5349
|
+
flushParagraph();
|
|
5350
|
+
return nodes.length === 0 ? [{
|
|
5351
|
+
text: '',
|
|
5352
|
+
type: 'text'
|
|
5353
|
+
}] : nodes;
|
|
5354
|
+
};
|
|
5355
|
+
const getMessageContentDom = nodes => {
|
|
5356
|
+
return nodes.flatMap(node => {
|
|
5357
|
+
if (node.type === 'text') {
|
|
5358
|
+
return [{
|
|
5359
|
+
childCount: 1,
|
|
5360
|
+
className: Markdown,
|
|
5361
|
+
type: P
|
|
5362
|
+
}, text(node.text)];
|
|
5363
|
+
}
|
|
5364
|
+
return [{
|
|
5365
|
+
childCount: node.items.length,
|
|
5366
|
+
className: ChatOrderedList,
|
|
5367
|
+
type: Ol
|
|
5368
|
+
}, ...node.items.flatMap(item => {
|
|
5369
|
+
return [{
|
|
5370
|
+
childCount: 1,
|
|
5371
|
+
className: ChatOrderedListItem,
|
|
5372
|
+
type: Li
|
|
5373
|
+
}, text(item.text)];
|
|
5374
|
+
})];
|
|
5375
|
+
});
|
|
5376
|
+
};
|
|
5377
|
+
|
|
5378
|
+
const getToolCallArgumentPreview = rawArguments => {
|
|
5379
|
+
if (!rawArguments.trim()) {
|
|
5380
|
+
return '""';
|
|
5381
|
+
}
|
|
5382
|
+
let parsed;
|
|
5383
|
+
try {
|
|
5384
|
+
parsed = JSON.parse(rawArguments);
|
|
5385
|
+
} catch {
|
|
5386
|
+
return rawArguments;
|
|
5387
|
+
}
|
|
5388
|
+
if (!parsed || typeof parsed !== 'object') {
|
|
5389
|
+
return rawArguments;
|
|
5390
|
+
}
|
|
5391
|
+
const path = Reflect.get(parsed, 'path');
|
|
5392
|
+
if (typeof path === 'string') {
|
|
5393
|
+
return `"${path}"`;
|
|
5394
|
+
}
|
|
5395
|
+
const keys = Object.keys(parsed);
|
|
5396
|
+
if (keys.length === 1) {
|
|
5397
|
+
const value = Reflect.get(parsed, keys[0]);
|
|
5398
|
+
if (typeof value === 'string') {
|
|
5399
|
+
return `"${value}"`;
|
|
5400
|
+
}
|
|
5401
|
+
}
|
|
5402
|
+
return rawArguments;
|
|
5403
|
+
};
|
|
5404
|
+
const getToolCallsDom = message => {
|
|
5405
|
+
if (message.role !== 'assistant' || !message.toolCalls || message.toolCalls.length === 0) {
|
|
5406
|
+
return [];
|
|
5407
|
+
}
|
|
5408
|
+
return message.toolCalls.flatMap(toolCall => {
|
|
5409
|
+
const argumentPreview = getToolCallArgumentPreview(toolCall.arguments);
|
|
5410
|
+
const label = `${toolCall.name} ${argumentPreview}`;
|
|
5411
|
+
return [{
|
|
5412
|
+
childCount: 1,
|
|
5413
|
+
className: Markdown,
|
|
5414
|
+
type: P
|
|
5415
|
+
}, text(label)];
|
|
5416
|
+
});
|
|
5417
|
+
};
|
|
5120
5418
|
const getOpenRouterRequestFailedDom = () => {
|
|
5121
5419
|
return [{
|
|
5122
5420
|
childCount: openRouterRequestFailureReasons.length,
|
|
@@ -5149,7 +5447,10 @@ const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput =
|
|
|
5149
5447
|
const isOpenRouterApiKeyMissingMessage = message.role === 'assistant' && message.text === openRouterApiKeyRequiredMessage;
|
|
5150
5448
|
const isOpenRouterRequestFailedMessage = message.role === 'assistant' && message.text === openRouterRequestFailedMessage;
|
|
5151
5449
|
const isOpenRouterTooManyRequestsMessage = message.role === 'assistant' && message.text.startsWith(openRouterTooManyRequestsMessage);
|
|
5152
|
-
const
|
|
5450
|
+
const messageIntermediate = parseMessageContent(message.text);
|
|
5451
|
+
const messageDom = getMessageContentDom(messageIntermediate);
|
|
5452
|
+
const toolCallsDom = getToolCallsDom(message);
|
|
5453
|
+
const extraChildCount = isOpenApiApiKeyMissingMessage || isOpenRouterApiKeyMissingMessage || isOpenRouterRequestFailedMessage || isOpenRouterTooManyRequestsMessage ? messageIntermediate.length + 1 + toolCallsDom.length : messageIntermediate.length + toolCallsDom.length;
|
|
5153
5454
|
return [{
|
|
5154
5455
|
childCount: 1,
|
|
5155
5456
|
className: mergeClassNames(Message, roleClassName),
|
|
@@ -5158,11 +5459,7 @@ const getChatMessageDom = (message, openRouterApiKeyInput, openApiApiKeyInput =
|
|
|
5158
5459
|
childCount: extraChildCount,
|
|
5159
5460
|
className: ChatMessageContent,
|
|
5160
5461
|
type: Div
|
|
5161
|
-
},
|
|
5162
|
-
childCount: 1,
|
|
5163
|
-
className: Markdown,
|
|
5164
|
-
type: P
|
|
5165
|
-
}, text(message.text), ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyInput) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyInput, openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
|
|
5462
|
+
}, ...messageDom, ...toolCallsDom, ...(isOpenApiApiKeyMissingMessage ? getMissingOpenApiApiKeyDom(openApiApiKeyInput) : []), ...(isOpenRouterApiKeyMissingMessage ? getMissingOpenRouterApiKeyDom(openRouterApiKeyInput, openRouterApiKeyState) : []), ...(isOpenRouterRequestFailedMessage ? getOpenRouterRequestFailedDom() : []), ...(isOpenRouterTooManyRequestsMessage ? getOpenRouterTooManyRequestsDom() : [])];
|
|
5166
5463
|
};
|
|
5167
5464
|
|
|
5168
5465
|
const getEmptyMessagesDom = () => {
|
|
@@ -5406,6 +5703,9 @@ const renderEventListeners = () => {
|
|
|
5406
5703
|
}, {
|
|
5407
5704
|
name: HandleClickNew,
|
|
5408
5705
|
params: ['handleClickNew']
|
|
5706
|
+
}, {
|
|
5707
|
+
name: HandleClickSessionDebug,
|
|
5708
|
+
params: ['handleClickSessionDebug']
|
|
5409
5709
|
}, {
|
|
5410
5710
|
name: HandleClickBack,
|
|
5411
5711
|
params: ['handleClickBack']
|
|
@@ -5553,6 +5853,7 @@ const commandMap = {
|
|
|
5553
5853
|
'Chat.handleClickDelete': wrapCommand(handleClickDelete),
|
|
5554
5854
|
'Chat.handleClickList': wrapCommand(handleClickList),
|
|
5555
5855
|
'Chat.handleClickNew': wrapCommand(handleClickNew),
|
|
5856
|
+
'Chat.handleClickSessionDebug': wrapCommand(handleClickSessionDebug),
|
|
5556
5857
|
'Chat.handleClickSettings': handleClickSettings,
|
|
5557
5858
|
'Chat.handleInput': wrapCommand(handleInput),
|
|
5558
5859
|
'Chat.handleInputFocus': wrapCommand(handleInputFocus),
|
|
@@ -5588,7 +5889,7 @@ const initializeChatNetworkWorker = async () => {
|
|
|
5588
5889
|
commandMap: {},
|
|
5589
5890
|
send
|
|
5590
5891
|
});
|
|
5591
|
-
|
|
5892
|
+
set$3(rpc);
|
|
5592
5893
|
};
|
|
5593
5894
|
|
|
5594
5895
|
const listen = async () => {
|