@huyooo/ai-chat-bridge-electron 0.2.13 → 0.2.14
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/main/index.d.ts +2 -0
- package/dist/main/index.js +20 -0
- package/dist/preload/index.d.ts +25 -7
- package/dist/preload/index.js +2 -0
- package/dist/renderer/index.d.cts +1 -1
- package/dist/renderer/index.d.ts +54 -15
- package/dist/renderer/index.js +10 -0
- package/package.json +4 -4
- package/src/main/index.ts +33 -0
- package/src/preload/index.ts +25 -7
- package/src/renderer/index.ts +39 -7
package/dist/main/index.d.ts
CHANGED
|
@@ -175,6 +175,8 @@ interface FileInfo {
|
|
|
175
175
|
interface ElectronBridgeOptions extends Omit<AgentConfig, 'tools'> {
|
|
176
176
|
/** IPC channel 前缀 */
|
|
177
177
|
channelPrefix?: string;
|
|
178
|
+
/** Tavily API Key(用于统一 Web Search;可选) */
|
|
179
|
+
tavilyApiKey?: string;
|
|
178
180
|
/**
|
|
179
181
|
* 统一数据目录(必填)
|
|
180
182
|
* 所有数据都存储在此目录下:
|
package/dist/main/index.js
CHANGED
|
@@ -559,6 +559,21 @@ async function createElectronBridge(options) {
|
|
|
559
559
|
ipcMain2.handle(`${channelPrefix}:models`, () => {
|
|
560
560
|
return MODELS;
|
|
561
561
|
});
|
|
562
|
+
ipcMain2.handle(`${channelPrefix}:getAllTools`, async () => {
|
|
563
|
+
console.log("[Main] getAllTools \u8C03\u7528\uFF0C\u5F53\u524D\u5DE5\u5177\u6570\u91CF:", agent["tools"].size);
|
|
564
|
+
console.log("[Main] toolConfig \u662F\u5426\u5B58\u5728:", !!agent["toolConfig"]);
|
|
565
|
+
console.log("[Main] toolConfig \u5185\u5BB9:", agent["toolConfig"] ? `${agent["toolConfig"].length} \u4E2A\u5DE5\u5177\u914D\u7F6E` : "undefined");
|
|
566
|
+
if (agent["tools"].size === 0 && agent["toolConfig"]) {
|
|
567
|
+
console.log("[Main] \u5DE5\u5177\u672A\u521D\u59CB\u5316\uFF0C\u5F00\u59CB asyncInit...");
|
|
568
|
+
await agent["asyncInit"]();
|
|
569
|
+
console.log("[Main] asyncInit \u5B8C\u6210\uFF0C\u5DE5\u5177\u6570\u91CF:", agent["tools"].size);
|
|
570
|
+
} else if (!agent["toolConfig"]) {
|
|
571
|
+
console.warn("[Main] \u26A0\uFE0F toolConfig \u4E0D\u5B58\u5728\uFF0C\u5DE5\u5177\u53EF\u80FD\u672A\u6CE8\u5165\uFF01");
|
|
572
|
+
}
|
|
573
|
+
const tools = agent.getAllTools();
|
|
574
|
+
console.log("[Main] getAllTools \u8FD4\u56DE:", tools.length, "\u4E2A\u5DE5\u5177", tools.map((t) => t.name));
|
|
575
|
+
return tools;
|
|
576
|
+
});
|
|
562
577
|
ipcMain2.handle(`${channelPrefix}:send`, async (event, params) => {
|
|
563
578
|
const webContents = event.sender;
|
|
564
579
|
const { message, images, options: options2 = {}, sessionId } = params;
|
|
@@ -671,6 +686,7 @@ async function createElectronBridge(options) {
|
|
|
671
686
|
sessionId: params.sessionId,
|
|
672
687
|
role: params.role,
|
|
673
688
|
content: params.content,
|
|
689
|
+
images: params.images || [],
|
|
674
690
|
model: params.model || null,
|
|
675
691
|
mode: params.mode || null,
|
|
676
692
|
webSearchEnabled: params.webSearchEnabled ?? null,
|
|
@@ -691,6 +707,10 @@ async function createElectronBridge(options) {
|
|
|
691
707
|
await storage.deleteMessagesAfter(sessionId, new Date(timestamp), getContext());
|
|
692
708
|
return { success: true };
|
|
693
709
|
});
|
|
710
|
+
ipcMain2.handle(`${channelPrefix}:messages:deleteAfterMessageId`, async (_event, sessionId, messageId) => {
|
|
711
|
+
await storage.deleteMessagesAfterMessageId(sessionId, messageId, getContext());
|
|
712
|
+
return { success: true };
|
|
713
|
+
});
|
|
694
714
|
ipcMain2.handle(`${channelPrefix}:operations:list`, async (_event, sessionId) => {
|
|
695
715
|
return storage.getOperations(sessionId, getContext());
|
|
696
716
|
});
|
package/dist/preload/index.d.ts
CHANGED
|
@@ -21,12 +21,14 @@ interface SendMessageParams {
|
|
|
21
21
|
interface ModelOption {
|
|
22
22
|
modelId: string;
|
|
23
23
|
displayName: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
supportsThinking: boolean;
|
|
25
|
+
supportsVision: boolean;
|
|
26
|
+
tooltip?: {
|
|
27
|
+
features?: string[];
|
|
28
|
+
/** 开销信息(数组,分行显示) */
|
|
29
|
+
cost?: string[];
|
|
30
|
+
description?: string;
|
|
31
|
+
};
|
|
30
32
|
}
|
|
31
33
|
/** 会话记录 */
|
|
32
34
|
interface SessionRecord {
|
|
@@ -49,6 +51,8 @@ interface MessageRecord {
|
|
|
49
51
|
sessionId: string;
|
|
50
52
|
role: 'user' | 'assistant';
|
|
51
53
|
content: string;
|
|
54
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
55
|
+
images: string[];
|
|
52
56
|
/** 生成此消息时使用的模型 */
|
|
53
57
|
model?: string | null;
|
|
54
58
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -60,7 +64,10 @@ interface MessageRecord {
|
|
|
60
64
|
/** 执行步骤列表 JSON */
|
|
61
65
|
steps?: string | null;
|
|
62
66
|
operationIds?: string | null;
|
|
63
|
-
|
|
67
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
68
|
+
sequence: number;
|
|
69
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
70
|
+
timestamp: number;
|
|
64
71
|
}
|
|
65
72
|
/** 操作记录 */
|
|
66
73
|
interface OperationRecord {
|
|
@@ -124,6 +131,11 @@ interface AsrResultData {
|
|
|
124
131
|
interface AiChatBridge {
|
|
125
132
|
/** 获取可用模型 */
|
|
126
133
|
getModels(): Promise<ModelOption[]>;
|
|
134
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
135
|
+
getAllTools(): Promise<Array<{
|
|
136
|
+
name: string;
|
|
137
|
+
description: string;
|
|
138
|
+
}>>;
|
|
127
139
|
/** 发送消息 */
|
|
128
140
|
send(params: SendMessageParams): Promise<void>;
|
|
129
141
|
/** 取消当前请求 */
|
|
@@ -169,6 +181,8 @@ interface AiChatBridge {
|
|
|
169
181
|
sessionId: string;
|
|
170
182
|
role: 'user' | 'assistant';
|
|
171
183
|
content: string;
|
|
184
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
185
|
+
images?: string[];
|
|
172
186
|
/** 生成此消息时使用的模型 */
|
|
173
187
|
model?: string;
|
|
174
188
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -193,6 +207,10 @@ interface AiChatBridge {
|
|
|
193
207
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{
|
|
194
208
|
success: boolean;
|
|
195
209
|
}>;
|
|
210
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
211
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{
|
|
212
|
+
success: boolean;
|
|
213
|
+
}>;
|
|
196
214
|
/** 获取操作日志 */
|
|
197
215
|
getOperations(sessionId: string): Promise<OperationRecord[]>;
|
|
198
216
|
/** 获取回收站 */
|
package/dist/preload/index.js
CHANGED
|
@@ -5,6 +5,7 @@ function exposeElectronBridge(options = {}) {
|
|
|
5
5
|
const bridge = {
|
|
6
6
|
// ============ Chat API ============
|
|
7
7
|
getModels: () => ipcRenderer.invoke(`${channelPrefix}:models`),
|
|
8
|
+
getAllTools: () => ipcRenderer.invoke(`${channelPrefix}:getAllTools`),
|
|
8
9
|
send: (params) => ipcRenderer.invoke(`${channelPrefix}:send`, params),
|
|
9
10
|
cancel: () => ipcRenderer.invoke(`${channelPrefix}:cancel`),
|
|
10
11
|
// 无状态架构:历史通过 ChatOptions.history 传入
|
|
@@ -40,6 +41,7 @@ function exposeElectronBridge(options = {}) {
|
|
|
40
41
|
saveMessage: (params) => ipcRenderer.invoke(`${channelPrefix}:messages:save`, params),
|
|
41
42
|
updateMessage: (params) => ipcRenderer.invoke(`${channelPrefix}:messages:update`, params),
|
|
42
43
|
deleteMessagesAfter: (sessionId, timestamp) => ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfter`, sessionId, timestamp),
|
|
44
|
+
deleteMessagesAfterMessageId: (sessionId, messageId) => ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfterMessageId`, sessionId, messageId),
|
|
43
45
|
// ============ Operations API ============
|
|
44
46
|
getOperations: (sessionId) => ipcRenderer.invoke(`${channelPrefix}:operations:list`, sessionId),
|
|
45
47
|
// ============ Trash API ============
|
|
@@ -269,7 +269,7 @@ interface AiChatBridge {
|
|
|
269
269
|
* 在渲染进程中使用,创建 ChatAdapter
|
|
270
270
|
*/
|
|
271
271
|
|
|
272
|
-
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
272
|
+
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_output' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
273
273
|
interface ChatEvent {
|
|
274
274
|
type: ChatEventType;
|
|
275
275
|
data: unknown;
|
package/dist/renderer/index.d.ts
CHANGED
|
@@ -21,12 +21,14 @@ interface SendMessageParams {
|
|
|
21
21
|
interface ModelOption$1 {
|
|
22
22
|
modelId: string;
|
|
23
23
|
displayName: string;
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
24
|
+
supportsThinking: boolean;
|
|
25
|
+
supportsVision: boolean;
|
|
26
|
+
tooltip?: {
|
|
27
|
+
features?: string[];
|
|
28
|
+
/** 开销信息(数组,分行显示) */
|
|
29
|
+
cost?: string[];
|
|
30
|
+
description?: string;
|
|
31
|
+
};
|
|
30
32
|
}
|
|
31
33
|
/** 会话记录 */
|
|
32
34
|
interface SessionRecord$1 {
|
|
@@ -49,6 +51,8 @@ interface MessageRecord$1 {
|
|
|
49
51
|
sessionId: string;
|
|
50
52
|
role: 'user' | 'assistant';
|
|
51
53
|
content: string;
|
|
54
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
55
|
+
images: string[];
|
|
52
56
|
/** 生成此消息时使用的模型 */
|
|
53
57
|
model?: string | null;
|
|
54
58
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -60,7 +64,10 @@ interface MessageRecord$1 {
|
|
|
60
64
|
/** 执行步骤列表 JSON */
|
|
61
65
|
steps?: string | null;
|
|
62
66
|
operationIds?: string | null;
|
|
63
|
-
|
|
67
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
68
|
+
sequence: number;
|
|
69
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
70
|
+
timestamp: number;
|
|
64
71
|
}
|
|
65
72
|
/** 操作记录 */
|
|
66
73
|
interface OperationRecord$1 {
|
|
@@ -124,6 +131,11 @@ interface AsrResultData$1 {
|
|
|
124
131
|
interface AiChatBridge {
|
|
125
132
|
/** 获取可用模型 */
|
|
126
133
|
getModels(): Promise<ModelOption$1[]>;
|
|
134
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
135
|
+
getAllTools(): Promise<Array<{
|
|
136
|
+
name: string;
|
|
137
|
+
description: string;
|
|
138
|
+
}>>;
|
|
127
139
|
/** 发送消息 */
|
|
128
140
|
send(params: SendMessageParams): Promise<void>;
|
|
129
141
|
/** 取消当前请求 */
|
|
@@ -169,6 +181,8 @@ interface AiChatBridge {
|
|
|
169
181
|
sessionId: string;
|
|
170
182
|
role: 'user' | 'assistant';
|
|
171
183
|
content: string;
|
|
184
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
185
|
+
images?: string[];
|
|
172
186
|
/** 生成此消息时使用的模型 */
|
|
173
187
|
model?: string;
|
|
174
188
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -193,6 +207,10 @@ interface AiChatBridge {
|
|
|
193
207
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{
|
|
194
208
|
success: boolean;
|
|
195
209
|
}>;
|
|
210
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
211
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{
|
|
212
|
+
success: boolean;
|
|
213
|
+
}>;
|
|
196
214
|
/** 获取操作日志 */
|
|
197
215
|
getOperations(sessionId: string): Promise<OperationRecord$1[]>;
|
|
198
216
|
/** 获取回收站 */
|
|
@@ -340,7 +358,7 @@ interface AiChatBridge {
|
|
|
340
358
|
* 在渲染进程中使用,创建 ChatAdapter
|
|
341
359
|
*/
|
|
342
360
|
|
|
343
|
-
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
361
|
+
type ChatEventType = 'thinking_start' | 'thinking_delta' | 'thinking_end' | 'search_start' | 'search_result' | 'search_end' | 'tool_approval_request' | 'tool_call_start' | 'tool_call_output' | 'tool_call_result' | 'text_delta' | 'image' | 'video' | 'stream_start' | 'done' | 'error' | 'abort' | 'step_start' | 'step_end';
|
|
344
362
|
interface ChatEvent {
|
|
345
363
|
type: ChatEventType;
|
|
346
364
|
data: unknown;
|
|
@@ -369,6 +387,8 @@ interface ChatOptions {
|
|
|
369
387
|
enableWebSearch?: boolean;
|
|
370
388
|
/** 深度思考开关(每个 provider 内部使用最优参数) */
|
|
371
389
|
thinkingMode?: ThinkingMode;
|
|
390
|
+
/** 启用的工具名称列表(undefined 表示全部启用) */
|
|
391
|
+
enabledTools?: string[];
|
|
372
392
|
/** 自动运行配置 */
|
|
373
393
|
autoRunConfig?: AutoRunConfig;
|
|
374
394
|
/** 对话历史(无状态架构,历史从前端传入) */
|
|
@@ -382,12 +402,17 @@ interface ModelOption {
|
|
|
382
402
|
modelId: string;
|
|
383
403
|
/** 显示名称 */
|
|
384
404
|
displayName: string;
|
|
385
|
-
/**
|
|
386
|
-
|
|
387
|
-
/**
|
|
388
|
-
|
|
389
|
-
/**
|
|
390
|
-
|
|
405
|
+
/** 是否支持深度思考 */
|
|
406
|
+
supportsThinking: boolean;
|
|
407
|
+
/** 是否支持图片理解 */
|
|
408
|
+
supportsVision: boolean;
|
|
409
|
+
/** 模型项 hover 提示(由后端定义,前端只渲染) */
|
|
410
|
+
tooltip?: {
|
|
411
|
+
features?: string[];
|
|
412
|
+
/** 开销信息(数组,分行显示) */
|
|
413
|
+
cost?: string[];
|
|
414
|
+
description?: string;
|
|
415
|
+
};
|
|
391
416
|
}
|
|
392
417
|
interface SessionRecord {
|
|
393
418
|
id: string;
|
|
@@ -408,6 +433,8 @@ interface MessageRecord {
|
|
|
408
433
|
sessionId: string;
|
|
409
434
|
role: 'user' | 'assistant';
|
|
410
435
|
content: string;
|
|
436
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
437
|
+
images: string[];
|
|
411
438
|
/** 生成此消息时使用的模型 */
|
|
412
439
|
model?: string | null;
|
|
413
440
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -419,7 +446,10 @@ interface MessageRecord {
|
|
|
419
446
|
/** 执行步骤列表 JSON */
|
|
420
447
|
steps?: string | null;
|
|
421
448
|
operationIds?: string | null;
|
|
422
|
-
|
|
449
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
450
|
+
sequence: number;
|
|
451
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
452
|
+
timestamp: number;
|
|
423
453
|
}
|
|
424
454
|
interface OperationRecord {
|
|
425
455
|
id: string;
|
|
@@ -478,6 +508,11 @@ interface AsrResultData {
|
|
|
478
508
|
interface ChatAdapter {
|
|
479
509
|
/** 获取可用模型 */
|
|
480
510
|
getModels(): Promise<ModelOption[]>;
|
|
511
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
512
|
+
getAllTools?(): Promise<Array<{
|
|
513
|
+
name: string;
|
|
514
|
+
description: string;
|
|
515
|
+
}>>;
|
|
481
516
|
/** 发送消息,返回异步迭代器 */
|
|
482
517
|
sendMessage(message: string, options?: ChatOptions, images?: string[], sessionId?: string): AsyncIterable<ChatEvent>;
|
|
483
518
|
/** 取消当前请求 */
|
|
@@ -517,6 +552,8 @@ interface ChatAdapter {
|
|
|
517
552
|
sessionId: string;
|
|
518
553
|
role: 'user' | 'assistant';
|
|
519
554
|
content: string;
|
|
555
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
556
|
+
images?: string[];
|
|
520
557
|
/** 生成此消息时使用的模型 */
|
|
521
558
|
model?: string;
|
|
522
559
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -537,6 +574,8 @@ interface ChatAdapter {
|
|
|
537
574
|
}): Promise<void>;
|
|
538
575
|
/** 删除指定时间之后的消息(用于分叉) */
|
|
539
576
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<void>;
|
|
577
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
578
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<void>;
|
|
540
579
|
/** 获取操作日志 */
|
|
541
580
|
getOperations(sessionId: string): Promise<OperationRecord[]>;
|
|
542
581
|
/** 获取回收站 */
|
package/dist/renderer/index.js
CHANGED
|
@@ -14,6 +14,13 @@ function createElectronAdapter(options = {}) {
|
|
|
14
14
|
const bridge = getBridge();
|
|
15
15
|
return bridge.getModels();
|
|
16
16
|
},
|
|
17
|
+
async getAllTools() {
|
|
18
|
+
const bridge = getBridge();
|
|
19
|
+
if (typeof bridge.getAllTools === "function") {
|
|
20
|
+
return bridge.getAllTools();
|
|
21
|
+
}
|
|
22
|
+
return [];
|
|
23
|
+
},
|
|
17
24
|
async *sendMessage(message, options2, images, sessionId) {
|
|
18
25
|
const bridge = getBridge();
|
|
19
26
|
const queue = [];
|
|
@@ -84,6 +91,9 @@ function createElectronAdapter(options = {}) {
|
|
|
84
91
|
async deleteMessagesAfter(sessionId, timestamp) {
|
|
85
92
|
await getBridge().deleteMessagesAfter(sessionId, timestamp);
|
|
86
93
|
},
|
|
94
|
+
async deleteMessagesAfterMessageId(sessionId, messageId) {
|
|
95
|
+
await getBridge().deleteMessagesAfterMessageId(sessionId, messageId);
|
|
96
|
+
},
|
|
87
97
|
// ============ Operations API ============
|
|
88
98
|
async getOperations(sessionId) {
|
|
89
99
|
return getBridge().getOperations(sessionId);
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@huyooo/ai-chat-bridge-electron",
|
|
3
|
-
"version": "0.2.
|
|
3
|
+
"version": "0.2.14",
|
|
4
4
|
"description": "AI Chat Electron Bridge - IPC integration for Electron apps",
|
|
5
5
|
"type": "module",
|
|
6
6
|
"main": "./dist/main/index.js",
|
|
@@ -34,9 +34,9 @@
|
|
|
34
34
|
"clean": "rm -rf dist"
|
|
35
35
|
},
|
|
36
36
|
"dependencies": {
|
|
37
|
-
"@huyooo/ai-chat-core": "^0.2.
|
|
38
|
-
"@huyooo/ai-chat-storage": "^0.2.
|
|
39
|
-
"@huyooo/ai-search": "^0.2.
|
|
37
|
+
"@huyooo/ai-chat-core": "^0.2.14",
|
|
38
|
+
"@huyooo/ai-chat-storage": "^0.2.14",
|
|
39
|
+
"@huyooo/ai-search": "^0.2.14",
|
|
40
40
|
"uuid": "^11.1.0",
|
|
41
41
|
"ws": "^8.18.3"
|
|
42
42
|
},
|
package/src/main/index.ts
CHANGED
|
@@ -46,6 +46,8 @@ import {
|
|
|
46
46
|
export interface ElectronBridgeOptions extends Omit<AgentConfig, 'tools'> {
|
|
47
47
|
/** IPC channel 前缀 */
|
|
48
48
|
channelPrefix?: string;
|
|
49
|
+
/** Tavily API Key(用于统一 Web Search;可选) */
|
|
50
|
+
tavilyApiKey?: string;
|
|
49
51
|
/**
|
|
50
52
|
* 统一数据目录(必填)
|
|
51
53
|
* 所有数据都存储在此目录下:
|
|
@@ -88,6 +90,8 @@ interface MessageParams {
|
|
|
88
90
|
sessionId: string;
|
|
89
91
|
role: 'user' | 'assistant';
|
|
90
92
|
content: string;
|
|
93
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
94
|
+
images?: string[];
|
|
91
95
|
/** 生成此消息时使用的模型 */
|
|
92
96
|
model?: string;
|
|
93
97
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -225,6 +229,28 @@ export async function createElectronBridge(options: ElectronBridgeOptions) {
|
|
|
225
229
|
return MODELS;
|
|
226
230
|
});
|
|
227
231
|
|
|
232
|
+
// 获取所有工具列表(用于设置面板)
|
|
233
|
+
ipcMain.handle(`${channelPrefix}:getAllTools`, async () => {
|
|
234
|
+
// 确保工具已初始化(通过调用 chat 方法触发 asyncInit)
|
|
235
|
+
// 如果工具还未初始化,先触发一次初始化
|
|
236
|
+
console.log('[Main] getAllTools 调用,当前工具数量:', agent['tools'].size);
|
|
237
|
+
console.log('[Main] toolConfig 是否存在:', !!agent['toolConfig']);
|
|
238
|
+
console.log('[Main] toolConfig 内容:', agent['toolConfig'] ? `${agent['toolConfig'].length} 个工具配置` : 'undefined');
|
|
239
|
+
|
|
240
|
+
// 如果工具未初始化,尝试初始化
|
|
241
|
+
if (agent['tools'].size === 0 && agent['toolConfig']) {
|
|
242
|
+
console.log('[Main] 工具未初始化,开始 asyncInit...');
|
|
243
|
+
await agent['asyncInit']();
|
|
244
|
+
console.log('[Main] asyncInit 完成,工具数量:', agent['tools'].size);
|
|
245
|
+
} else if (!agent['toolConfig']) {
|
|
246
|
+
console.warn('[Main] ⚠️ toolConfig 不存在,工具可能未注入!');
|
|
247
|
+
}
|
|
248
|
+
|
|
249
|
+
const tools = (agent as any).getAllTools();
|
|
250
|
+
console.log('[Main] getAllTools 返回:', tools.length, '个工具', tools.map((t: any) => t.name));
|
|
251
|
+
return tools;
|
|
252
|
+
});
|
|
253
|
+
|
|
228
254
|
// 发送消息
|
|
229
255
|
ipcMain.handle(`${channelPrefix}:send`, async (event, params: SendMessageParams) => {
|
|
230
256
|
const webContents = event.sender;
|
|
@@ -387,6 +413,7 @@ export async function createElectronBridge(options: ElectronBridgeOptions) {
|
|
|
387
413
|
sessionId: params.sessionId,
|
|
388
414
|
role: params.role,
|
|
389
415
|
content: params.content,
|
|
416
|
+
images: params.images || [],
|
|
390
417
|
model: params.model || null,
|
|
391
418
|
mode: params.mode || null,
|
|
392
419
|
webSearchEnabled: params.webSearchEnabled ?? null,
|
|
@@ -412,6 +439,12 @@ export async function createElectronBridge(options: ElectronBridgeOptions) {
|
|
|
412
439
|
return { success: true };
|
|
413
440
|
});
|
|
414
441
|
|
|
442
|
+
// 删除指定消息之后的所有消息(用于分叉)
|
|
443
|
+
ipcMain.handle(`${channelPrefix}:messages:deleteAfterMessageId`, async (_event, sessionId: string, messageId: string) => {
|
|
444
|
+
await storage.deleteMessagesAfterMessageId(sessionId, messageId, getContext());
|
|
445
|
+
return { success: true };
|
|
446
|
+
});
|
|
447
|
+
|
|
415
448
|
// 获取操作日志
|
|
416
449
|
ipcMain.handle(`${channelPrefix}:operations:list`, async (_event, sessionId: string) => {
|
|
417
450
|
return storage.getOperations(sessionId, getContext());
|
package/src/preload/index.ts
CHANGED
|
@@ -25,12 +25,14 @@ interface SendMessageParams {
|
|
|
25
25
|
interface ModelOption {
|
|
26
26
|
modelId: string;
|
|
27
27
|
displayName: string;
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
28
|
+
supportsThinking: boolean;
|
|
29
|
+
supportsVision: boolean;
|
|
30
|
+
tooltip?: {
|
|
31
|
+
features?: string[];
|
|
32
|
+
/** 开销信息(数组,分行显示) */
|
|
33
|
+
cost?: string[];
|
|
34
|
+
description?: string;
|
|
35
|
+
};
|
|
34
36
|
}
|
|
35
37
|
|
|
36
38
|
/** 会话记录 */
|
|
@@ -55,6 +57,8 @@ interface MessageRecord {
|
|
|
55
57
|
sessionId: string;
|
|
56
58
|
role: 'user' | 'assistant';
|
|
57
59
|
content: string;
|
|
60
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
61
|
+
images: string[];
|
|
58
62
|
/** 生成此消息时使用的模型 */
|
|
59
63
|
model?: string | null;
|
|
60
64
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -66,7 +70,10 @@ interface MessageRecord {
|
|
|
66
70
|
/** 执行步骤列表 JSON */
|
|
67
71
|
steps?: string | null;
|
|
68
72
|
operationIds?: string | null;
|
|
69
|
-
|
|
73
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
74
|
+
sequence: number;
|
|
75
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
76
|
+
timestamp: number;
|
|
70
77
|
}
|
|
71
78
|
|
|
72
79
|
/** 操作记录 */
|
|
@@ -137,6 +144,8 @@ export interface AiChatBridge {
|
|
|
137
144
|
// ============ Chat API ============
|
|
138
145
|
/** 获取可用模型 */
|
|
139
146
|
getModels(): Promise<ModelOption[]>;
|
|
147
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
148
|
+
getAllTools(): Promise<Array<{ name: string; description: string }>>;
|
|
140
149
|
/** 发送消息 */
|
|
141
150
|
send(params: SendMessageParams): Promise<void>;
|
|
142
151
|
/** 取消当前请求 */
|
|
@@ -171,6 +180,8 @@ export interface AiChatBridge {
|
|
|
171
180
|
sessionId: string;
|
|
172
181
|
role: 'user' | 'assistant';
|
|
173
182
|
content: string;
|
|
183
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
184
|
+
images?: string[];
|
|
174
185
|
/** 生成此消息时使用的模型 */
|
|
175
186
|
model?: string;
|
|
176
187
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -191,6 +202,8 @@ export interface AiChatBridge {
|
|
|
191
202
|
}): Promise<{ success: boolean }>;
|
|
192
203
|
/** 删除指定时间之后的消息(用于分叉) */
|
|
193
204
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<{ success: boolean }>;
|
|
205
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
206
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<{ success: boolean }>;
|
|
194
207
|
|
|
195
208
|
// ============ Operations API ============
|
|
196
209
|
/** 获取操作日志 */
|
|
@@ -323,6 +336,9 @@ export function exposeElectronBridge(options: ExposeOptions = {}) {
|
|
|
323
336
|
getModels: () =>
|
|
324
337
|
ipcRenderer.invoke(`${channelPrefix}:models`),
|
|
325
338
|
|
|
339
|
+
getAllTools: () =>
|
|
340
|
+
ipcRenderer.invoke(`${channelPrefix}:getAllTools`),
|
|
341
|
+
|
|
326
342
|
send: (params: SendMessageParams) =>
|
|
327
343
|
ipcRenderer.invoke(`${channelPrefix}:send`, params),
|
|
328
344
|
|
|
@@ -390,6 +406,8 @@ export function exposeElectronBridge(options: ExposeOptions = {}) {
|
|
|
390
406
|
|
|
391
407
|
deleteMessagesAfter: (sessionId: string, timestamp: number) =>
|
|
392
408
|
ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfter`, sessionId, timestamp),
|
|
409
|
+
deleteMessagesAfterMessageId: (sessionId: string, messageId: string) =>
|
|
410
|
+
ipcRenderer.invoke(`${channelPrefix}:messages:deleteAfterMessageId`, sessionId, messageId),
|
|
393
411
|
|
|
394
412
|
// ============ Operations API ============
|
|
395
413
|
getOperations: (sessionId: string) =>
|
package/src/renderer/index.ts
CHANGED
|
@@ -16,6 +16,7 @@ export type ChatEventType =
|
|
|
16
16
|
| 'search_end'
|
|
17
17
|
| 'tool_approval_request'
|
|
18
18
|
| 'tool_call_start'
|
|
19
|
+
| 'tool_call_output'
|
|
19
20
|
| 'tool_call_result'
|
|
20
21
|
| 'text_delta'
|
|
21
22
|
| 'image'
|
|
@@ -69,6 +70,8 @@ export interface ChatOptions {
|
|
|
69
70
|
enableWebSearch?: boolean;
|
|
70
71
|
/** 深度思考开关(每个 provider 内部使用最优参数) */
|
|
71
72
|
thinkingMode?: ThinkingMode;
|
|
73
|
+
/** 启用的工具名称列表(undefined 表示全部启用) */
|
|
74
|
+
enabledTools?: string[];
|
|
72
75
|
/** 自动运行配置 */
|
|
73
76
|
autoRunConfig?: AutoRunConfig;
|
|
74
77
|
/** 对话历史(无状态架构,历史从前端传入) */
|
|
@@ -83,12 +86,17 @@ export interface ModelOption {
|
|
|
83
86
|
modelId: string;
|
|
84
87
|
/** 显示名称 */
|
|
85
88
|
displayName: string;
|
|
86
|
-
/**
|
|
87
|
-
|
|
88
|
-
/**
|
|
89
|
-
|
|
90
|
-
/**
|
|
91
|
-
|
|
89
|
+
/** 是否支持深度思考 */
|
|
90
|
+
supportsThinking: boolean;
|
|
91
|
+
/** 是否支持图片理解 */
|
|
92
|
+
supportsVision: boolean;
|
|
93
|
+
/** 模型项 hover 提示(由后端定义,前端只渲染) */
|
|
94
|
+
tooltip?: {
|
|
95
|
+
features?: string[];
|
|
96
|
+
/** 开销信息(数组,分行显示) */
|
|
97
|
+
cost?: string[];
|
|
98
|
+
description?: string;
|
|
99
|
+
};
|
|
92
100
|
}
|
|
93
101
|
|
|
94
102
|
// 会话记录
|
|
@@ -113,6 +121,8 @@ export interface MessageRecord {
|
|
|
113
121
|
sessionId: string;
|
|
114
122
|
role: 'user' | 'assistant';
|
|
115
123
|
content: string;
|
|
124
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
125
|
+
images: string[];
|
|
116
126
|
/** 生成此消息时使用的模型 */
|
|
117
127
|
model?: string | null;
|
|
118
128
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -124,7 +134,10 @@ export interface MessageRecord {
|
|
|
124
134
|
/** 执行步骤列表 JSON */
|
|
125
135
|
steps?: string | null;
|
|
126
136
|
operationIds?: string | null;
|
|
127
|
-
|
|
137
|
+
/** 消息序号(会话内递增,用于分叉删除,必须) */
|
|
138
|
+
sequence: number;
|
|
139
|
+
/** 时间戳(毫秒,Unix 时间戳,不受 JSON 序列化影响) */
|
|
140
|
+
timestamp: number;
|
|
128
141
|
}
|
|
129
142
|
|
|
130
143
|
// 操作记录
|
|
@@ -196,6 +209,8 @@ export interface ChatAdapter {
|
|
|
196
209
|
// ============ Chat API ============
|
|
197
210
|
/** 获取可用模型 */
|
|
198
211
|
getModels(): Promise<ModelOption[]>;
|
|
212
|
+
/** 获取所有工具列表(用于设置面板) */
|
|
213
|
+
getAllTools?(): Promise<Array<{ name: string; description: string }>>;
|
|
199
214
|
/** 发送消息,返回异步迭代器 */
|
|
200
215
|
sendMessage(message: string, options?: ChatOptions, images?: string[], sessionId?: string): AsyncIterable<ChatEvent>;
|
|
201
216
|
/** 取消当前请求 */
|
|
@@ -226,6 +241,8 @@ export interface ChatAdapter {
|
|
|
226
241
|
sessionId: string;
|
|
227
242
|
role: 'user' | 'assistant';
|
|
228
243
|
content: string;
|
|
244
|
+
/** 用户消息附带的图片(data URL 或 base64) */
|
|
245
|
+
images?: string[];
|
|
229
246
|
/** 生成此消息时使用的模型 */
|
|
230
247
|
model?: string;
|
|
231
248
|
/** 生成此消息时使用的模式 (ask/agent) */
|
|
@@ -246,6 +263,8 @@ export interface ChatAdapter {
|
|
|
246
263
|
}): Promise<void>;
|
|
247
264
|
/** 删除指定时间之后的消息(用于分叉) */
|
|
248
265
|
deleteMessagesAfter(sessionId: string, timestamp: number): Promise<void>;
|
|
266
|
+
/** 删除指定消息之后的所有消息(用于分叉) */
|
|
267
|
+
deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<void>;
|
|
249
268
|
|
|
250
269
|
// ============ Operations API ============
|
|
251
270
|
/** 获取操作日志 */
|
|
@@ -353,6 +372,15 @@ export function createElectronAdapter(options: CreateAdapterOptions = {}): ChatA
|
|
|
353
372
|
return bridge.getModels();
|
|
354
373
|
},
|
|
355
374
|
|
|
375
|
+
async getAllTools(): Promise<Array<{ name: string; description: string }>> {
|
|
376
|
+
const bridge = getBridge();
|
|
377
|
+
// preload 已保证存在;这里保留一层容错,避免宿主未升级时直接崩
|
|
378
|
+
if (typeof (bridge as unknown as { getAllTools?: unknown }).getAllTools === 'function') {
|
|
379
|
+
return bridge.getAllTools();
|
|
380
|
+
}
|
|
381
|
+
return [];
|
|
382
|
+
},
|
|
383
|
+
|
|
356
384
|
async *sendMessage(
|
|
357
385
|
message: string,
|
|
358
386
|
options?: ChatOptions,
|
|
@@ -451,6 +479,10 @@ export function createElectronAdapter(options: CreateAdapterOptions = {}): ChatA
|
|
|
451
479
|
await getBridge().deleteMessagesAfter(sessionId, timestamp);
|
|
452
480
|
},
|
|
453
481
|
|
|
482
|
+
async deleteMessagesAfterMessageId(sessionId: string, messageId: string): Promise<void> {
|
|
483
|
+
await getBridge().deleteMessagesAfterMessageId(sessionId, messageId);
|
|
484
|
+
},
|
|
485
|
+
|
|
454
486
|
// ============ Operations API ============
|
|
455
487
|
async getOperations(sessionId: string): Promise<OperationRecord[]> {
|
|
456
488
|
return getBridge().getOperations(sessionId);
|