@yushaw/sanqian-chat 0.1.1 → 0.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/README.md +116 -0
- package/dist/core/index.d.mts +180 -6
- package/dist/core/index.d.ts +180 -6
- package/dist/core/index.js +548 -64
- package/dist/core/index.mjs +534 -63
- package/dist/main/index.d.mts +320 -5
- package/dist/main/index.d.ts +320 -5
- package/dist/main/index.js +466 -39
- package/dist/main/index.mjs +454 -38
- package/dist/preload/index.d.ts +88 -0
- package/dist/preload/index.js +5 -1
- package/dist/renderer/index.d.mts +609 -18
- package/dist/renderer/index.d.ts +609 -18
- package/dist/renderer/index.js +7870 -537
- package/dist/renderer/index.mjs +7854 -538
- package/package.json +16 -4
- package/src/renderer/styles/baseStyles.ts +74 -0
- package/src/renderer/styles/chat.css +3674 -0
- package/src/renderer/styles/chatCss.ts +11 -0
- package/src/renderer/styles/coreCss.ts +3237 -0
- package/src/renderer/styles/preflightCss.ts +454 -0
- package/src/renderer/styles/safe.css +3227 -0
- package/src/renderer/styles/safeCss.ts +10 -0
- package/src/renderer/styles/tailwind.css +683 -0
- package/src/renderer/styles/variables.css +704 -0
package/dist/core/index.mjs
CHANGED
|
@@ -1,4 +1,491 @@
|
|
|
1
|
+
// src/core/history.ts
|
|
2
|
+
function safeParseArgs(value) {
|
|
3
|
+
if (!value) return void 0;
|
|
4
|
+
if (typeof value === "object") return value;
|
|
5
|
+
if (typeof value === "string") {
|
|
6
|
+
try {
|
|
7
|
+
return JSON.parse(value);
|
|
8
|
+
} catch {
|
|
9
|
+
return void 0;
|
|
10
|
+
}
|
|
11
|
+
}
|
|
12
|
+
return void 0;
|
|
13
|
+
}
|
|
14
|
+
function parseToolCalls(toolCalls) {
|
|
15
|
+
if (!toolCalls) return void 0;
|
|
16
|
+
let calls = toolCalls;
|
|
17
|
+
if (typeof calls === "string") {
|
|
18
|
+
try {
|
|
19
|
+
calls = JSON.parse(calls);
|
|
20
|
+
} catch {
|
|
21
|
+
return void 0;
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (!Array.isArray(calls)) return void 0;
|
|
25
|
+
return calls.map((tc) => {
|
|
26
|
+
const fn = tc.function || void 0;
|
|
27
|
+
const name = fn?.name || tc.name || tc.tool_name || "";
|
|
28
|
+
const rawArgs = fn?.arguments ?? tc.args ?? tc.arguments;
|
|
29
|
+
const args = safeParseArgs(rawArgs);
|
|
30
|
+
const id = tc.id || tc.tool_call_id || tc.call_id;
|
|
31
|
+
return {
|
|
32
|
+
id,
|
|
33
|
+
name,
|
|
34
|
+
args,
|
|
35
|
+
status: "completed",
|
|
36
|
+
result: tc.result
|
|
37
|
+
};
|
|
38
|
+
}).filter((tc) => tc.name || tc.id);
|
|
39
|
+
}
|
|
40
|
+
function getNumericMessageId(message) {
|
|
41
|
+
if (typeof message.message_id === "number") {
|
|
42
|
+
return message.message_id;
|
|
43
|
+
}
|
|
44
|
+
if (typeof message.id === "string" && /^\d+$/.test(message.id)) {
|
|
45
|
+
return Number(message.id);
|
|
46
|
+
}
|
|
47
|
+
return void 0;
|
|
48
|
+
}
|
|
49
|
+
function mergeConsecutiveAssistantMessages(rawMessages) {
|
|
50
|
+
const result = [];
|
|
51
|
+
let i = 0;
|
|
52
|
+
while (i < rawMessages.length) {
|
|
53
|
+
const msg = rawMessages[i];
|
|
54
|
+
const numericMessageId = getNumericMessageId(msg);
|
|
55
|
+
const messageId = numericMessageId ? `history-${numericMessageId}` : msg.id || `history-${i}`;
|
|
56
|
+
if (msg.role === "assistant") {
|
|
57
|
+
const consecutiveAssistantMsgs = [msg];
|
|
58
|
+
const toolMessages = [];
|
|
59
|
+
let j = i + 1;
|
|
60
|
+
while (j < rawMessages.length) {
|
|
61
|
+
if (rawMessages[j].role === "assistant") {
|
|
62
|
+
consecutiveAssistantMsgs.push(rawMessages[j]);
|
|
63
|
+
j++;
|
|
64
|
+
} else if (rawMessages[j].role === "tool") {
|
|
65
|
+
toolMessages.push(rawMessages[j]);
|
|
66
|
+
j++;
|
|
67
|
+
} else {
|
|
68
|
+
break;
|
|
69
|
+
}
|
|
70
|
+
}
|
|
71
|
+
const blocks = [];
|
|
72
|
+
let blockTime = Date.now();
|
|
73
|
+
let fallbackToolCalls;
|
|
74
|
+
let lastEffectiveToolCalls;
|
|
75
|
+
for (let k = 0; k < consecutiveAssistantMsgs.length; k++) {
|
|
76
|
+
const assistantMsg = consecutiveAssistantMsgs[k];
|
|
77
|
+
const msgToolCalls = parseToolCalls(assistantMsg.toolCalls || assistantMsg.tool_calls);
|
|
78
|
+
const isLastAssistant = k === consecutiveAssistantMsgs.length - 1;
|
|
79
|
+
const fallbackCalls = isLastAssistant && toolMessages.length > 0 ? toolMessages.map((toolMessage, idx) => ({
|
|
80
|
+
id: (toolMessage.tool_call_id || void 0) ?? `tool-${idx}`,
|
|
81
|
+
name: "",
|
|
82
|
+
args: void 0,
|
|
83
|
+
status: "completed",
|
|
84
|
+
result: toolMessage.content
|
|
85
|
+
})) : void 0;
|
|
86
|
+
const effectiveToolCalls = msgToolCalls && msgToolCalls.length > 0 ? msgToolCalls : fallbackCalls;
|
|
87
|
+
if (!msgToolCalls || msgToolCalls.length === 0) {
|
|
88
|
+
if (effectiveToolCalls && effectiveToolCalls.length > 0) {
|
|
89
|
+
fallbackToolCalls = effectiveToolCalls;
|
|
90
|
+
}
|
|
91
|
+
}
|
|
92
|
+
lastEffectiveToolCalls = effectiveToolCalls;
|
|
93
|
+
const hasToolCalls = effectiveToolCalls && effectiveToolCalls.length > 0;
|
|
94
|
+
if (assistantMsg.thinking && assistantMsg.thinking.trim()) {
|
|
95
|
+
blocks.push({
|
|
96
|
+
type: "thinking",
|
|
97
|
+
content: assistantMsg.thinking,
|
|
98
|
+
timestamp: blockTime++,
|
|
99
|
+
isIntermediate: true
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
const shouldDeferText = Boolean(hasToolCalls && isLastAssistant && toolMessages.length === 0);
|
|
103
|
+
const hasContent = Boolean(assistantMsg.content && assistantMsg.content.trim());
|
|
104
|
+
if (hasContent && !shouldDeferText) {
|
|
105
|
+
blocks.push({
|
|
106
|
+
type: "text",
|
|
107
|
+
content: assistantMsg.content,
|
|
108
|
+
timestamp: blockTime++,
|
|
109
|
+
isIntermediate: hasToolCalls || !isLastAssistant
|
|
110
|
+
});
|
|
111
|
+
}
|
|
112
|
+
if (effectiveToolCalls) {
|
|
113
|
+
const toolResultBlocks = [];
|
|
114
|
+
for (const tc of effectiveToolCalls) {
|
|
115
|
+
const toolIndex = effectiveToolCalls.indexOf(tc);
|
|
116
|
+
blocks.push({
|
|
117
|
+
type: "tool_call",
|
|
118
|
+
content: "",
|
|
119
|
+
timestamp: blockTime++,
|
|
120
|
+
toolName: tc.name,
|
|
121
|
+
toolArgs: tc.args,
|
|
122
|
+
toolCallId: tc.id,
|
|
123
|
+
toolStatus: "completed",
|
|
124
|
+
isIntermediate: true
|
|
125
|
+
});
|
|
126
|
+
const toolResultById = tc.id ? toolMessages.find((tm) => tm.tool_call_id === tc.id) : void 0;
|
|
127
|
+
const toolResultByIndex = toolMessages[toolIndex];
|
|
128
|
+
const toolResult = toolResultById || toolResultByIndex;
|
|
129
|
+
const resultContent = tc.result ?? toolResult?.content;
|
|
130
|
+
const toolResultId = toolResult?.tool_call_id || void 0;
|
|
131
|
+
if (resultContent) {
|
|
132
|
+
toolResultBlocks.push({
|
|
133
|
+
type: "tool_result",
|
|
134
|
+
content: resultContent,
|
|
135
|
+
timestamp: blockTime++,
|
|
136
|
+
toolName: tc.name,
|
|
137
|
+
toolCallId: tc.id || toolResultId,
|
|
138
|
+
isIntermediate: true
|
|
139
|
+
});
|
|
140
|
+
tc.result = resultContent;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
blocks.push(...toolResultBlocks);
|
|
144
|
+
}
|
|
145
|
+
if (hasContent && shouldDeferText) {
|
|
146
|
+
blocks.push({
|
|
147
|
+
type: "text",
|
|
148
|
+
content: assistantMsg.content,
|
|
149
|
+
timestamp: blockTime++,
|
|
150
|
+
isIntermediate: false
|
|
151
|
+
});
|
|
152
|
+
}
|
|
153
|
+
}
|
|
154
|
+
if (consecutiveAssistantMsgs.length > 1) {
|
|
155
|
+
let contentMessages = consecutiveAssistantMsgs.filter(
|
|
156
|
+
(m) => !m.toolCalls && !m.tool_calls
|
|
157
|
+
);
|
|
158
|
+
if (contentMessages.length === 0) {
|
|
159
|
+
contentMessages = [consecutiveAssistantMsgs[consecutiveAssistantMsgs.length - 1]];
|
|
160
|
+
}
|
|
161
|
+
const mergedContent = contentMessages.map((m) => m.content).filter((c) => c && c.trim()).join("\n\n");
|
|
162
|
+
const mergedToolCalls = consecutiveAssistantMsgs.flatMap((m) => parseToolCalls(m.toolCalls || m.tool_calls) || []);
|
|
163
|
+
const effectiveMergedToolCalls = mergedToolCalls.length > 0 ? mergedToolCalls : fallbackToolCalls || [];
|
|
164
|
+
for (const toolMsg of toolMessages) {
|
|
165
|
+
const toolCallId = toolMsg.tool_call_id;
|
|
166
|
+
const matchingToolCall = effectiveMergedToolCalls.find(
|
|
167
|
+
(tc) => toolCallId && tc.id === toolCallId || !tc.result
|
|
168
|
+
);
|
|
169
|
+
if (matchingToolCall) {
|
|
170
|
+
matchingToolCall.result = toolMsg.content;
|
|
171
|
+
}
|
|
172
|
+
}
|
|
173
|
+
const thinkingParts = consecutiveAssistantMsgs.map((m) => m.thinking).filter((t) => t && t.trim());
|
|
174
|
+
const mergedThinking = thinkingParts.length > 1 ? thinkingParts.join("\n\u2500\u2500\u2500\u2500\u2500\n") : thinkingParts[0] || "";
|
|
175
|
+
const lastAssistantMsg = consecutiveAssistantMsgs[consecutiveAssistantMsgs.length - 1];
|
|
176
|
+
const lastToolMsg = toolMessages[toolMessages.length - 1];
|
|
177
|
+
const lastMsgId = Math.max(
|
|
178
|
+
getNumericMessageId(lastAssistantMsg) || 0,
|
|
179
|
+
lastToolMsg ? getNumericMessageId(lastToolMsg) || 0 : 0
|
|
180
|
+
);
|
|
181
|
+
const mergedMessageId = lastMsgId > 0 ? `history-${lastMsgId}` : messageId;
|
|
182
|
+
result.push({
|
|
183
|
+
id: mergedMessageId,
|
|
184
|
+
role: "assistant",
|
|
185
|
+
content: mergedContent,
|
|
186
|
+
timestamp: msg.timestamp || msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
187
|
+
toolCalls: effectiveMergedToolCalls.length > 0 ? effectiveMergedToolCalls : void 0,
|
|
188
|
+
thinking: mergedThinking || void 0,
|
|
189
|
+
filePaths: msg.filePaths,
|
|
190
|
+
blocks: blocks.length > 0 ? blocks : void 0,
|
|
191
|
+
isComplete: true
|
|
192
|
+
});
|
|
193
|
+
i = j;
|
|
194
|
+
} else {
|
|
195
|
+
result.push({
|
|
196
|
+
id: messageId,
|
|
197
|
+
role: "assistant",
|
|
198
|
+
content: msg.content || "",
|
|
199
|
+
timestamp: msg.timestamp || msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
200
|
+
toolCalls: lastEffectiveToolCalls,
|
|
201
|
+
thinking: msg.thinking || void 0,
|
|
202
|
+
filePaths: msg.filePaths,
|
|
203
|
+
blocks: blocks.length > 0 ? blocks : void 0,
|
|
204
|
+
isComplete: true
|
|
205
|
+
});
|
|
206
|
+
i++;
|
|
207
|
+
}
|
|
208
|
+
} else if (msg.role === "tool") {
|
|
209
|
+
i++;
|
|
210
|
+
} else {
|
|
211
|
+
result.push({
|
|
212
|
+
id: messageId,
|
|
213
|
+
role: msg.role,
|
|
214
|
+
content: msg.content || "",
|
|
215
|
+
timestamp: msg.timestamp || msg.created_at || (/* @__PURE__ */ new Date()).toISOString(),
|
|
216
|
+
toolCalls: parseToolCalls(msg.toolCalls || msg.tool_calls),
|
|
217
|
+
thinking: msg.thinking || void 0,
|
|
218
|
+
filePaths: msg.filePaths
|
|
219
|
+
});
|
|
220
|
+
i++;
|
|
221
|
+
}
|
|
222
|
+
}
|
|
223
|
+
return result;
|
|
224
|
+
}
|
|
225
|
+
|
|
1
226
|
// src/core/adapter.ts
|
|
227
|
+
function createChatAdapter(config) {
|
|
228
|
+
let sdkInstance = null;
|
|
229
|
+
let sdkPromise = null;
|
|
230
|
+
let connectionStatus = "disconnected";
|
|
231
|
+
const connectionListeners = /* @__PURE__ */ new Set();
|
|
232
|
+
let currentRunId = null;
|
|
233
|
+
let reconnectAcquired = false;
|
|
234
|
+
const updateStatus = (status, error, errorCode) => {
|
|
235
|
+
connectionStatus = status;
|
|
236
|
+
connectionListeners.forEach((cb) => cb(status, error, errorCode));
|
|
237
|
+
};
|
|
238
|
+
const ensureSdk = async () => {
|
|
239
|
+
if (sdkInstance) return sdkInstance;
|
|
240
|
+
if (sdkPromise) return sdkPromise;
|
|
241
|
+
sdkPromise = (async () => {
|
|
242
|
+
const { SanqianSDK: SDK } = await import("@yushaw/sanqian-sdk");
|
|
243
|
+
const tools = config.tools?.map((t) => ({
|
|
244
|
+
name: t.name,
|
|
245
|
+
description: t.description,
|
|
246
|
+
parameters: t.parameters,
|
|
247
|
+
handler: t.handler
|
|
248
|
+
})).filter((t) => !!t.handler) || [];
|
|
249
|
+
sdkInstance = new SDK({
|
|
250
|
+
appName: config.appName,
|
|
251
|
+
appVersion: config.appVersion,
|
|
252
|
+
tools
|
|
253
|
+
});
|
|
254
|
+
return sdkInstance;
|
|
255
|
+
})();
|
|
256
|
+
return sdkPromise;
|
|
257
|
+
};
|
|
258
|
+
return {
|
|
259
|
+
async connect() {
|
|
260
|
+
if (reconnectAcquired) {
|
|
261
|
+
return;
|
|
262
|
+
}
|
|
263
|
+
updateStatus("connecting");
|
|
264
|
+
try {
|
|
265
|
+
const sdk = await ensureSdk();
|
|
266
|
+
sdk.on("connected", () => updateStatus("connected"));
|
|
267
|
+
sdk.on("disconnected", () => {
|
|
268
|
+
if (reconnectAcquired && connectionStatus !== "disconnected") {
|
|
269
|
+
updateStatus("reconnecting");
|
|
270
|
+
}
|
|
271
|
+
});
|
|
272
|
+
sdk.on("error", (err) => updateStatus("error", err.message, "CONNECTION_FAILED"));
|
|
273
|
+
sdk.acquireReconnect();
|
|
274
|
+
reconnectAcquired = true;
|
|
275
|
+
await sdk.ensureReady();
|
|
276
|
+
updateStatus("connected");
|
|
277
|
+
} catch (e) {
|
|
278
|
+
updateStatus("error", e instanceof Error ? e.message : "Connection failed", "CONNECTION_FAILED");
|
|
279
|
+
throw e;
|
|
280
|
+
}
|
|
281
|
+
},
|
|
282
|
+
async disconnect() {
|
|
283
|
+
if (sdkInstance) {
|
|
284
|
+
if (reconnectAcquired) {
|
|
285
|
+
sdkInstance.releaseReconnect();
|
|
286
|
+
reconnectAcquired = false;
|
|
287
|
+
}
|
|
288
|
+
sdkInstance.removeAllListeners();
|
|
289
|
+
await sdkInstance.disconnect();
|
|
290
|
+
sdkInstance = null;
|
|
291
|
+
sdkPromise = null;
|
|
292
|
+
}
|
|
293
|
+
updateStatus("disconnected");
|
|
294
|
+
},
|
|
295
|
+
isConnected() {
|
|
296
|
+
return sdkInstance?.isConnected() ?? false;
|
|
297
|
+
},
|
|
298
|
+
getConnectionStatus() {
|
|
299
|
+
return connectionStatus;
|
|
300
|
+
},
|
|
301
|
+
onConnectionChange(callback) {
|
|
302
|
+
connectionListeners.add(callback);
|
|
303
|
+
callback(connectionStatus);
|
|
304
|
+
return () => connectionListeners.delete(callback);
|
|
305
|
+
},
|
|
306
|
+
async listConversations(options) {
|
|
307
|
+
const sdk = await ensureSdk();
|
|
308
|
+
await sdk.ensureReady();
|
|
309
|
+
const result = await sdk.listConversations({ agentId: config.agentId, ...options });
|
|
310
|
+
return {
|
|
311
|
+
conversations: result.conversations.map((c) => ({
|
|
312
|
+
id: c.conversation_id,
|
|
313
|
+
title: c.title || "Untitled",
|
|
314
|
+
createdAt: c.created_at || "",
|
|
315
|
+
updatedAt: c.updated_at || "",
|
|
316
|
+
messageCount: c.message_count,
|
|
317
|
+
agentId: c.agent_id || null
|
|
318
|
+
})),
|
|
319
|
+
total: result.total
|
|
320
|
+
};
|
|
321
|
+
},
|
|
322
|
+
async getConversation(id, options) {
|
|
323
|
+
const sdk = await ensureSdk();
|
|
324
|
+
await sdk.ensureReady();
|
|
325
|
+
const detail = await sdk.getConversation(id, { messageLimit: options?.messageLimit });
|
|
326
|
+
let rawMessages = detail.messages || [];
|
|
327
|
+
const sdkWithHistory = sdk;
|
|
328
|
+
if (typeof sdkWithHistory.getMessages === "function") {
|
|
329
|
+
try {
|
|
330
|
+
const history = await sdkWithHistory.getMessages(id, { limit: options?.messageLimit });
|
|
331
|
+
if (history?.messages && history.messages.length > 0) {
|
|
332
|
+
rawMessages = history.messages;
|
|
333
|
+
}
|
|
334
|
+
} catch (e) {
|
|
335
|
+
console.warn("[sanqian-chat] getMessages failed, fallback to getConversation:", e);
|
|
336
|
+
}
|
|
337
|
+
}
|
|
338
|
+
const normalizedMessages = mergeConsecutiveAssistantMessages(
|
|
339
|
+
rawMessages.map((m, i) => ({
|
|
340
|
+
id: m.id || `msg-${i}`,
|
|
341
|
+
role: m.role,
|
|
342
|
+
content: m.content,
|
|
343
|
+
created_at: m.created_at || void 0,
|
|
344
|
+
tool_calls: m.tool_calls || m.toolCalls,
|
|
345
|
+
tool_call_id: m.tool_call_id ?? null,
|
|
346
|
+
thinking: m.thinking || void 0,
|
|
347
|
+
message_id: m.message_id,
|
|
348
|
+
filePaths: m.file_paths || m.filePaths
|
|
349
|
+
}))
|
|
350
|
+
);
|
|
351
|
+
return {
|
|
352
|
+
id: detail.conversation_id,
|
|
353
|
+
title: detail.title || "Untitled",
|
|
354
|
+
createdAt: detail.created_at || "",
|
|
355
|
+
updatedAt: detail.updated_at || "",
|
|
356
|
+
messageCount: detail.message_count,
|
|
357
|
+
agentId: detail.agent_id || null,
|
|
358
|
+
messages: normalizedMessages
|
|
359
|
+
};
|
|
360
|
+
},
|
|
361
|
+
async deleteConversation(id) {
|
|
362
|
+
const sdk = await ensureSdk();
|
|
363
|
+
await sdk.ensureReady();
|
|
364
|
+
await sdk.deleteConversation(id);
|
|
365
|
+
},
|
|
366
|
+
async chatStream(messages, conversationId, onEvent, options) {
|
|
367
|
+
const sdk = await ensureSdk();
|
|
368
|
+
await sdk.ensureReady();
|
|
369
|
+
const agentId = options?.agentId ?? config.agentId;
|
|
370
|
+
const sdkMessages = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
371
|
+
const stream = sdk.chatStream(
|
|
372
|
+
agentId,
|
|
373
|
+
sdkMessages,
|
|
374
|
+
{
|
|
375
|
+
conversationId,
|
|
376
|
+
persistHistory: config.persistHistory ?? false
|
|
377
|
+
}
|
|
378
|
+
);
|
|
379
|
+
return processStreamEvents(stream, onEvent, sdk, (id) => {
|
|
380
|
+
currentRunId = id;
|
|
381
|
+
});
|
|
382
|
+
},
|
|
383
|
+
sendHitlResponse(response, runId) {
|
|
384
|
+
if (!sdkInstance) return;
|
|
385
|
+
const id = runId || currentRunId;
|
|
386
|
+
if (id) {
|
|
387
|
+
sdkInstance.sendHitlResponse(id, response);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
};
|
|
391
|
+
}
|
|
392
|
+
function processStreamEvents(stream, onEvent, sdk, setCurrentRunId) {
|
|
393
|
+
const controller = new AbortController();
|
|
394
|
+
const signal = controller.signal;
|
|
395
|
+
let currentRunId = null;
|
|
396
|
+
(async () => {
|
|
397
|
+
try {
|
|
398
|
+
for await (const event of stream) {
|
|
399
|
+
if (signal.aborted) break;
|
|
400
|
+
switch (event.type) {
|
|
401
|
+
case "start":
|
|
402
|
+
if (event.run_id) {
|
|
403
|
+
currentRunId = event.run_id;
|
|
404
|
+
setCurrentRunId(event.run_id);
|
|
405
|
+
onEvent({ type: "start", run_id: event.run_id, conversationId: event.conversationId });
|
|
406
|
+
}
|
|
407
|
+
break;
|
|
408
|
+
case "text":
|
|
409
|
+
onEvent({ type: "text", content: event.content || "" });
|
|
410
|
+
break;
|
|
411
|
+
case "thinking":
|
|
412
|
+
onEvent({ type: "thinking", content: event.content || "" });
|
|
413
|
+
break;
|
|
414
|
+
case "tool_call":
|
|
415
|
+
if (event.tool_call) {
|
|
416
|
+
onEvent({ type: "tool_call", tool_call: event.tool_call });
|
|
417
|
+
}
|
|
418
|
+
break;
|
|
419
|
+
case "tool_args_chunk":
|
|
420
|
+
onEvent({
|
|
421
|
+
type: "tool_args_chunk",
|
|
422
|
+
tool_call_id: event.tool_call_id || "",
|
|
423
|
+
tool_name: event.tool_name,
|
|
424
|
+
chunk: event.chunk || ""
|
|
425
|
+
});
|
|
426
|
+
break;
|
|
427
|
+
case "tool_args":
|
|
428
|
+
onEvent({
|
|
429
|
+
type: "tool_args",
|
|
430
|
+
tool_call_id: event.tool_call_id || "",
|
|
431
|
+
tool_name: event.tool_name,
|
|
432
|
+
args: event.args || {}
|
|
433
|
+
});
|
|
434
|
+
break;
|
|
435
|
+
case "tool_result":
|
|
436
|
+
onEvent({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
|
|
437
|
+
break;
|
|
438
|
+
case "done":
|
|
439
|
+
onEvent({ type: "done", conversationId: event.conversationId || "", title: event.title });
|
|
440
|
+
currentRunId = null;
|
|
441
|
+
setCurrentRunId(null);
|
|
442
|
+
break;
|
|
443
|
+
case "cancelled":
|
|
444
|
+
onEvent({ type: "cancelled", run_id: event.run_id });
|
|
445
|
+
currentRunId = null;
|
|
446
|
+
setCurrentRunId(null);
|
|
447
|
+
break;
|
|
448
|
+
case "error":
|
|
449
|
+
onEvent({ type: "error", error: event.error || "Unknown error" });
|
|
450
|
+
currentRunId = null;
|
|
451
|
+
setCurrentRunId(null);
|
|
452
|
+
break;
|
|
453
|
+
default: {
|
|
454
|
+
const evt = event;
|
|
455
|
+
if (evt.type === "interrupt") {
|
|
456
|
+
currentRunId = evt.run_id || null;
|
|
457
|
+
setCurrentRunId(evt.run_id || null);
|
|
458
|
+
onEvent({
|
|
459
|
+
type: "interrupt",
|
|
460
|
+
interrupt_type: evt.interrupt_type || "",
|
|
461
|
+
interrupt_payload: evt.interrupt_payload,
|
|
462
|
+
run_id: evt.run_id
|
|
463
|
+
});
|
|
464
|
+
}
|
|
465
|
+
break;
|
|
466
|
+
}
|
|
467
|
+
}
|
|
468
|
+
}
|
|
469
|
+
} catch (e) {
|
|
470
|
+
if (!signal.aborted) {
|
|
471
|
+
onEvent({ type: "error", error: e instanceof Error ? e.message : "Stream error" });
|
|
472
|
+
}
|
|
473
|
+
}
|
|
474
|
+
})();
|
|
475
|
+
return {
|
|
476
|
+
cancel: () => {
|
|
477
|
+
controller.abort();
|
|
478
|
+
const sdkWithCancel = sdk;
|
|
479
|
+
if (currentRunId && typeof sdkWithCancel.cancelRun === "function") {
|
|
480
|
+
try {
|
|
481
|
+
sdkWithCancel.cancelRun(currentRunId);
|
|
482
|
+
} catch (e) {
|
|
483
|
+
console.warn("[chat adapter] Failed to cancel run:", e);
|
|
484
|
+
}
|
|
485
|
+
}
|
|
486
|
+
}
|
|
487
|
+
};
|
|
488
|
+
}
|
|
2
489
|
function createSdkAdapter(config) {
|
|
3
490
|
let connectionStatus = "disconnected";
|
|
4
491
|
const connectionListeners = /* @__PURE__ */ new Set();
|
|
@@ -21,6 +508,10 @@ function createSdkAdapter(config) {
|
|
|
21
508
|
}
|
|
22
509
|
},
|
|
23
510
|
async disconnect() {
|
|
511
|
+
const sdk = config.getSdk();
|
|
512
|
+
if (sdk) {
|
|
513
|
+
await sdk.disconnect();
|
|
514
|
+
}
|
|
24
515
|
updateStatus("disconnected");
|
|
25
516
|
},
|
|
26
517
|
isConnected() {
|
|
@@ -46,7 +537,8 @@ function createSdkAdapter(config) {
|
|
|
46
537
|
title: c.title || "Untitled",
|
|
47
538
|
createdAt: c.created_at || "",
|
|
48
539
|
updatedAt: c.updated_at || "",
|
|
49
|
-
messageCount: c.message_count
|
|
540
|
+
messageCount: c.message_count,
|
|
541
|
+
agentId: c.agent_id || null
|
|
50
542
|
})),
|
|
51
543
|
total: result.total
|
|
52
544
|
};
|
|
@@ -55,18 +547,39 @@ function createSdkAdapter(config) {
|
|
|
55
547
|
const sdk = config.getSdk();
|
|
56
548
|
if (!sdk) throw new Error("SDK not ready");
|
|
57
549
|
const detail = await sdk.getConversation(id, { messageLimit: options?.messageLimit });
|
|
550
|
+
let rawMessages = detail.messages || [];
|
|
551
|
+
const sdkWithHistory = sdk;
|
|
552
|
+
if (typeof sdkWithHistory.getMessages === "function") {
|
|
553
|
+
try {
|
|
554
|
+
const history = await sdkWithHistory.getMessages(id, { limit: options?.messageLimit });
|
|
555
|
+
if (history?.messages && history.messages.length > 0) {
|
|
556
|
+
rawMessages = history.messages;
|
|
557
|
+
}
|
|
558
|
+
} catch (e) {
|
|
559
|
+
console.warn("[sanqian-chat] getMessages failed, fallback to getConversation:", e);
|
|
560
|
+
}
|
|
561
|
+
}
|
|
562
|
+
const normalizedMessages = mergeConsecutiveAssistantMessages(
|
|
563
|
+
rawMessages.map((m, i) => ({
|
|
564
|
+
id: m.id || `msg-${i}`,
|
|
565
|
+
role: m.role,
|
|
566
|
+
content: m.content,
|
|
567
|
+
created_at: m.created_at || void 0,
|
|
568
|
+
tool_calls: m.tool_calls || m.toolCalls,
|
|
569
|
+
tool_call_id: m.tool_call_id ?? null,
|
|
570
|
+
thinking: m.thinking || void 0,
|
|
571
|
+
message_id: m.message_id,
|
|
572
|
+
filePaths: m.file_paths || m.filePaths
|
|
573
|
+
}))
|
|
574
|
+
);
|
|
58
575
|
return {
|
|
59
576
|
id: detail.conversation_id,
|
|
60
577
|
title: detail.title || "Untitled",
|
|
61
578
|
createdAt: detail.created_at || "",
|
|
62
579
|
updatedAt: detail.updated_at || "",
|
|
63
580
|
messageCount: detail.message_count,
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
role: m.role,
|
|
67
|
-
content: m.content,
|
|
68
|
-
timestamp: m.created_at || (/* @__PURE__ */ new Date()).toISOString()
|
|
69
|
-
}))
|
|
581
|
+
agentId: detail.agent_id || null,
|
|
582
|
+
messages: normalizedMessages
|
|
70
583
|
};
|
|
71
584
|
},
|
|
72
585
|
async deleteConversation(id) {
|
|
@@ -74,65 +587,20 @@ function createSdkAdapter(config) {
|
|
|
74
587
|
if (!sdk) throw new Error("SDK not ready");
|
|
75
588
|
await sdk.deleteConversation(id);
|
|
76
589
|
},
|
|
77
|
-
async chatStream(messages, conversationId, onEvent) {
|
|
590
|
+
async chatStream(messages, conversationId, onEvent, options) {
|
|
78
591
|
const sdk = config.getSdk();
|
|
79
|
-
const agentId = config.getAgentId();
|
|
592
|
+
const agentId = options?.agentId ?? config.getAgentId();
|
|
80
593
|
if (!sdk || !agentId) throw new Error("SDK or agent not ready");
|
|
81
594
|
await sdk.ensureReady();
|
|
82
595
|
const sdkMessages = messages.map((m) => ({ role: m.role, content: m.content }));
|
|
83
|
-
const stream = sdk.chatStream(
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
case "text":
|
|
92
|
-
onEvent({ type: "text", content: event.content || "" });
|
|
93
|
-
break;
|
|
94
|
-
case "thinking":
|
|
95
|
-
onEvent({ type: "thinking", content: event.content || "" });
|
|
96
|
-
break;
|
|
97
|
-
case "tool_call":
|
|
98
|
-
if (event.tool_call) {
|
|
99
|
-
onEvent({ type: "tool_call", tool_call: event.tool_call });
|
|
100
|
-
}
|
|
101
|
-
break;
|
|
102
|
-
case "tool_result":
|
|
103
|
-
onEvent({ type: "tool_result", tool_call_id: event.tool_call_id || "", result: event.result });
|
|
104
|
-
break;
|
|
105
|
-
case "done":
|
|
106
|
-
onEvent({ type: "done", conversationId: event.conversationId || "", title: event.title });
|
|
107
|
-
break;
|
|
108
|
-
case "error":
|
|
109
|
-
onEvent({ type: "error", error: event.error || "Unknown error" });
|
|
110
|
-
break;
|
|
111
|
-
default:
|
|
112
|
-
const evt = event;
|
|
113
|
-
if (evt.type === "interrupt") {
|
|
114
|
-
currentRunId = evt.run_id || null;
|
|
115
|
-
onEvent({
|
|
116
|
-
type: "interrupt",
|
|
117
|
-
interrupt_type: evt.interrupt_type || "",
|
|
118
|
-
interrupt_payload: evt.interrupt_payload,
|
|
119
|
-
run_id: evt.run_id
|
|
120
|
-
});
|
|
121
|
-
}
|
|
122
|
-
break;
|
|
123
|
-
}
|
|
124
|
-
}
|
|
125
|
-
} catch (e) {
|
|
126
|
-
if (!signal.aborted) {
|
|
127
|
-
onEvent({ type: "error", error: e instanceof Error ? e.message : "Stream error" });
|
|
128
|
-
}
|
|
129
|
-
}
|
|
130
|
-
})();
|
|
131
|
-
return {
|
|
132
|
-
cancel: () => {
|
|
133
|
-
controller.abort();
|
|
134
|
-
}
|
|
135
|
-
};
|
|
596
|
+
const stream = sdk.chatStream(
|
|
597
|
+
agentId,
|
|
598
|
+
sdkMessages,
|
|
599
|
+
{ conversationId }
|
|
600
|
+
);
|
|
601
|
+
return processStreamEvents(stream, onEvent, sdk, (id) => {
|
|
602
|
+
currentRunId = id;
|
|
603
|
+
});
|
|
136
604
|
},
|
|
137
605
|
sendHitlResponse(response, runId) {
|
|
138
606
|
const sdk = config.getSdk();
|
|
@@ -145,5 +613,8 @@ function createSdkAdapter(config) {
|
|
|
145
613
|
};
|
|
146
614
|
}
|
|
147
615
|
export {
|
|
148
|
-
|
|
616
|
+
createChatAdapter,
|
|
617
|
+
createSdkAdapter,
|
|
618
|
+
mergeConsecutiveAssistantMessages,
|
|
619
|
+
parseToolCalls
|
|
149
620
|
};
|