@wu529778790/open-im 1.9.4-beta.5 → 1.9.4-beta.7
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/dingtalk/event-handler.js +2 -2
- package/dist/feishu/event-handler.js +30 -32
- package/dist/platform/handle-ai-request.d.ts +2 -0
- package/dist/platform/handle-ai-request.js +2 -1
- package/dist/platform/handle-text-flow.js +2 -1
- package/dist/qq/event-handler.js +4 -3
- package/dist/queue/request-queue.d.ts +1 -1
- package/dist/queue/request-queue.js +6 -2
- package/dist/queue/request-queue.test.js +23 -5
- package/dist/shared/ai-task.d.ts +2 -0
- package/dist/shared/ai-task.js +9 -0
- package/dist/telegram/event-handler.js +2 -2
- package/dist/wework/event-handler.js +2 -2
- package/dist/workbuddy/event-handler.js +4 -4
- package/package.json +1 -1
|
@@ -199,9 +199,9 @@ export function setupDingTalkHandlers(config, sessionManager) {
|
|
|
199
199
|
async function enqueuePrompt(userId, chatId, prompt, dingtalkTarget) {
|
|
200
200
|
const workDir = sessionManager.getWorkDir(userId);
|
|
201
201
|
const convId = sessionManager.getConvId(userId);
|
|
202
|
-
return requestQueue.enqueue(userId, convId, prompt, async (nextPrompt) => {
|
|
202
|
+
return requestQueue.enqueue(userId, convId, prompt, async (nextPrompt, signal) => {
|
|
203
203
|
senderCtx.dingtalkTarget = dingtalkTarget;
|
|
204
|
-
await handleAIRequest({ userId, chatId, prompt: nextPrompt, workDir, convId });
|
|
204
|
+
await handleAIRequest({ userId, chatId, prompt: nextPrompt, workDir, convId, signal });
|
|
205
205
|
});
|
|
206
206
|
}
|
|
207
207
|
async function handleEvent(data) {
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { resolvePlatformAiCommand } from '../config.js';
|
|
2
|
-
import { sendTextReply, startTypingLoop, sendImageReply, sendThinkingCard, streamContentUpdate, } from './message-sender.js';
|
|
2
|
+
import { sendTextReply, startTypingLoop, sendImageReply, sendThinkingCard, streamContentUpdate, sendFinalCards, sendErrorCard, } from './message-sender.js';
|
|
3
3
|
import { buildCardV2 } from './card-builder.js';
|
|
4
4
|
import { disableStreaming, updateCardFull, destroySession } from './cardkit-manager.js';
|
|
5
5
|
import { CARDKIT_THROTTLE_MS } from '../constants.js';
|
|
@@ -172,33 +172,6 @@ export function setupFeishuHandlers(config, sessionManager) {
|
|
|
172
172
|
await sendImageReply(chatId, imagePath);
|
|
173
173
|
},
|
|
174
174
|
};
|
|
175
|
-
// Feishu-specific task callbacks for CardKit streaming
|
|
176
|
-
const feishuTaskCallbacks = {
|
|
177
|
-
streamUpdate: async (content, toolNote) => {
|
|
178
|
-
if (consecutiveStreamErrors >= MAX_STREAM_ERRORS)
|
|
179
|
-
return; // Stop trying
|
|
180
|
-
const note = buildProgressNote(toolNote);
|
|
181
|
-
// Note: cardId is passed via taskKey format: userId:cardId
|
|
182
|
-
const cardId = content; // Placeholder, will be overridden via context
|
|
183
|
-
streamContentUpdate(cardId, content, note).then(() => {
|
|
184
|
-
consecutiveStreamErrors = 0;
|
|
185
|
-
}).catch((e) => {
|
|
186
|
-
consecutiveStreamErrors++;
|
|
187
|
-
if (consecutiveStreamErrors >= MAX_STREAM_ERRORS) {
|
|
188
|
-
log.warn(`Stream update failed ${consecutiveStreamErrors} times consecutively, giving up: ${e?.message ?? e}`);
|
|
189
|
-
}
|
|
190
|
-
else {
|
|
191
|
-
log.debug('Stream update failed (will retry on next update):', e?.message ?? e);
|
|
192
|
-
}
|
|
193
|
-
});
|
|
194
|
-
},
|
|
195
|
-
sendComplete: async (content, note) => {
|
|
196
|
-
// Will be customized via context
|
|
197
|
-
},
|
|
198
|
-
sendError: async (error) => {
|
|
199
|
-
// Will be customized via context
|
|
200
|
-
},
|
|
201
|
-
};
|
|
202
175
|
// Create platform-specific AI request handler
|
|
203
176
|
const handleAIRequest = createPlatformAIRequestHandler({
|
|
204
177
|
platform: 'feishu',
|
|
@@ -214,6 +187,31 @@ export function setupFeishuHandlers(config, sessionManager) {
|
|
|
214
187
|
const resetCard = buildCardV2({ content: content || '...', status: 'streaming', toolName: resolvePlatformAiCommand(config, 'feishu') }, cardId);
|
|
215
188
|
updateCardFull(cardId, resetCard).catch((e) => log.warn('Thinking→text transition update failed:', e?.message ?? e));
|
|
216
189
|
},
|
|
190
|
+
taskCallbacksFactory: ({ msgId: cardId, toolId }) => ({
|
|
191
|
+
streamUpdate: async (content, toolNote) => {
|
|
192
|
+
if (consecutiveStreamErrors >= MAX_STREAM_ERRORS)
|
|
193
|
+
return;
|
|
194
|
+
const note = buildProgressNote(toolNote);
|
|
195
|
+
streamContentUpdate(cardId, content, note).then(() => {
|
|
196
|
+
consecutiveStreamErrors = 0;
|
|
197
|
+
}).catch((e) => {
|
|
198
|
+
consecutiveStreamErrors++;
|
|
199
|
+
if (consecutiveStreamErrors >= MAX_STREAM_ERRORS) {
|
|
200
|
+
log.warn(`Stream update failed ${consecutiveStreamErrors} times consecutively, giving up: ${e?.message ?? e}`);
|
|
201
|
+
}
|
|
202
|
+
else {
|
|
203
|
+
log.debug('Stream update failed (will retry on next update):', e?.message ?? e);
|
|
204
|
+
}
|
|
205
|
+
});
|
|
206
|
+
},
|
|
207
|
+
sendComplete: async (content, note, thinkingText) => {
|
|
208
|
+
const aiTool = resolvePlatformAiCommand(config, 'feishu');
|
|
209
|
+
await sendFinalCards('', cardId, cardId, content, note ?? '', thinkingText, aiTool);
|
|
210
|
+
},
|
|
211
|
+
sendError: async (error) => {
|
|
212
|
+
await sendErrorCard(cardId, error);
|
|
213
|
+
},
|
|
214
|
+
}),
|
|
217
215
|
});
|
|
218
216
|
/**
|
|
219
217
|
* 解析 action value(兼容对象、JSON 字符串)
|
|
@@ -459,8 +457,8 @@ export function setupFeishuHandlers(config, sessionManager) {
|
|
|
459
457
|
});
|
|
460
458
|
const work = sessionManager.getWorkDir(senderId);
|
|
461
459
|
const convId = sessionManager.getConvId(senderId);
|
|
462
|
-
const enqueueResult = ctx.requestQueue.enqueue(senderId, convId, prompt, async (p) => {
|
|
463
|
-
await handleAIRequest({ userId: senderId, chatId, prompt: p, workDir: work, convId, replyToMessageId: messageId });
|
|
460
|
+
const enqueueResult = ctx.requestQueue.enqueue(senderId, convId, prompt, async (p, signal) => {
|
|
461
|
+
await handleAIRequest({ userId: senderId, chatId, prompt: p, workDir: work, convId, replyToMessageId: messageId, signal });
|
|
464
462
|
});
|
|
465
463
|
if (enqueueResult === 'rejected') {
|
|
466
464
|
sendTextReply(chatId, 'Request queue is full. Please try again later.').catch((sendErr) => {
|
|
@@ -507,8 +505,8 @@ export function setupFeishuHandlers(config, sessionManager) {
|
|
|
507
505
|
});
|
|
508
506
|
const workDir = sessionManager.getWorkDir(senderId);
|
|
509
507
|
const convId = sessionManager.getConvId(senderId);
|
|
510
|
-
const enqueueResult = ctx.requestQueue.enqueue(senderId, convId, prompt, async (p) => {
|
|
511
|
-
await handleAIRequest({ userId: senderId, chatId, prompt: p, workDir, convId, replyToMessageId: messageId });
|
|
508
|
+
const enqueueResult = ctx.requestQueue.enqueue(senderId, convId, prompt, async (p, signal) => {
|
|
509
|
+
await handleAIRequest({ userId: senderId, chatId, prompt: p, workDir, convId, replyToMessageId: messageId, signal });
|
|
512
510
|
});
|
|
513
511
|
if (enqueueResult === 'rejected') {
|
|
514
512
|
sendTextReply(chatId, 'Request queue is full. Please try again later.').catch((sendErr) => {
|
|
@@ -92,6 +92,8 @@ export interface HandleAIRequestParams {
|
|
|
92
92
|
workDir: string;
|
|
93
93
|
convId?: string;
|
|
94
94
|
replyToMessageId?: string;
|
|
95
|
+
/** AbortSignal from the request queue; fires on task timeout */
|
|
96
|
+
signal?: AbortSignal;
|
|
95
97
|
}
|
|
96
98
|
/**
|
|
97
99
|
* Creates a platform-specific handleAIRequest function.
|
|
@@ -29,7 +29,7 @@ const log = createLogger('PlatformAI');
|
|
|
29
29
|
export function createPlatformAIRequestHandler(deps) {
|
|
30
30
|
const { platform, config, sessionManager, sender, throttleMs, runningTasks, minContentDeltaChars, taskKeyBuilder, onThinkingToText, extraInit, taskCallbacks, taskCallbacksFactory, } = deps;
|
|
31
31
|
async function handleAIRequest(params) {
|
|
32
|
-
const { userId, chatId, prompt, workDir, convId, replyToMessageId } = params;
|
|
32
|
+
const { userId, chatId, prompt, workDir, convId, replyToMessageId, signal } = params;
|
|
33
33
|
log.info(`[${platform}] AI request: userId=${userId}, chatId=${chatId}, promptLength=${prompt.length}`);
|
|
34
34
|
// 1. Resolve AI command and adapter
|
|
35
35
|
const aiCommand = resolvePlatformAiCommand(config, platform);
|
|
@@ -156,6 +156,7 @@ export function createPlatformAIRequestHandler(deps) {
|
|
|
156
156
|
convId,
|
|
157
157
|
platform,
|
|
158
158
|
taskKey,
|
|
159
|
+
signal,
|
|
159
160
|
}, prompt, toolAdapter, mergedCallbacks);
|
|
160
161
|
}
|
|
161
162
|
catch (err) {
|
|
@@ -92,7 +92,7 @@ export async function handleTextFlow(params) {
|
|
|
92
92
|
const { requestQueue } = ctx;
|
|
93
93
|
const workDir = workDirOverride;
|
|
94
94
|
const convId = convIdOverride;
|
|
95
|
-
const enqueueResult = requestQueue.enqueue(userId, convId ?? '', text, async (prompt) => {
|
|
95
|
+
const enqueueResult = requestQueue.enqueue(userId, convId ?? '', text, async (prompt, signal) => {
|
|
96
96
|
await handleAIRequest({
|
|
97
97
|
userId,
|
|
98
98
|
chatId,
|
|
@@ -100,6 +100,7 @@ export async function handleTextFlow(params) {
|
|
|
100
100
|
workDir: workDir ?? '',
|
|
101
101
|
convId,
|
|
102
102
|
replyToMessageId,
|
|
103
|
+
signal,
|
|
103
104
|
});
|
|
104
105
|
});
|
|
105
106
|
if (enqueueResult === 'rejected') {
|
package/dist/qq/event-handler.js
CHANGED
|
@@ -167,7 +167,7 @@ export function setupQQHandlers(config, sessionManager) {
|
|
|
167
167
|
});
|
|
168
168
|
// Wrap factory handleAIRequest to match ClaudeRequestHandler signature
|
|
169
169
|
// (used by commandHandler.dispatch and handleTextFlow)
|
|
170
|
-
async function handleAIRequest(userId, chatId, prompt, workDir, convId, _threadCtx, replyToMessageId) {
|
|
170
|
+
async function handleAIRequest(userId, chatId, prompt, workDir, convId, _threadCtx, replyToMessageId, signal) {
|
|
171
171
|
await factoryHandleAIRequest({
|
|
172
172
|
userId,
|
|
173
173
|
chatId,
|
|
@@ -175,6 +175,7 @@ export function setupQQHandlers(config, sessionManager) {
|
|
|
175
175
|
workDir,
|
|
176
176
|
convId,
|
|
177
177
|
replyToMessageId,
|
|
178
|
+
signal,
|
|
178
179
|
});
|
|
179
180
|
}
|
|
180
181
|
function cleanupRecentEvents(now) {
|
|
@@ -264,8 +265,8 @@ export function setupQQHandlers(config, sessionManager) {
|
|
|
264
265
|
setActiveChatId("qq", chatId);
|
|
265
266
|
setChatUser(chatId, userId, "qq");
|
|
266
267
|
// Enqueue attachment prompt
|
|
267
|
-
const enqueueResult = requestQueue.enqueue(userId, convId ?? '', attachmentPrompt, async (prompt) => {
|
|
268
|
-
await handleAIRequest(userId, chatId, prompt, workDir, convId, undefined, event.id);
|
|
268
|
+
const enqueueResult = requestQueue.enqueue(userId, convId ?? '', attachmentPrompt, async (prompt, signal) => {
|
|
269
|
+
await handleAIRequest(userId, chatId, prompt, workDir, convId, undefined, event.id, signal);
|
|
269
270
|
});
|
|
270
271
|
if (enqueueResult === "rejected") {
|
|
271
272
|
await sendTextReply(chatId, "Request queue is full. Please try again later.");
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
export type EnqueueResult = 'running' | 'queued' | 'rejected';
|
|
2
2
|
export declare class RequestQueue {
|
|
3
3
|
private queues;
|
|
4
|
-
enqueue(userId: string, convId: string, prompt: string, execute: (prompt: string) => Promise<void>): EnqueueResult;
|
|
4
|
+
enqueue(userId: string, convId: string, prompt: string, execute: (prompt: string, signal: AbortSignal) => Promise<void>): EnqueueResult;
|
|
5
5
|
/** 清除指定用户会话的所有排队任务(不中止正在运行的任务) */
|
|
6
6
|
clear(userId: string, convId: string): number;
|
|
7
7
|
private run;
|
|
@@ -35,12 +35,16 @@ export class RequestQueue {
|
|
|
35
35
|
return cleared;
|
|
36
36
|
}
|
|
37
37
|
async run(key, prompt, execute) {
|
|
38
|
+
const controller = new AbortController();
|
|
38
39
|
let timer;
|
|
39
40
|
try {
|
|
40
41
|
const timeoutPromise = new Promise((_, reject) => {
|
|
41
|
-
timer = setTimeout(() =>
|
|
42
|
+
timer = setTimeout(() => {
|
|
43
|
+
controller.abort();
|
|
44
|
+
reject(new Error(`Task timed out after ${TASK_TIMEOUT_MS / 1000}s`));
|
|
45
|
+
}, TASK_TIMEOUT_MS);
|
|
42
46
|
});
|
|
43
|
-
await Promise.race([execute(prompt), timeoutPromise]);
|
|
47
|
+
await Promise.race([execute(prompt, controller.signal), timeoutPromise]);
|
|
44
48
|
}
|
|
45
49
|
catch (err) {
|
|
46
50
|
log.error(`Error executing task for ${key}:`, err);
|
|
@@ -8,7 +8,7 @@ describe('RequestQueue', () => {
|
|
|
8
8
|
expect(result).toBe('running');
|
|
9
9
|
// Allow microtask queue to settle
|
|
10
10
|
await new Promise((r) => setTimeout(r, 10));
|
|
11
|
-
expect(execute).toHaveBeenCalledWith('hello');
|
|
11
|
+
expect(execute).toHaveBeenCalledWith('hello', expect.any(AbortSignal));
|
|
12
12
|
});
|
|
13
13
|
it('returns "queued" when a task is already running', () => {
|
|
14
14
|
const queue = new RequestQueue();
|
|
@@ -37,11 +37,11 @@ describe('RequestQueue', () => {
|
|
|
37
37
|
queue.enqueue('user1', 'conv1', 'first', execute);
|
|
38
38
|
queue.enqueue('user1', 'conv1', 'second', execute);
|
|
39
39
|
expect(execute).toHaveBeenCalledTimes(1);
|
|
40
|
-
expect(execute).toHaveBeenCalledWith('first');
|
|
40
|
+
expect(execute).toHaveBeenCalledWith('first', expect.any(AbortSignal));
|
|
41
41
|
resolveFirst();
|
|
42
42
|
await new Promise((r) => setTimeout(r, 20));
|
|
43
43
|
expect(execute).toHaveBeenCalledTimes(2);
|
|
44
|
-
expect(execute).toHaveBeenCalledWith('second');
|
|
44
|
+
expect(execute).toHaveBeenCalledWith('second', expect.any(AbortSignal));
|
|
45
45
|
});
|
|
46
46
|
it('isolates queues per user:convId', () => {
|
|
47
47
|
const queue = new RequestQueue();
|
|
@@ -90,7 +90,25 @@ describe('RequestQueue', () => {
|
|
|
90
90
|
queue.enqueue('user1', 'conv1', 'second', execute);
|
|
91
91
|
await new Promise((r) => setTimeout(r, 20));
|
|
92
92
|
expect(execute).toHaveBeenCalledTimes(2);
|
|
93
|
-
expect(execute).toHaveBeenCalledWith('first');
|
|
94
|
-
expect(execute).toHaveBeenCalledWith('second');
|
|
93
|
+
expect(execute).toHaveBeenCalledWith('first', expect.any(AbortSignal));
|
|
94
|
+
expect(execute).toHaveBeenCalledWith('second', expect.any(AbortSignal));
|
|
95
|
+
});
|
|
96
|
+
it('aborts the AbortSignal on timeout', async () => {
|
|
97
|
+
vi.useFakeTimers();
|
|
98
|
+
const queue = new RequestQueue();
|
|
99
|
+
let receivedSignal;
|
|
100
|
+
const execute = vi.fn().mockImplementation(async (_prompt, signal) => {
|
|
101
|
+
receivedSignal = signal;
|
|
102
|
+
// Never resolve — simulates a stuck task
|
|
103
|
+
await new Promise(() => { });
|
|
104
|
+
});
|
|
105
|
+
queue.enqueue('user1', 'conv1', 'hello', execute);
|
|
106
|
+
await vi.advanceTimersByTimeAsync(10);
|
|
107
|
+
expect(receivedSignal?.aborted).toBe(false);
|
|
108
|
+
// Advance past timeout
|
|
109
|
+
vi.advanceTimersByTime(10 * 60 * 1000 + 1);
|
|
110
|
+
await vi.advanceTimersByTimeAsync(100);
|
|
111
|
+
expect(receivedSignal?.aborted).toBe(true);
|
|
112
|
+
vi.useRealTimers();
|
|
95
113
|
});
|
|
96
114
|
});
|
package/dist/shared/ai-task.d.ts
CHANGED
|
@@ -17,6 +17,8 @@ export interface TaskContext {
|
|
|
17
17
|
threadId?: string;
|
|
18
18
|
platform: string;
|
|
19
19
|
taskKey: string;
|
|
20
|
+
/** AbortSignal from the request queue; fires on task timeout to abort the running SDK session */
|
|
21
|
+
signal?: AbortSignal;
|
|
20
22
|
}
|
|
21
23
|
export interface TaskAdapter {
|
|
22
24
|
streamUpdate(content: string, toolNote?: string): void;
|
package/dist/shared/ai-task.js
CHANGED
|
@@ -255,5 +255,14 @@ export function runAITask(deps, ctx, prompt, toolAdapter, platformAdapter) {
|
|
|
255
255
|
return;
|
|
256
256
|
}
|
|
257
257
|
platformAdapter.onTaskReady(taskState);
|
|
258
|
+
// Wire queue abort signal to the running task's abort handle
|
|
259
|
+
if (ctx.signal) {
|
|
260
|
+
if (ctx.signal.aborted) {
|
|
261
|
+
taskState.handle.abort();
|
|
262
|
+
}
|
|
263
|
+
else {
|
|
264
|
+
ctx.signal.addEventListener('abort', () => taskState.handle.abort(), { once: true });
|
|
265
|
+
}
|
|
266
|
+
}
|
|
258
267
|
});
|
|
259
268
|
}
|
|
@@ -254,8 +254,8 @@ export function setupTelegramHandlers(bot, config, sessionManager) {
|
|
|
254
254
|
});
|
|
255
255
|
const workDir = sessionManager.getWorkDir(userId);
|
|
256
256
|
const convId = sessionManager.getConvId(userId);
|
|
257
|
-
return requestQueue.enqueue(userId, convId, prompt, async (nextPrompt) => {
|
|
258
|
-
await handleAIRequest({ userId, chatId, prompt: nextPrompt, workDir, convId });
|
|
257
|
+
return requestQueue.enqueue(userId, convId, prompt, async (nextPrompt, signal) => {
|
|
258
|
+
await handleAIRequest({ userId, chatId, prompt: nextPrompt, workDir, convId, signal });
|
|
259
259
|
});
|
|
260
260
|
}
|
|
261
261
|
bot.on("callback_query", async (ctx) => {
|
|
@@ -240,8 +240,8 @@ export function setupWeWorkHandlers(config, sessionManager) {
|
|
|
240
240
|
async function enqueuePrompt(userId, chatId, prompt, reqId) {
|
|
241
241
|
const workDir = sessionManager.getWorkDir(userId);
|
|
242
242
|
const convId = sessionManager.getConvId(userId);
|
|
243
|
-
const enqueueResult = ctx.requestQueue.enqueue(userId, convId, prompt, async (nextPrompt) => {
|
|
244
|
-
await handleAIRequest({ userId, chatId, prompt: nextPrompt, workDir, convId, replyToMessageId: undefined });
|
|
243
|
+
const enqueueResult = ctx.requestQueue.enqueue(userId, convId, prompt, async (nextPrompt, signal) => {
|
|
244
|
+
await handleAIRequest({ userId, chatId, prompt: nextPrompt, workDir, convId, replyToMessageId: undefined, signal });
|
|
245
245
|
});
|
|
246
246
|
if (enqueueResult === 'rejected') {
|
|
247
247
|
await sendTextReply(chatId, 'Request queue is full. Please try again later.', reqId);
|
|
@@ -83,7 +83,7 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
|
|
|
83
83
|
extraInit,
|
|
84
84
|
});
|
|
85
85
|
// WorkBuddy-specific wrapper that captures msgId
|
|
86
|
-
async function handleAIRequest(userId, chatId, msgId, prompt, workDir, convId) {
|
|
86
|
+
async function handleAIRequest(userId, chatId, msgId, prompt, workDir, convId, signal) {
|
|
87
87
|
log.info(`[AI_REQUEST] userId=${userId}, chatId=${chatId}, msgId=${msgId}, promptLength=${prompt.length}`);
|
|
88
88
|
// WorkBuddy uses incoming msgId as taskKey (no thinking message needed)
|
|
89
89
|
const taskKey = `${userId}:${msgId}`;
|
|
@@ -104,7 +104,7 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
|
|
|
104
104
|
log.info(`[handleAIRequest] Running ${aiCommand} for user ${userId}, sessionId=${sessionId ?? 'new'}`);
|
|
105
105
|
// Set up task tracking key mapping
|
|
106
106
|
taskKeyByChatId.set(chatId, taskKey);
|
|
107
|
-
await runAITask({ config, sessionManager }, { userId, chatId, workDir, sessionId, convId, platform: 'workbuddy', taskKey }, prompt, toolAdapter, {
|
|
107
|
+
await runAITask({ config, sessionManager }, { userId, chatId, workDir, sessionId, convId, platform: 'workbuddy', taskKey, signal }, prompt, toolAdapter, {
|
|
108
108
|
throttleMs: WORKBUDDY_THROTTLE_MS,
|
|
109
109
|
streamUpdate: async (content) => {
|
|
110
110
|
log.debug(`Stream update (not sent): ${content.substring(0, 50)}...`);
|
|
@@ -172,9 +172,9 @@ export function setupWorkBuddyHandlers(config, sessionManager) {
|
|
|
172
172
|
if (!text) {
|
|
173
173
|
return;
|
|
174
174
|
}
|
|
175
|
-
const enqueueResult = ctx.requestQueue.enqueue(userId, convId, text, async (nextPrompt) => {
|
|
175
|
+
const enqueueResult = ctx.requestQueue.enqueue(userId, convId, text, async (nextPrompt, signal) => {
|
|
176
176
|
log.info(`Executing AI request for: ${nextPrompt}`);
|
|
177
|
-
await handleAIRequest(userId, chatId, msgId, nextPrompt, workDir, convId);
|
|
177
|
+
await handleAIRequest(userId, chatId, msgId, nextPrompt, workDir, convId, signal);
|
|
178
178
|
});
|
|
179
179
|
if (enqueueResult === 'rejected') {
|
|
180
180
|
await sendErrorReply(null, chatId, 'Request queue is full. Please try again later.', msgId);
|