xiaozuoassistant 0.1.50 → 0.1.52
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/config.json +2 -1
- package/dist/client/assets/{browser-ponyfill-C0qLfQMC.js → browser-ponyfill-Bz0RS90l.js} +1 -1
- package/dist/client/assets/index-BwZQ_7Ty.js +158 -0
- package/dist/client/index.html +1 -1
- package/dist/server/channels/base-channel.js +6 -0
- package/dist/server/channels/web.js +21 -0
- package/dist/server/config/loader.js +6 -2
- package/dist/server/core/agents/runtime.js +5 -1
- package/dist/server/core/brain.js +5 -2
- package/dist/server/core/memories/manager.js +9 -0
- package/dist/server/core/memories/short-term.js +68 -0
- package/dist/server/core/task-queue.js +104 -0
- package/dist/server/index.js +40 -39
- package/package.json +1 -1
- package/dist/client/assets/index-BfEuvtsz.js +0 -158
package/dist/client/index.html
CHANGED
|
@@ -5,7 +5,7 @@
|
|
|
5
5
|
<link rel="icon" href="data:image/svg+xml,<svg xmlns=%22http://www.w3.org/2000/svg%22 viewBox=%220 0 100 100%22><text y=%22.9em%22 font-size=%2290%22>🍇</text></svg>" />
|
|
6
6
|
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
7
7
|
<title>xiaozuoAssistant</title>
|
|
8
|
-
<script type="module" crossorigin src="/assets/index-
|
|
8
|
+
<script type="module" crossorigin src="/assets/index-BwZQ_7Ty.js"></script>
|
|
9
9
|
<link rel="stylesheet" crossorigin href="/assets/index-C72E_nIr.css">
|
|
10
10
|
</head>
|
|
11
11
|
<body>
|
|
@@ -2,6 +2,12 @@ export class BaseChannel {
|
|
|
2
2
|
constructor() {
|
|
3
3
|
this.messageHandler = null;
|
|
4
4
|
}
|
|
5
|
+
sendEvent(sessionId, event, payload) {
|
|
6
|
+
const text = event === 'message'
|
|
7
|
+
? String(payload?.content ?? '')
|
|
8
|
+
: `[${event}] ${typeof payload === 'string' ? payload : JSON.stringify(payload)}`;
|
|
9
|
+
void this.send(sessionId, text);
|
|
10
|
+
}
|
|
5
11
|
onMessage(handler) {
|
|
6
12
|
this.messageHandler = handler;
|
|
7
13
|
}
|
|
@@ -2,6 +2,7 @@ export class WebChannel {
|
|
|
2
2
|
constructor(io) {
|
|
3
3
|
this.name = 'web';
|
|
4
4
|
this.messageHandler = null;
|
|
5
|
+
this.syncHandler = null;
|
|
5
6
|
this.io = io;
|
|
6
7
|
}
|
|
7
8
|
async start() {
|
|
@@ -12,6 +13,20 @@ export class WebChannel {
|
|
|
12
13
|
socket.join(sessionId);
|
|
13
14
|
console.log(`Socket ${socket.id} joined session ${sessionId}`);
|
|
14
15
|
});
|
|
16
|
+
socket.on('sync_session', async (data) => {
|
|
17
|
+
try {
|
|
18
|
+
if (!this.syncHandler)
|
|
19
|
+
return;
|
|
20
|
+
const { sessionId, since } = data || {};
|
|
21
|
+
if (!sessionId)
|
|
22
|
+
return;
|
|
23
|
+
const snapshot = await this.syncHandler(sessionId, since);
|
|
24
|
+
socket.emit('session_sync', { sessionId, ...snapshot });
|
|
25
|
+
}
|
|
26
|
+
catch (e) {
|
|
27
|
+
socket.emit('session_sync', { sessionId: data?.sessionId, messages: [], runs: [], error: String(e?.message || e) });
|
|
28
|
+
}
|
|
29
|
+
});
|
|
15
30
|
// 当客户端发送消息
|
|
16
31
|
socket.on('message', (data) => {
|
|
17
32
|
console.log(`[WebChannel] Received message from ${data.sessionId}: ${data.content}`);
|
|
@@ -39,7 +54,13 @@ export class WebChannel {
|
|
|
39
54
|
timestamp: Date.now()
|
|
40
55
|
});
|
|
41
56
|
}
|
|
57
|
+
sendEvent(sessionId, event, payload) {
|
|
58
|
+
this.io.to(sessionId).emit(event, payload);
|
|
59
|
+
}
|
|
42
60
|
onMessage(handler) {
|
|
43
61
|
this.messageHandler = handler;
|
|
44
62
|
}
|
|
63
|
+
setSyncHandler(handler) {
|
|
64
|
+
this.syncHandler = handler;
|
|
65
|
+
}
|
|
45
66
|
}
|
|
@@ -22,7 +22,8 @@ try {
|
|
|
22
22
|
model: 'qwen-plus',
|
|
23
23
|
temperature: 0.7,
|
|
24
24
|
requestTimeoutMs: 600000,
|
|
25
|
-
maxRetries: 2
|
|
25
|
+
maxRetries: 2,
|
|
26
|
+
maxToolIterations: 200
|
|
26
27
|
};
|
|
27
28
|
}
|
|
28
29
|
else {
|
|
@@ -32,6 +33,8 @@ try {
|
|
|
32
33
|
loadedConfig.llm.requestTimeoutMs = 600000;
|
|
33
34
|
if (loadedConfig.llm.maxRetries === undefined)
|
|
34
35
|
loadedConfig.llm.maxRetries = 2;
|
|
36
|
+
if (loadedConfig.llm.maxToolIterations === undefined)
|
|
37
|
+
loadedConfig.llm.maxToolIterations = 200;
|
|
35
38
|
}
|
|
36
39
|
// Override with env vars if present (optional, but good for security)
|
|
37
40
|
if (process.env.XIAOZUO_LLM_API_KEY)
|
|
@@ -51,7 +54,8 @@ catch (error) {
|
|
|
51
54
|
model: 'qwen-plus',
|
|
52
55
|
temperature: 0.7,
|
|
53
56
|
requestTimeoutMs: 600000,
|
|
54
|
-
maxRetries: 2
|
|
57
|
+
maxRetries: 2,
|
|
58
|
+
maxToolIterations: 200
|
|
55
59
|
},
|
|
56
60
|
logging: { level: 'info' },
|
|
57
61
|
scheduler: { memoryMaintenanceCron: '0 0 * * *' },
|
|
@@ -32,7 +32,7 @@ export class AgentRuntime {
|
|
|
32
32
|
console.log(`[Agent:${this.name}] Calling LLM...`);
|
|
33
33
|
let response = await this.callLLM(messages);
|
|
34
34
|
let iterations = 0;
|
|
35
|
-
const MAX_ITERATIONS =
|
|
35
|
+
const MAX_ITERATIONS = config.llm.maxToolIterations ?? 200;
|
|
36
36
|
while (response.choices[0].message.tool_calls && iterations < MAX_ITERATIONS) {
|
|
37
37
|
iterations++;
|
|
38
38
|
const toolCalls = response.choices[0].message.tool_calls;
|
|
@@ -68,6 +68,10 @@ export class AgentRuntime {
|
|
|
68
68
|
}
|
|
69
69
|
response = await this.callLLM(messages);
|
|
70
70
|
}
|
|
71
|
+
if (response.choices[0].message.tool_calls && iterations >= MAX_ITERATIONS) {
|
|
72
|
+
const content = response.choices[0].message.content || 'No response generated.';
|
|
73
|
+
return `${content}\n\n(已到达回合上限,建议回复“继续”以接着执行;可在 config.json 设置 llm.maxToolIterations,当前=${MAX_ITERATIONS})`;
|
|
74
|
+
}
|
|
71
75
|
return response.choices[0].message.content || 'No response generated.';
|
|
72
76
|
}
|
|
73
77
|
catch (error) {
|
|
@@ -45,7 +45,7 @@ export class Brain {
|
|
|
45
45
|
if (process.env.DEBUG)
|
|
46
46
|
console.log('[Brain] LLM Response (snippet):', contentSnippet);
|
|
47
47
|
let iterations = 0;
|
|
48
|
-
const MAX_ITERATIONS =
|
|
48
|
+
const MAX_ITERATIONS = config.llm.maxToolIterations ?? 200;
|
|
49
49
|
while (response.choices[0].message.tool_calls && iterations < MAX_ITERATIONS) {
|
|
50
50
|
iterations++;
|
|
51
51
|
const toolCalls = response.choices[0].message.tool_calls;
|
|
@@ -92,10 +92,13 @@ export class Brain {
|
|
|
92
92
|
if (process.env.DEBUG)
|
|
93
93
|
console.log('[Brain] LLM Response (after tool, snippet):', nextContentSnippet);
|
|
94
94
|
}
|
|
95
|
+
const hitLimit = Boolean(response.choices[0].message.tool_calls) && iterations >= MAX_ITERATIONS;
|
|
95
96
|
const finalContent = response.choices[0].message.content || 'I could not generate a response.';
|
|
96
97
|
if (process.env.DEBUG)
|
|
97
98
|
console.log('[Brain] Final Response (snippet):', finalContent.substring(0, 100) + '...');
|
|
98
|
-
|
|
99
|
+
if (!hitLimit)
|
|
100
|
+
return finalContent;
|
|
101
|
+
return `${finalContent}\n\n(已到达回合上限,建议回复“继续”以接着执行;可在 config.json 设置 llm.maxToolIterations,当前=${MAX_ITERATIONS})`;
|
|
99
102
|
}
|
|
100
103
|
catch (error) {
|
|
101
104
|
console.error('[Brain] Error in processing:', error);
|
|
@@ -44,6 +44,15 @@ export class MemoryManager {
|
|
|
44
44
|
// structured info from the message. For MVP, we skip automatic extraction here
|
|
45
45
|
// but provide the API for the Brain to call explicitly.
|
|
46
46
|
}
|
|
47
|
+
async createRun(sessionId, userContent, runId) {
|
|
48
|
+
return this.shortTerm.createRun(sessionId, userContent, runId);
|
|
49
|
+
}
|
|
50
|
+
async updateRun(sessionId, runId, patch) {
|
|
51
|
+
return this.shortTerm.updateRun(sessionId, runId, patch);
|
|
52
|
+
}
|
|
53
|
+
async listRuns(sessionId) {
|
|
54
|
+
return this.shortTerm.listRuns(sessionId);
|
|
55
|
+
}
|
|
47
56
|
async getHistory(sessionId) {
|
|
48
57
|
return this.shortTerm.getMessages(sessionId);
|
|
49
58
|
}
|
|
@@ -50,6 +50,9 @@ export class ShortTermMemory {
|
|
|
50
50
|
getMessagesFile(sessionId) {
|
|
51
51
|
return path.join(this.getSessionDir(sessionId), 'messages.json');
|
|
52
52
|
}
|
|
53
|
+
getRunsFile(sessionId) {
|
|
54
|
+
return path.join(this.getSessionDir(sessionId), 'runs.json');
|
|
55
|
+
}
|
|
53
56
|
async loadIndex() {
|
|
54
57
|
try {
|
|
55
58
|
const data = await fs.readJson(INDEX_FILE);
|
|
@@ -185,6 +188,71 @@ export class ShortTermMemory {
|
|
|
185
188
|
}
|
|
186
189
|
return { meta, messages };
|
|
187
190
|
}
|
|
191
|
+
async createRun(sessionId, userContent, runId) {
|
|
192
|
+
await this.ensureReady();
|
|
193
|
+
const meta = await this.getSessionMeta(sessionId);
|
|
194
|
+
if (!meta)
|
|
195
|
+
throw new Error('Session not found');
|
|
196
|
+
const now = Date.now();
|
|
197
|
+
const id = runId || uuidv4();
|
|
198
|
+
const run = {
|
|
199
|
+
id,
|
|
200
|
+
sessionId,
|
|
201
|
+
status: 'queued',
|
|
202
|
+
userContent,
|
|
203
|
+
createdAt: now,
|
|
204
|
+
updatedAt: now
|
|
205
|
+
};
|
|
206
|
+
const file = this.getRunsFile(sessionId);
|
|
207
|
+
let runs = [];
|
|
208
|
+
try {
|
|
209
|
+
const existing = await fs.readJson(file);
|
|
210
|
+
runs = Array.isArray(existing) ? existing : [];
|
|
211
|
+
}
|
|
212
|
+
catch {
|
|
213
|
+
await fs.ensureDir(this.getSessionDir(sessionId));
|
|
214
|
+
}
|
|
215
|
+
runs.push(run);
|
|
216
|
+
await fs.writeJson(file, runs, { spaces: 2 });
|
|
217
|
+
return run;
|
|
218
|
+
}
|
|
219
|
+
async updateRun(sessionId, runId, patch) {
|
|
220
|
+
await this.ensureReady();
|
|
221
|
+
const file = this.getRunsFile(sessionId);
|
|
222
|
+
let runs = [];
|
|
223
|
+
try {
|
|
224
|
+
const existing = await fs.readJson(file);
|
|
225
|
+
runs = Array.isArray(existing) ? existing : [];
|
|
226
|
+
}
|
|
227
|
+
catch {
|
|
228
|
+
runs = [];
|
|
229
|
+
}
|
|
230
|
+
const idx = runs.findIndex(r => r.id === runId);
|
|
231
|
+
if (idx < 0)
|
|
232
|
+
throw new Error('Run not found');
|
|
233
|
+
const next = {
|
|
234
|
+
...runs[idx],
|
|
235
|
+
...patch,
|
|
236
|
+
status: patch.status || runs[idx].status,
|
|
237
|
+
updatedAt: patch.updatedAt ?? Date.now()
|
|
238
|
+
};
|
|
239
|
+
runs[idx] = next;
|
|
240
|
+
await fs.writeJson(file, runs, { spaces: 2 });
|
|
241
|
+
return next;
|
|
242
|
+
}
|
|
243
|
+
async listRuns(sessionId) {
|
|
244
|
+
await this.ensureReady();
|
|
245
|
+
const file = this.getRunsFile(sessionId);
|
|
246
|
+
try {
|
|
247
|
+
const existing = await fs.readJson(file);
|
|
248
|
+
const runs = Array.isArray(existing) ? existing : [];
|
|
249
|
+
runs.sort((a, b) => (b.createdAt ?? 0) - (a.createdAt ?? 0));
|
|
250
|
+
return runs;
|
|
251
|
+
}
|
|
252
|
+
catch {
|
|
253
|
+
return [];
|
|
254
|
+
}
|
|
255
|
+
}
|
|
188
256
|
async getMessages(sessionId) {
|
|
189
257
|
const session = await this.getSession(sessionId);
|
|
190
258
|
return session ? session.messages : [];
|
|
@@ -0,0 +1,104 @@
|
|
|
1
|
+
import { memory } from './memory.js';
|
|
2
|
+
import { brain } from './brain.js';
|
|
3
|
+
import { eventBus } from './event-bus.js';
|
|
4
|
+
export class TaskQueue {
|
|
5
|
+
constructor() {
|
|
6
|
+
this.chains = new Map();
|
|
7
|
+
}
|
|
8
|
+
enqueue(task) {
|
|
9
|
+
const key = task.run.sessionId;
|
|
10
|
+
const prev = this.chains.get(key) || Promise.resolve();
|
|
11
|
+
const next = prev
|
|
12
|
+
.catch(() => { })
|
|
13
|
+
.then(() => this.process(task))
|
|
14
|
+
.finally(() => {
|
|
15
|
+
if (this.chains.get(key) === next)
|
|
16
|
+
this.chains.delete(key);
|
|
17
|
+
});
|
|
18
|
+
this.chains.set(key, next);
|
|
19
|
+
}
|
|
20
|
+
async process(task) {
|
|
21
|
+
const { run, channel } = task;
|
|
22
|
+
try {
|
|
23
|
+
const session = await memory.getSession(run.sessionId);
|
|
24
|
+
if (!session) {
|
|
25
|
+
await memory.updateRun(run.sessionId, run.id, { status: 'failed', error: 'Session not found', completedAt: Date.now() });
|
|
26
|
+
eventBus.emitEvent({
|
|
27
|
+
type: 'run_status',
|
|
28
|
+
payload: { runId: run.id, status: 'failed', error: 'Session not found' },
|
|
29
|
+
channel,
|
|
30
|
+
sessionId: run.sessionId,
|
|
31
|
+
timestamp: Date.now()
|
|
32
|
+
});
|
|
33
|
+
return;
|
|
34
|
+
}
|
|
35
|
+
await memory.updateRun(run.sessionId, run.id, { status: 'running', updatedAt: Date.now() });
|
|
36
|
+
eventBus.emitEvent({
|
|
37
|
+
type: 'run_status',
|
|
38
|
+
payload: { runId: run.id, status: 'running' },
|
|
39
|
+
channel,
|
|
40
|
+
sessionId: run.sessionId,
|
|
41
|
+
timestamp: Date.now()
|
|
42
|
+
});
|
|
43
|
+
const context = await memory.getRelevantContext(run.userContent, run.sessionId);
|
|
44
|
+
const sessionAlias = session.meta.alias ? `Session Alias: ${session.meta.alias}` : '';
|
|
45
|
+
const workspaceLine = session.meta.workspace ? `Current Workspace: ${session.meta.workspace}` : '';
|
|
46
|
+
const enhancedSystemPrompt = `You are xiaozuoAssistant, a helpful AI assistant.
|
|
47
|
+
${sessionAlias}
|
|
48
|
+
${workspaceLine}
|
|
49
|
+
Here is some relevant context from your memory:
|
|
50
|
+
${context}`;
|
|
51
|
+
const ctx = {
|
|
52
|
+
sessionId: run.sessionId,
|
|
53
|
+
history: session.messages,
|
|
54
|
+
channel,
|
|
55
|
+
session: session.meta,
|
|
56
|
+
metadata: {
|
|
57
|
+
workspace: session.meta.workspace,
|
|
58
|
+
alias: session.meta.alias,
|
|
59
|
+
runId: run.id
|
|
60
|
+
}
|
|
61
|
+
};
|
|
62
|
+
const responseContent = await brain.processMessage(session.messages, run.userContent, enhancedSystemPrompt, ctx);
|
|
63
|
+
await memory.addMessage(run.sessionId, { role: 'assistant', content: responseContent, timestamp: Date.now() });
|
|
64
|
+
await memory.updateRun(run.sessionId, run.id, { status: 'completed', completedAt: Date.now() });
|
|
65
|
+
eventBus.emitEvent({
|
|
66
|
+
type: 'run_status',
|
|
67
|
+
payload: { runId: run.id, status: 'completed' },
|
|
68
|
+
channel,
|
|
69
|
+
sessionId: run.sessionId,
|
|
70
|
+
timestamp: Date.now()
|
|
71
|
+
});
|
|
72
|
+
eventBus.emitEvent({
|
|
73
|
+
type: 'response',
|
|
74
|
+
payload: { content: responseContent, runId: run.id },
|
|
75
|
+
channel,
|
|
76
|
+
sessionId: run.sessionId,
|
|
77
|
+
timestamp: Date.now()
|
|
78
|
+
});
|
|
79
|
+
}
|
|
80
|
+
catch (e) {
|
|
81
|
+
const msg = String(e?.message || e);
|
|
82
|
+
try {
|
|
83
|
+
await memory.updateRun(run.sessionId, run.id, { status: 'failed', error: msg, completedAt: Date.now() });
|
|
84
|
+
}
|
|
85
|
+
catch {
|
|
86
|
+
// ignore
|
|
87
|
+
}
|
|
88
|
+
try {
|
|
89
|
+
await memory.addMessage(run.sessionId, { role: 'assistant', content: `Error: ${msg}`, timestamp: Date.now() });
|
|
90
|
+
}
|
|
91
|
+
catch {
|
|
92
|
+
// ignore
|
|
93
|
+
}
|
|
94
|
+
eventBus.emitEvent({
|
|
95
|
+
type: 'run_status',
|
|
96
|
+
payload: { runId: run.id, status: 'failed', error: msg },
|
|
97
|
+
channel,
|
|
98
|
+
sessionId: run.sessionId,
|
|
99
|
+
timestamp: Date.now()
|
|
100
|
+
});
|
|
101
|
+
}
|
|
102
|
+
}
|
|
103
|
+
}
|
|
104
|
+
export const taskQueue = new TaskQueue();
|
package/dist/server/index.js
CHANGED
|
@@ -21,6 +21,8 @@ import { createOfficeAgent } from './agents/office.js';
|
|
|
21
21
|
import { DelegateSkill } from './skills/delegate.js';
|
|
22
22
|
import { createChannels } from './channels/create-channels.js';
|
|
23
23
|
import { createHttpStack } from './server/create-http.js';
|
|
24
|
+
import { taskQueue } from './core/task-queue.js';
|
|
25
|
+
import { WebChannel } from './channels/web.js';
|
|
24
26
|
// 解决 ES Module 中没有 __dirname 的问题
|
|
25
27
|
const __filename = fileURLToPath(import.meta.url);
|
|
26
28
|
const __dirname = path.dirname(__filename);
|
|
@@ -267,6 +269,19 @@ const pluginManager = new PluginManager(path.resolve(process.cwd(), 'plugins'));
|
|
|
267
269
|
await pluginManager.loadPlugins();
|
|
268
270
|
// 初始化通道
|
|
269
271
|
const channels = createChannels({ app, io, config, pluginManager });
|
|
272
|
+
for (const ch of channels) {
|
|
273
|
+
if (ch instanceof WebChannel) {
|
|
274
|
+
ch.setSyncHandler(async (sessionId, since) => {
|
|
275
|
+
const session = await memory.getSession(sessionId);
|
|
276
|
+
if (!session)
|
|
277
|
+
return { messages: [], runs: [] };
|
|
278
|
+
const runs = await memory.listRuns(sessionId);
|
|
279
|
+
const sinceTs = typeof since === 'number' ? since : 0;
|
|
280
|
+
const messages = (session.messages || []).filter((m) => (m.timestamp ?? 0) > sinceTs);
|
|
281
|
+
return { messages, runs };
|
|
282
|
+
});
|
|
283
|
+
}
|
|
284
|
+
}
|
|
270
285
|
// 启动通道并绑定事件
|
|
271
286
|
channels.forEach(channel => {
|
|
272
287
|
channel.start();
|
|
@@ -286,55 +301,27 @@ eventBus.onEvent('message', async (event) => {
|
|
|
286
301
|
const { sessionId, payload, channel: channelName } = event;
|
|
287
302
|
const content = payload.content;
|
|
288
303
|
try {
|
|
289
|
-
// 1. 获取会话上下文
|
|
290
304
|
const session = await memory.getSession(sessionId);
|
|
291
305
|
if (!session) {
|
|
292
306
|
throw new Error('Session not found. Please create or select a session first.');
|
|
293
307
|
}
|
|
294
|
-
// 2. 记录用户消息
|
|
295
308
|
await memory.addMessage(sessionId, { role: 'user', content, timestamp: Date.now() });
|
|
296
|
-
|
|
297
|
-
|
|
298
|
-
|
|
299
|
-
|
|
300
|
-
// Let's modify Brain.processMessage to accept an optional context string or just prepend it.
|
|
301
|
-
// For this MVP, we will prepend the context to the system prompt or as a new system message.
|
|
302
|
-
// Or simpler: We just pass session.messages as before, but Brain needs to know about the context.
|
|
303
|
-
// Let's change how we call brain.processMessage.
|
|
304
|
-
// Construct a temporary history with enhanced context for the LLM
|
|
305
|
-
const sessionAlias = session.meta.alias ? `Session Alias: ${session.meta.alias}` : '';
|
|
306
|
-
const workspaceLine = session.meta.workspace ? `Current Workspace: ${session.meta.workspace}` : '';
|
|
307
|
-
const enhancedSystemPrompt = `You are xiaozuoAssistant, a helpful AI assistant.
|
|
308
|
-
${sessionAlias}
|
|
309
|
-
${workspaceLine}
|
|
310
|
-
Here is some relevant context from your memory:
|
|
311
|
-
${context}`;
|
|
312
|
-
// We need to inject this into the Brain.
|
|
313
|
-
// Since Brain.processMessage takes history, let's just pass the history but we need to tell Brain to use this system prompt.
|
|
314
|
-
// We can modify Brain to accept a system prompt override.
|
|
315
|
-
// Or we can just create a new method in Brain or pass it as an argument.
|
|
316
|
-
// Let's update Brain.processMessage signature in next step. For now, let's just pass the history.
|
|
317
|
-
const ctx = {
|
|
318
|
-
sessionId,
|
|
319
|
-
history: session.messages,
|
|
309
|
+
const run = await memory.createRun(sessionId, content);
|
|
310
|
+
eventBus.emitEvent({
|
|
311
|
+
type: 'run_ack',
|
|
312
|
+
payload: { runId: run.id },
|
|
320
313
|
channel: channelName,
|
|
321
|
-
|
|
322
|
-
|
|
323
|
-
|
|
324
|
-
alias: session.meta.alias
|
|
325
|
-
}
|
|
326
|
-
};
|
|
327
|
-
const responseContent = await brain.processMessage(session.messages, content, enhancedSystemPrompt, ctx);
|
|
328
|
-
// 4. 记录 AI 响应
|
|
329
|
-
await memory.addMessage(sessionId, { role: 'assistant', content: responseContent, timestamp: Date.now() });
|
|
330
|
-
// 5. 发送响应事件
|
|
314
|
+
sessionId,
|
|
315
|
+
timestamp: Date.now()
|
|
316
|
+
});
|
|
331
317
|
eventBus.emitEvent({
|
|
332
|
-
type: '
|
|
333
|
-
payload: {
|
|
318
|
+
type: 'run_status',
|
|
319
|
+
payload: { runId: run.id, status: 'queued' },
|
|
334
320
|
channel: channelName,
|
|
335
|
-
sessionId
|
|
321
|
+
sessionId,
|
|
336
322
|
timestamp: Date.now()
|
|
337
323
|
});
|
|
324
|
+
taskQueue.enqueue({ run, channel: channelName });
|
|
338
325
|
}
|
|
339
326
|
catch (error) {
|
|
340
327
|
console.error('Error processing message:', error);
|
|
@@ -361,6 +348,20 @@ eventBus.onEvent('response', (event) => {
|
|
|
361
348
|
channel.send(sessionId, payload.content);
|
|
362
349
|
}
|
|
363
350
|
});
|
|
351
|
+
eventBus.onEvent('run_ack', (event) => {
|
|
352
|
+
const { sessionId, payload, channel: channelName } = event;
|
|
353
|
+
const channel = channels.find(c => c.name === channelName);
|
|
354
|
+
if (channel?.sendEvent) {
|
|
355
|
+
channel.sendEvent(sessionId, 'run_ack', payload);
|
|
356
|
+
}
|
|
357
|
+
});
|
|
358
|
+
eventBus.onEvent('run_status', (event) => {
|
|
359
|
+
const { sessionId, payload, channel: channelName } = event;
|
|
360
|
+
const channel = channels.find(c => c.name === channelName);
|
|
361
|
+
if (channel?.sendEvent) {
|
|
362
|
+
channel.sendEvent(sessionId, 'run_status', payload);
|
|
363
|
+
}
|
|
364
|
+
});
|
|
364
365
|
eventBus.onEvent('error', (event) => {
|
|
365
366
|
const { sessionId, payload, channel: channelName } = event;
|
|
366
367
|
const channel = channels.find(c => c.name === channelName);
|
package/package.json
CHANGED