@myassis/gateway 1.0.29 → 1.0.31
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/routes/agent.js +8 -2
- package/dist/services/agent/AgentManager.js +2 -0
- package/dist/services/llm/LLMClient.js +166 -259
- package/dist/services/session/Session.js +7 -1
- package/dist/services/session/SessionManager.js +7 -1
- package/dist/services/session/SessionStore.js +16 -6
- package/dist/services/tools/screenshot.js +1 -1
- package/migrations/013_add_message_queue_auto_execute_to_sessions.sql +1 -0
- package/migrations/014_add_message_queue.sql +1 -0
- package/package.json +1 -1
package/dist/routes/agent.js
CHANGED
|
@@ -261,8 +261,11 @@ router.post('/:id/sessions', ensureAgentManager, async (req, res) => {
|
|
|
261
261
|
success: true,
|
|
262
262
|
data: {
|
|
263
263
|
id: session.id,
|
|
264
|
+
agentId: session.agentId,
|
|
264
265
|
title: session.title,
|
|
265
266
|
selectModelId: session.selectModelId,
|
|
267
|
+
messageQueue: session.messageQueue,
|
|
268
|
+
messageQueueAutoExecute: session.messageQueueAutoExecute,
|
|
266
269
|
createdAt: session.createdAt,
|
|
267
270
|
updatedAt: session.updatedAt,
|
|
268
271
|
}
|
|
@@ -285,14 +288,17 @@ router.put('/:id/sessions/:sessionId', ensureAgentManager, async (req, res) => {
|
|
|
285
288
|
if (!agent) {
|
|
286
289
|
return res.status(404).json({ success: false, error: 'Agent not found' });
|
|
287
290
|
}
|
|
288
|
-
const { title, selectModelId, unreadCount } = req.body;
|
|
289
|
-
const session = agent.updateSession(req.params.sessionId, { title, selectModelId, unreadCount });
|
|
291
|
+
const { title, selectModelId, unreadCount, messageQueue, messageQueueAutoExecute } = req.body;
|
|
292
|
+
const session = agent.updateSession(req.params.sessionId, { title, selectModelId, unreadCount, messageQueue, messageQueueAutoExecute });
|
|
290
293
|
res.json({
|
|
291
294
|
success: true,
|
|
292
295
|
data: {
|
|
293
296
|
id: session.id,
|
|
297
|
+
agentId: session.agentId,
|
|
294
298
|
title: session.title,
|
|
295
299
|
selectModelId: session.selectModelId,
|
|
300
|
+
messageQueue: session.messageQueue,
|
|
301
|
+
messageQueueAutoExecute: session.messageQueueAutoExecute,
|
|
296
302
|
createdAt: session.createdAt,
|
|
297
303
|
updatedAt: session.updatedAt,
|
|
298
304
|
}
|
|
@@ -1,9 +1,20 @@
|
|
|
1
1
|
"use strict";
|
|
2
|
+
var __importDefault = (this && this.__importDefault) || function (mod) {
|
|
3
|
+
return (mod && mod.__esModule) ? mod : { "default": mod };
|
|
4
|
+
};
|
|
2
5
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
6
|
exports.getGlobalModelSelector = exports.parseAruments = exports.LLMClient = void 0;
|
|
4
7
|
const index_js_1 = require("../../stores/index.js");
|
|
5
8
|
const shared_1 = require("@myassis/shared");
|
|
9
|
+
const promises_1 = __importDefault(require("fs/promises"));
|
|
6
10
|
const logger = (0, shared_1.getLogger)('LLMClient');
|
|
11
|
+
function convertBase64ToImage(base64Img) {
|
|
12
|
+
// 去掉data:image/xxx;base64,前缀
|
|
13
|
+
const pureBase64 = base64Img.replace(/^data:image\/\w+;base64,/, '');
|
|
14
|
+
const buf = Buffer.from(pureBase64, 'base64');
|
|
15
|
+
// 写入文件
|
|
16
|
+
promises_1.default.writeFile('screenshot.png', buf);
|
|
17
|
+
}
|
|
7
18
|
/**
|
|
8
19
|
* LLM 调用器类
|
|
9
20
|
* 提供 Chat 和 streamChat 两种调用方式
|
|
@@ -126,293 +137,189 @@ class LLMClient {
|
|
|
126
137
|
if (!apiKey) {
|
|
127
138
|
throw new Error('No API Key');
|
|
128
139
|
}
|
|
129
|
-
|
|
130
|
-
|
|
131
|
-
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
},
|
|
151
|
-
};
|
|
152
|
-
}
|
|
153
|
-
else if (model.baseUrl.includes('anthropic.com')) {
|
|
154
|
-
return {
|
|
155
|
-
url: `${model.baseUrl}/v1/messages`,
|
|
156
|
-
headers: {
|
|
157
|
-
'Content-Type': 'application/json',
|
|
158
|
-
'x-api-key': apiKey || '',
|
|
159
|
-
'anthropic-version': '2023-06-01',
|
|
160
|
-
'anthropic-dangerous-direct-browser-access': 'true',
|
|
161
|
-
},
|
|
162
|
-
body: {
|
|
163
|
-
model: model.modelId,
|
|
164
|
-
messages: this.messages.filter((m) => m.role !== 'system').map((m) => {
|
|
165
|
-
// 处理工具消息
|
|
166
|
-
if (m.role === 'tool') {
|
|
167
|
-
return {
|
|
168
|
-
role: 'user',
|
|
169
|
-
content: [
|
|
170
|
-
{
|
|
171
|
-
type: 'tool_result',
|
|
172
|
-
tool_use_id: m.tool_call_id,
|
|
173
|
-
content: m.content,
|
|
174
|
-
}
|
|
175
|
-
],
|
|
176
|
-
};
|
|
140
|
+
// OpenAI compatible API
|
|
141
|
+
let existAttachments = false;
|
|
142
|
+
let existScreenShot = false;
|
|
143
|
+
const body = {
|
|
144
|
+
model: model.modelId,
|
|
145
|
+
messages: this.messages.map((m, index) => {
|
|
146
|
+
if (m.role === 'tool') {
|
|
147
|
+
if (m.tool_call_name == 'screenshot') {
|
|
148
|
+
if (index === this.messages.length - 1) {
|
|
149
|
+
existScreenShot = true;
|
|
150
|
+
const contentObj = JSON.parse(m.content);
|
|
151
|
+
return [{
|
|
152
|
+
role: 'tool',
|
|
153
|
+
tool_call_id: m.tool_call_id,
|
|
154
|
+
content: JSON.stringify({
|
|
155
|
+
success: contentObj.success,
|
|
156
|
+
result_type: 'image',
|
|
157
|
+
image_url: contentObj.output,
|
|
158
|
+
text_for_llm: '工具生成了一张图片,图片地址已返回。'
|
|
159
|
+
}),
|
|
160
|
+
}];
|
|
177
161
|
}
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
181
|
-
|
|
182
|
-
|
|
183
|
-
|
|
184
|
-
type: 'text',
|
|
185
|
-
text: m.content,
|
|
186
|
-
});
|
|
187
|
-
}
|
|
188
|
-
// 添加附件
|
|
189
|
-
for (const attachment of m.attachments) {
|
|
190
|
-
if (attachment.type === 'image') {
|
|
191
|
-
// Anthropic 支持 base64 图片
|
|
192
|
-
// 假设 attachment.url 可能是 base64 格式或 URL 格式
|
|
193
|
-
if (attachment.url.startsWith('data:')) {
|
|
194
|
-
// base64 格式
|
|
195
|
-
const base64Data = attachment.url.split(',')[1];
|
|
196
|
-
const mimeType = attachment.mimeType || 'image/png';
|
|
197
|
-
contentArray.push({
|
|
198
|
-
type: 'image',
|
|
199
|
-
source: {
|
|
200
|
-
type: 'base64',
|
|
201
|
-
media_type: mimeType,
|
|
202
|
-
data: base64Data,
|
|
203
|
-
},
|
|
204
|
-
});
|
|
205
|
-
}
|
|
206
|
-
else {
|
|
207
|
-
// URL 格式 - Anthropic 也支持
|
|
208
|
-
contentArray.push({
|
|
209
|
-
type: 'image',
|
|
210
|
-
source: {
|
|
211
|
-
type: 'url',
|
|
212
|
-
media_type: attachment.mimeType || 'image/png',
|
|
213
|
-
url: attachment.url,
|
|
214
|
-
},
|
|
215
|
-
});
|
|
216
|
-
}
|
|
217
|
-
}
|
|
218
|
-
else {
|
|
219
|
-
// 其他文件类型 - Anthropic 不直接支持,添加为文本引用
|
|
220
|
-
const fileInfo = `[附件: ${attachment.name}](${attachment.url})`;
|
|
221
|
-
if (contentArray.length === 0) {
|
|
222
|
-
contentArray.push({
|
|
223
|
-
type: 'text',
|
|
224
|
-
text: fileInfo,
|
|
225
|
-
});
|
|
226
|
-
}
|
|
227
|
-
else {
|
|
228
|
-
const lastItem = contentArray[contentArray.length - 1];
|
|
229
|
-
if (lastItem.type === 'text') {
|
|
230
|
-
lastItem.text += `\n${fileInfo}`;
|
|
231
|
-
}
|
|
232
|
-
else {
|
|
233
|
-
contentArray.push({
|
|
234
|
-
type: 'text',
|
|
235
|
-
text: fileInfo,
|
|
236
|
-
});
|
|
237
|
-
}
|
|
238
|
-
}
|
|
239
|
-
}
|
|
240
|
-
}
|
|
241
|
-
return {
|
|
242
|
-
role: 'user',
|
|
243
|
-
content: contentArray,
|
|
244
|
-
};
|
|
162
|
+
else {
|
|
163
|
+
return [{
|
|
164
|
+
role: 'tool',
|
|
165
|
+
tool_call_id: m.tool_call_id,
|
|
166
|
+
content: m.content.substring(0, 100) + '...(内容过长已截断)',
|
|
167
|
+
}];
|
|
245
168
|
}
|
|
246
|
-
|
|
247
|
-
|
|
248
|
-
role: m.role,
|
|
249
|
-
content: m.content,
|
|
250
|
-
};
|
|
251
|
-
}),
|
|
252
|
-
stream,
|
|
253
|
-
max_tokens: model.maxTokens || 4096,
|
|
254
|
-
},
|
|
255
|
-
};
|
|
256
|
-
}
|
|
257
|
-
else {
|
|
258
|
-
// OpenAI compatible API
|
|
259
|
-
let existAttachments = false;
|
|
260
|
-
const body = {
|
|
261
|
-
model: model.modelId,
|
|
262
|
-
messages: this.messages.map((m) => {
|
|
263
|
-
if (m.role === 'tool') {
|
|
169
|
+
}
|
|
170
|
+
else {
|
|
264
171
|
return [{
|
|
265
172
|
role: 'tool',
|
|
266
173
|
tool_call_id: m.tool_call_id,
|
|
267
174
|
content: m.content,
|
|
268
175
|
}];
|
|
269
176
|
}
|
|
270
|
-
|
|
271
|
-
|
|
272
|
-
|
|
273
|
-
|
|
274
|
-
|
|
275
|
-
|
|
276
|
-
|
|
277
|
-
|
|
278
|
-
|
|
279
|
-
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
}
|
|
285
|
-
}
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
|
|
301
|
-
|
|
302
|
-
|
|
303
|
-
|
|
304
|
-
|
|
305
|
-
|
|
306
|
-
|
|
307
|
-
|
|
308
|
-
|
|
309
|
-
|
|
310
|
-
|
|
311
|
-
|
|
312
|
-
|
|
313
|
-
}
|
|
314
|
-
}
|
|
315
|
-
if (item.tool_calls.length > 0) {
|
|
316
|
-
items.push(item);
|
|
317
|
-
items = items.concat(tools);
|
|
177
|
+
}
|
|
178
|
+
// 助手调用工具的消息:必须保留 tool_calls!!(你之前丢了,导致报错)
|
|
179
|
+
if (m.role === 'assistant' && m.tool_calls) {
|
|
180
|
+
return [{
|
|
181
|
+
role: 'assistant',
|
|
182
|
+
content: m.content || null,
|
|
183
|
+
tool_calls: m.tool_calls.map(m => {
|
|
184
|
+
return {
|
|
185
|
+
id: m.id,
|
|
186
|
+
function: {
|
|
187
|
+
name: m.toolName,
|
|
188
|
+
arguments: m.input
|
|
189
|
+
},
|
|
190
|
+
type: 'function'
|
|
191
|
+
};
|
|
192
|
+
}),
|
|
193
|
+
}];
|
|
194
|
+
}
|
|
195
|
+
if (m.role === 'assistant' && m.toolCalls) {
|
|
196
|
+
let items = [];
|
|
197
|
+
for (let toolCall of m.toolCalls) {
|
|
198
|
+
const item = {
|
|
199
|
+
role: 'assistant',
|
|
200
|
+
content: toolCall.content,
|
|
201
|
+
reasoning_content: toolCall.reasoningContent,
|
|
202
|
+
tool_calls: []
|
|
203
|
+
};
|
|
204
|
+
const tools = [];
|
|
205
|
+
for (let i = 0; i < toolCall.toolCalls.length; i++) {
|
|
206
|
+
const toolCallItem = toolCall.toolCalls[i];
|
|
207
|
+
if (toolCallItem.output != null) {
|
|
208
|
+
item.tool_calls.push({
|
|
209
|
+
id: toolCallItem.id,
|
|
210
|
+
function: {
|
|
211
|
+
name: toolCallItem.toolName,
|
|
212
|
+
arguments: toolCallItem.input
|
|
213
|
+
},
|
|
214
|
+
type: 'function'
|
|
215
|
+
});
|
|
216
|
+
tools.push({
|
|
217
|
+
role: 'tool',
|
|
218
|
+
tool_call_id: toolCallItem.id,
|
|
219
|
+
content: toolCallItem.output.substring(0, 100) + '...' + '(内容过长已截断)'
|
|
220
|
+
});
|
|
318
221
|
}
|
|
319
222
|
}
|
|
320
|
-
if (
|
|
321
|
-
items.push(
|
|
322
|
-
|
|
323
|
-
content: m.content
|
|
324
|
-
});
|
|
223
|
+
if (item.tool_calls.length > 0) {
|
|
224
|
+
items.push(item);
|
|
225
|
+
items = items.concat(tools);
|
|
325
226
|
}
|
|
326
|
-
return items;
|
|
327
227
|
}
|
|
328
|
-
|
|
329
|
-
|
|
330
|
-
|
|
331
|
-
|
|
332
|
-
|
|
333
|
-
|
|
334
|
-
|
|
335
|
-
|
|
336
|
-
|
|
337
|
-
|
|
338
|
-
|
|
339
|
-
|
|
340
|
-
|
|
341
|
-
|
|
342
|
-
|
|
343
|
-
|
|
344
|
-
|
|
345
|
-
|
|
228
|
+
if (m.content) {
|
|
229
|
+
items.push({
|
|
230
|
+
role: 'assistant',
|
|
231
|
+
content: m.content
|
|
232
|
+
});
|
|
233
|
+
}
|
|
234
|
+
return items;
|
|
235
|
+
}
|
|
236
|
+
// 处理用户消息中的附件
|
|
237
|
+
if (m.role === 'user' && m.attachments && m.attachments.length > 0) {
|
|
238
|
+
existAttachments = true;
|
|
239
|
+
const contentArray = [];
|
|
240
|
+
// 添加附件
|
|
241
|
+
for (const attachment of m.attachments) {
|
|
242
|
+
if (attachment.type === 'image') {
|
|
243
|
+
// 图片附件
|
|
244
|
+
contentArray.push({
|
|
245
|
+
type: 'image_url',
|
|
246
|
+
image_url: {
|
|
247
|
+
url: attachment.url,
|
|
248
|
+
detail: 'high',
|
|
249
|
+
},
|
|
250
|
+
});
|
|
251
|
+
}
|
|
252
|
+
else if (attachment.type === 'audio') {
|
|
253
|
+
// 音频附件 - 使用 file 类型
|
|
254
|
+
contentArray.push({
|
|
255
|
+
type: 'input_audio',
|
|
256
|
+
input_audio: {
|
|
257
|
+
data: attachment.url, // 如果是 base64 格式
|
|
258
|
+
format: attachment.mimeType?.split('/')[1] || 'wav',
|
|
259
|
+
},
|
|
260
|
+
});
|
|
261
|
+
}
|
|
262
|
+
else {
|
|
263
|
+
// 其他文件类型:video, file - 添加为文本引用
|
|
264
|
+
const fileInfo = `[附件: ${attachment.name}](${attachment.url})`;
|
|
265
|
+
if (contentArray.length === 0) {
|
|
346
266
|
contentArray.push({
|
|
347
|
-
type: '
|
|
348
|
-
|
|
349
|
-
data: attachment.url, // 如果是 base64 格式
|
|
350
|
-
format: attachment.mimeType?.split('/')[1] || 'wav',
|
|
351
|
-
},
|
|
267
|
+
type: 'text',
|
|
268
|
+
text: fileInfo,
|
|
352
269
|
});
|
|
353
270
|
}
|
|
354
271
|
else {
|
|
355
|
-
//
|
|
356
|
-
const
|
|
357
|
-
if (
|
|
272
|
+
// 如果已经有文本,将文件信息追加到文本中
|
|
273
|
+
const lastItem = contentArray[contentArray.length - 1];
|
|
274
|
+
if (lastItem.type === 'text') {
|
|
275
|
+
lastItem.text += `\n${fileInfo}`;
|
|
276
|
+
}
|
|
277
|
+
else {
|
|
358
278
|
contentArray.push({
|
|
359
279
|
type: 'text',
|
|
360
280
|
text: fileInfo,
|
|
361
281
|
});
|
|
362
282
|
}
|
|
363
|
-
else {
|
|
364
|
-
// 如果已经有文本,将文件信息追加到文本中
|
|
365
|
-
const lastItem = contentArray[contentArray.length - 1];
|
|
366
|
-
if (lastItem.type === 'text') {
|
|
367
|
-
lastItem.text += `\n${fileInfo}`;
|
|
368
|
-
}
|
|
369
|
-
else {
|
|
370
|
-
contentArray.push({
|
|
371
|
-
type: 'text',
|
|
372
|
-
text: fileInfo,
|
|
373
|
-
});
|
|
374
|
-
}
|
|
375
|
-
}
|
|
376
283
|
}
|
|
377
284
|
}
|
|
378
|
-
// 如果有文本内容,添加文本
|
|
379
|
-
if (m.content && m.content.trim()) {
|
|
380
|
-
contentArray.push({
|
|
381
|
-
type: 'text',
|
|
382
|
-
text: m.content,
|
|
383
|
-
});
|
|
384
|
-
}
|
|
385
|
-
return [{
|
|
386
|
-
role: m.role,
|
|
387
|
-
content: contentArray,
|
|
388
|
-
}];
|
|
389
285
|
}
|
|
390
|
-
//
|
|
286
|
+
// 如果有文本内容,添加文本
|
|
287
|
+
if (m.content && m.content.trim()) {
|
|
288
|
+
contentArray.push({
|
|
289
|
+
type: 'text',
|
|
290
|
+
text: m.content,
|
|
291
|
+
});
|
|
292
|
+
}
|
|
391
293
|
return [{
|
|
392
294
|
role: m.role,
|
|
393
|
-
content:
|
|
295
|
+
content: contentArray,
|
|
394
296
|
}];
|
|
395
|
-
}
|
|
396
|
-
|
|
397
|
-
|
|
398
|
-
|
|
399
|
-
|
|
400
|
-
|
|
401
|
-
}
|
|
402
|
-
|
|
403
|
-
|
|
404
|
-
|
|
405
|
-
|
|
406
|
-
|
|
407
|
-
return {
|
|
408
|
-
url: `${model.baseUrl}/chat/completions`,
|
|
409
|
-
headers: {
|
|
410
|
-
'Content-Type': 'application/json',
|
|
411
|
-
'Authorization': `Bearer ${apiKey}`,
|
|
412
|
-
},
|
|
413
|
-
body,
|
|
414
|
-
};
|
|
297
|
+
}
|
|
298
|
+
// 普通消息(无附件)
|
|
299
|
+
return [{
|
|
300
|
+
role: m.role,
|
|
301
|
+
content: m.content,
|
|
302
|
+
}];
|
|
303
|
+
}).flatMap(x => x),
|
|
304
|
+
stream,
|
|
305
|
+
enable_thinking: true,
|
|
306
|
+
};
|
|
307
|
+
if (this.tools && this.tools.length > 0) {
|
|
308
|
+
body.tools = this.tools;
|
|
415
309
|
}
|
|
310
|
+
const tokens = JSON.stringify(body).length;
|
|
311
|
+
logger.debug('tokens', tokens);
|
|
312
|
+
if (tokens > 200000 && !existAttachments && !existScreenShot) {
|
|
313
|
+
throw Error('exceed max message tokens');
|
|
314
|
+
}
|
|
315
|
+
return {
|
|
316
|
+
url: `${model.baseUrl}/chat/completions`,
|
|
317
|
+
headers: {
|
|
318
|
+
'Content-Type': 'application/json',
|
|
319
|
+
'Authorization': `Bearer ${apiKey}`,
|
|
320
|
+
},
|
|
321
|
+
body,
|
|
322
|
+
};
|
|
416
323
|
}
|
|
417
324
|
/**
|
|
418
325
|
* 执行流式请求
|
|
@@ -39,6 +39,8 @@ class Session {
|
|
|
39
39
|
title;
|
|
40
40
|
selectModelId;
|
|
41
41
|
messages;
|
|
42
|
+
messageQueue;
|
|
43
|
+
messageQueueAutoExecute;
|
|
42
44
|
voiceState;
|
|
43
45
|
createdAt;
|
|
44
46
|
updatedAt;
|
|
@@ -58,6 +60,8 @@ class Session {
|
|
|
58
60
|
this.title = data.title || 'New Chat';
|
|
59
61
|
this.selectModelId = data.selectModelId;
|
|
60
62
|
this.messages = [];
|
|
63
|
+
this.messageQueue = data.messageQueue || [];
|
|
64
|
+
this.messageQueueAutoExecute = data.messageQueueAutoExecute ?? true;
|
|
61
65
|
this.voiceState = data.voiceState || { isRecording: false, isPlaying: false };
|
|
62
66
|
this.lastMessageSummary = data.lastMessageSummary ?? null;
|
|
63
67
|
this.lastMessageSummaryAt = data.lastMessageSummaryAt ?? null;
|
|
@@ -390,7 +394,9 @@ class Session {
|
|
|
390
394
|
lastMessageSummary: this.lastMessageSummary,
|
|
391
395
|
lastMessageSummaryAt: this.lastMessageSummaryAt,
|
|
392
396
|
unreadCount: this.unreadCount,
|
|
393
|
-
isCurrent: this.isCurrent
|
|
397
|
+
isCurrent: this.isCurrent,
|
|
398
|
+
messageQueue: this.messageQueue,
|
|
399
|
+
messageQueueAutoExecute: this.messageQueueAutoExecute
|
|
394
400
|
};
|
|
395
401
|
}
|
|
396
402
|
getStreamDelay(streamSpeed) {
|
|
@@ -63,7 +63,9 @@ class SessionManager {
|
|
|
63
63
|
updatedAt: data.updatedAt,
|
|
64
64
|
isCurrent: data.isCurrent,
|
|
65
65
|
lastMessageSummary: data.lastMessageSummary,
|
|
66
|
-
lastMessageSummaryAt: data.lastMessageSummaryAt
|
|
66
|
+
lastMessageSummaryAt: data.lastMessageSummaryAt,
|
|
67
|
+
messageQueue: data.messageQueue,
|
|
68
|
+
messageQueueAutoExecute: data.messageQueueAutoExecute
|
|
67
69
|
});
|
|
68
70
|
session.loadMessages();
|
|
69
71
|
return session;
|
|
@@ -171,6 +173,10 @@ class SessionManager {
|
|
|
171
173
|
session.agentId = updates.agentId;
|
|
172
174
|
if (updates.unreadCount !== undefined)
|
|
173
175
|
session.unreadCount = updates.unreadCount;
|
|
176
|
+
if (updates.messageQueue !== undefined)
|
|
177
|
+
session.messageQueue = updates.messageQueue;
|
|
178
|
+
if (updates.messageQueueAutoExecute !== undefined)
|
|
179
|
+
session.messageQueueAutoExecute = updates.messageQueueAutoExecute;
|
|
174
180
|
session.updatedAt = Date.now();
|
|
175
181
|
session.save();
|
|
176
182
|
return session;
|
|
@@ -82,17 +82,17 @@ class SessionStore {
|
|
|
82
82
|
// ========== Session Operations ==========
|
|
83
83
|
insertSession(session) {
|
|
84
84
|
this.db.prepare(`
|
|
85
|
-
INSERT INTO sessions (id, user_id, agent_id, title, select_model_id, voice_state, created_at, updated_at,last_message_summary,last_message_summary_at,unread_count,is_current)
|
|
86
|
-
VALUES (?, ?, ?, ?, ?, ?, ?,
|
|
87
|
-
`).run(session.id, session.userId, session.agentId || null, session.title, session.selectModelId || '', JSON.stringify(session.voiceState), session.createdAt, session.updatedAt, session.lastMessageSummary, session.lastMessageSummaryAt, session.unreadCount || 0, session.isCurrent ? 1 : 0);
|
|
85
|
+
INSERT INTO sessions (id, user_id, agent_id, title, select_model_id, voice_state, created_at, updated_at,last_message_summary,last_message_summary_at,unread_count,is_current,message_queue,message_queue_auto_execute)
|
|
86
|
+
VALUES (?, ?, ?, ?, ?, ?, ?, ?,?,?,?,?,?,?)
|
|
87
|
+
`).run(session.id, session.userId, session.agentId || null, session.title, session.selectModelId || '', JSON.stringify(session.voiceState), session.createdAt, session.updatedAt, session.lastMessageSummary, session.lastMessageSummaryAt, session.unreadCount || 0, session.isCurrent ? 1 : 0, JSON.stringify(session.messageQueue || []), session.messageQueueAutoExecute === false ? 0 : 1);
|
|
88
88
|
}
|
|
89
89
|
updateSession(session) {
|
|
90
90
|
this.db.prepare(`
|
|
91
91
|
UPDATE sessions
|
|
92
92
|
SET user_id = ?, agent_id = ?, title = ?, select_model_id = ?,
|
|
93
|
-
voice_state = ?, updated_at = ?,last_message_summary=?,last_message_summary_at=?,unread_count=?,is_current=?
|
|
93
|
+
voice_state = ?, updated_at = ?,last_message_summary=?,last_message_summary_at=?,unread_count=?,is_current=?,message_queue=?,message_queue_auto_execute=?
|
|
94
94
|
WHERE id = ?
|
|
95
|
-
`).run(session.userId, session.agentId || null, session.title, session.selectModelId || '', JSON.stringify(session.voiceState), session.updatedAt, session.lastMessageSummary, session.lastMessageSummaryAt, session.unreadCount || 0, session.isCurrent ? 1 : 0, session.id);
|
|
95
|
+
`).run(session.userId, session.agentId || null, session.title, session.selectModelId || '', JSON.stringify(session.voiceState), session.updatedAt, session.lastMessageSummary, session.lastMessageSummaryAt, session.unreadCount || 0, session.isCurrent ? 1 : 0, JSON.stringify(session.messageQueue || []), session.messageQueueAutoExecute === false ? 0 : 1, session.id);
|
|
96
96
|
}
|
|
97
97
|
// 支持部分更新的 updateSession
|
|
98
98
|
updateSessionPartial(id, data) {
|
|
@@ -106,6 +106,14 @@ class SessionStore {
|
|
|
106
106
|
updates.push('select_model_id = ?');
|
|
107
107
|
values.push(data.selectModelId || '');
|
|
108
108
|
}
|
|
109
|
+
if (data.messageQueue !== undefined) {
|
|
110
|
+
updates.push('message_queue = ?');
|
|
111
|
+
values.push(JSON.stringify(data.messageQueue || []));
|
|
112
|
+
}
|
|
113
|
+
if (data.messageQueueAutoExecute !== undefined) {
|
|
114
|
+
updates.push('message_queue_auto_execute = ?');
|
|
115
|
+
values.push(data.messageQueueAutoExecute ? 1 : 0);
|
|
116
|
+
}
|
|
109
117
|
if (updates.length === 0)
|
|
110
118
|
return;
|
|
111
119
|
updates.push('updated_at = ?');
|
|
@@ -185,7 +193,9 @@ class SessionStore {
|
|
|
185
193
|
lastMessageSummary: row.last_message_summary,
|
|
186
194
|
lastMessageSummaryAt: row.last_message_summary_at,
|
|
187
195
|
unreadCount: row.unread_count,
|
|
188
|
-
isCurrent: !!row.is_current
|
|
196
|
+
isCurrent: !!row.is_current,
|
|
197
|
+
messageQueueAutoExecute: row.message_queue_auto_execute !== 0,
|
|
198
|
+
messageQueue: JSON.parse(row.message_queue || '[]'),
|
|
189
199
|
};
|
|
190
200
|
}
|
|
191
201
|
/**
|
|
@@ -15,7 +15,7 @@ exports.screenshotTool = {
|
|
|
15
15
|
const base64 = imgBuffer.toString('base64');
|
|
16
16
|
return {
|
|
17
17
|
success: true,
|
|
18
|
-
output:
|
|
18
|
+
output: `data:image/jpeg;base64,${base64}`,
|
|
19
19
|
};
|
|
20
20
|
}
|
|
21
21
|
catch (error) {
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE sessions ADD COLUMN message_queue_auto_execute INTEGER DEFAULT 1;
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
ALTER TABLE sessions ADD COLUMN message_queue TEXT DEFAULT '[]';
|