@multiplayer-app/ai-agent-node 0.1.0-beta.73 → 0.1.0-beta.75
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/cjs/helpers/AIHelper.cjs +5 -3
- package/dist/cjs/helpers/AIHelper.cjs.map +1 -1
- package/dist/cjs/helpers/AIHelper.d.ts +2 -1
- package/dist/cjs/helpers/AIHelper.d.ts.map +1 -1
- package/dist/cjs/processors/ActivityProcessor.cjs +16 -2
- package/dist/cjs/processors/ActivityProcessor.cjs.map +1 -1
- package/dist/cjs/processors/ActivityProcessor.d.ts +2 -0
- package/dist/cjs/processors/ActivityProcessor.d.ts.map +1 -1
- package/dist/cjs/processors/ActivityProcessor.test.cjs +63 -8
- package/dist/cjs/processors/ActivityProcessor.test.cjs.map +1 -1
- package/dist/cjs/processors/AgentProcessor.cjs +4 -3
- package/dist/cjs/processors/AgentProcessor.cjs.map +1 -1
- package/dist/cjs/processors/AgentProcessor.d.ts.map +1 -1
- package/dist/cjs/processors/ChatProcessor.cjs +330 -32
- package/dist/cjs/processors/ChatProcessor.cjs.map +1 -1
- package/dist/cjs/processors/ChatProcessor.d.ts +12 -2
- package/dist/cjs/processors/ChatProcessor.d.ts.map +1 -1
- package/dist/cjs/processors/ChatProcessor.test.cjs +41 -0
- package/dist/cjs/processors/ChatProcessor.test.cjs.map +1 -1
- package/dist/cjs/services/AIService.cjs +25 -20
- package/dist/cjs/services/AIService.cjs.map +1 -1
- package/dist/cjs/services/AIService.d.ts.map +1 -1
- package/dist/cjs/store/AgentStore.cjs +11 -0
- package/dist/cjs/store/AgentStore.cjs.map +1 -1
- package/dist/cjs/store/AgentStore.d.ts +1 -0
- package/dist/cjs/store/AgentStore.d.ts.map +1 -1
- package/dist/cjs/store/ConfigStore.cjs +7 -0
- package/dist/cjs/store/ConfigStore.cjs.map +1 -1
- package/dist/cjs/store/ConfigStore.d.ts +1 -0
- package/dist/cjs/store/ConfigStore.d.ts.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/cjs/utils/utils.cjs +31 -0
- package/dist/cjs/utils/utils.cjs.map +1 -0
- package/dist/cjs/utils/utils.d.ts +5 -0
- package/dist/cjs/utils/utils.d.ts.map +1 -0
- package/dist/esm/helpers/AIHelper.d.ts +2 -1
- package/dist/esm/helpers/AIHelper.d.ts.map +1 -1
- package/dist/esm/helpers/AIHelper.js +5 -3
- package/dist/esm/helpers/AIHelper.js.map +1 -1
- package/dist/esm/processors/ActivityProcessor.d.ts +2 -0
- package/dist/esm/processors/ActivityProcessor.d.ts.map +1 -1
- package/dist/esm/processors/ActivityProcessor.js +16 -2
- package/dist/esm/processors/ActivityProcessor.js.map +1 -1
- package/dist/esm/processors/ActivityProcessor.test.js +63 -8
- package/dist/esm/processors/ActivityProcessor.test.js.map +1 -1
- package/dist/esm/processors/AgentProcessor.d.ts.map +1 -1
- package/dist/esm/processors/AgentProcessor.js +4 -3
- package/dist/esm/processors/AgentProcessor.js.map +1 -1
- package/dist/esm/processors/ChatProcessor.d.ts +12 -2
- package/dist/esm/processors/ChatProcessor.d.ts.map +1 -1
- package/dist/esm/processors/ChatProcessor.js +330 -32
- package/dist/esm/processors/ChatProcessor.js.map +1 -1
- package/dist/esm/processors/ChatProcessor.test.js +41 -0
- package/dist/esm/processors/ChatProcessor.test.js.map +1 -1
- package/dist/esm/services/AIService.d.ts.map +1 -1
- package/dist/esm/services/AIService.js +25 -20
- package/dist/esm/services/AIService.js.map +1 -1
- package/dist/esm/store/AgentStore.d.ts +1 -0
- package/dist/esm/store/AgentStore.d.ts.map +1 -1
- package/dist/esm/store/AgentStore.js +11 -0
- package/dist/esm/store/AgentStore.js.map +1 -1
- package/dist/esm/store/ConfigStore.d.ts +1 -0
- package/dist/esm/store/ConfigStore.d.ts.map +1 -1
- package/dist/esm/store/ConfigStore.js +7 -0
- package/dist/esm/store/ConfigStore.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/dist/esm/utils/utils.d.ts +5 -0
- package/dist/esm/utils/utils.d.ts.map +1 -0
- package/dist/esm/utils/utils.js +26 -0
- package/dist/esm/utils/utils.js.map +1 -0
- package/package.json +3 -3
|
@@ -1,11 +1,11 @@
|
|
|
1
|
-
import { AgentStatus, AgentToolCallStatus, ChatType, MessageRole, SortOrder, StreamChunkType
|
|
1
|
+
import { ActivityOperationName, AgentStatus, AgentToolCallStatus, AgentToolType, ChatType, MessageRole, SortOrder, StreamChunkType } from '@multiplayer-app/ai-agent-types';
|
|
2
2
|
import { z } from 'zod';
|
|
3
|
-
import {
|
|
4
|
-
import {
|
|
5
|
-
import { ContextLimiter } from '../helpers';
|
|
3
|
+
import { AgentProcessEventType, ConfigStore } from '../store';
|
|
4
|
+
import { AIHelper, ContextLimiter, FileHelper } from '../helpers';
|
|
6
5
|
import { PassThrough } from 'stream';
|
|
7
6
|
import { logger } from '../libs/logger';
|
|
8
|
-
import {
|
|
7
|
+
import { AgentSessionKind } from "@multiplayer-app/ai-agent-types";
|
|
8
|
+
import { getAgentToolName } from "../utils/utils";
|
|
9
9
|
export class ChatProcessor {
|
|
10
10
|
aiHelper;
|
|
11
11
|
chatRepository;
|
|
@@ -50,6 +50,117 @@ export class ChatProcessor {
|
|
|
50
50
|
}
|
|
51
51
|
return payload.tenants ?? {};
|
|
52
52
|
}
|
|
53
|
+
async updateChatAndEmit(chat, update, excludeSocketId) {
|
|
54
|
+
await this.chatRepository.update(chat.id, update);
|
|
55
|
+
const updatedChat = await this.chatRepository.findById(chat.id);
|
|
56
|
+
const chatToEmit = updatedChat ?? { ...chat, ...update };
|
|
57
|
+
this.socketService.emitChatUpdate(chat.userId, chatToEmit, excludeSocketId);
|
|
58
|
+
return chatToEmit;
|
|
59
|
+
}
|
|
60
|
+
async startSubagentProcess(params) {
|
|
61
|
+
const childChat = await this.chatRepository.create({
|
|
62
|
+
title: `${params.subAgentConfig.name} subagent`,
|
|
63
|
+
type: ChatType.Agent,
|
|
64
|
+
status: AgentStatus.Streaming,
|
|
65
|
+
sessionKind: AgentSessionKind.SUBAGENT,
|
|
66
|
+
parentChatId: params.parentChat.id,
|
|
67
|
+
parentMessageId: params.parentMessage.id,
|
|
68
|
+
parentToolCallId: params.parentToolCallId,
|
|
69
|
+
contextKey: params.parentChat.contextKey,
|
|
70
|
+
userId: params.parentChat.userId,
|
|
71
|
+
model: params.subAgentConfig.defaultModel || params.parentChat.model,
|
|
72
|
+
});
|
|
73
|
+
try {
|
|
74
|
+
const parentToolCall = params.parentMessage.toolCalls?.find((toolCall) => toolCall.id === params.parentToolCallId);
|
|
75
|
+
if (parentToolCall) {
|
|
76
|
+
parentToolCall.input = {
|
|
77
|
+
...(parentToolCall.input ?? {}),
|
|
78
|
+
subagentChatId: childChat.id,
|
|
79
|
+
};
|
|
80
|
+
parentToolCall.subagentChatId = childChat.id;
|
|
81
|
+
const updatedParentMessage = await this.messageRepository.update(params.parentMessage.id, {
|
|
82
|
+
toolCalls: params.parentMessage.toolCalls,
|
|
83
|
+
});
|
|
84
|
+
if (updatedParentMessage) {
|
|
85
|
+
this.socketService.emitMessageUpdate(params.parentChat.userId, updatedParentMessage);
|
|
86
|
+
}
|
|
87
|
+
}
|
|
88
|
+
this.socketService.emitChatUpdate(params.parentChat.userId, childChat);
|
|
89
|
+
const executionTenants = this.getExecutionTenants(params);
|
|
90
|
+
const abortController = this.agentStore.registerSubAgentProcess(childChat.id, params.parentChat.id);
|
|
91
|
+
const userMessage = await this.createMessage(childChat, MessageRole.User, {
|
|
92
|
+
content: JSON.stringify(params.input),
|
|
93
|
+
agentName: undefined
|
|
94
|
+
});
|
|
95
|
+
await this.agentStore.shareAgentProcessEvent(childChat.id, {
|
|
96
|
+
type: AgentProcessEventType.Update,
|
|
97
|
+
data: userMessage
|
|
98
|
+
});
|
|
99
|
+
let assistantMessage = await this.createMessage(childChat, MessageRole.Assistant, {
|
|
100
|
+
content: '',
|
|
101
|
+
agentName: params.subAgentConfig.name
|
|
102
|
+
});
|
|
103
|
+
const parentActivity = await this.activityRepository.create({
|
|
104
|
+
ownerId: params.parentChat.userId,
|
|
105
|
+
groupId: params.parentChat.id,
|
|
106
|
+
name: ActivityOperationName.SUBAGENT,
|
|
107
|
+
tenants: executionTenants,
|
|
108
|
+
sourceId: assistantMessage.id,
|
|
109
|
+
sourceType: 'AgentMessage',
|
|
110
|
+
metadata: {
|
|
111
|
+
modelId: this.aiHelper.getLanguageModelId(childChat.model),
|
|
112
|
+
agentOptions: {
|
|
113
|
+
name: params.subAgentConfig.name,
|
|
114
|
+
modelId: this.aiHelper.getLanguageModelId(params.subAgentConfig.defaultModel),
|
|
115
|
+
temperature: params.subAgentConfig.temperature,
|
|
116
|
+
maxOutputTokens: params.subAgentConfig.maxOutputTokens,
|
|
117
|
+
topP: params.subAgentConfig.topP,
|
|
118
|
+
topK: params.subAgentConfig.topK,
|
|
119
|
+
presencePenalty: params.subAgentConfig.presencePenalty,
|
|
120
|
+
frequencyPenalty: params.subAgentConfig.frequencyPenalty,
|
|
121
|
+
stopSequences: params.subAgentConfig.stopSequences,
|
|
122
|
+
seed: params.subAgentConfig.seed,
|
|
123
|
+
},
|
|
124
|
+
},
|
|
125
|
+
});
|
|
126
|
+
const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
|
|
127
|
+
activity: parentActivity.id
|
|
128
|
+
});
|
|
129
|
+
const agentOptions = this.aiHelper.getAgentOptionsFromConfig(params.subAgentConfig, params.context);
|
|
130
|
+
agentOptions.onStepFinish = (stepResult) => {
|
|
131
|
+
return this.storeStepActivity({
|
|
132
|
+
chat: params.parentChat,
|
|
133
|
+
parentId: assistantMessage.activity,
|
|
134
|
+
stepResult,
|
|
135
|
+
name: ActivityOperationName.STEP_FINISHED,
|
|
136
|
+
sourceId: assistantMessage.id,
|
|
137
|
+
sourceType: 'AgentMessage',
|
|
138
|
+
tenants: executionTenants,
|
|
139
|
+
});
|
|
140
|
+
};
|
|
141
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(childChat.userId, agentOptions.name);
|
|
142
|
+
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
143
|
+
await this.streamMessageStep({
|
|
144
|
+
chat: childChat,
|
|
145
|
+
existingMessages: [userMessage, updatedAssistantMessage],
|
|
146
|
+
assistantMessage: updatedAssistantMessage,
|
|
147
|
+
agentOptions,
|
|
148
|
+
signal: abortController.signal,
|
|
149
|
+
tenants: executionTenants,
|
|
150
|
+
executionContext: params.executionContext,
|
|
151
|
+
context: params.context,
|
|
152
|
+
});
|
|
153
|
+
}
|
|
154
|
+
catch (error) {
|
|
155
|
+
await this.storeSubagentResponse({
|
|
156
|
+
subagentChat: childChat,
|
|
157
|
+
content: undefined,
|
|
158
|
+
error: error,
|
|
159
|
+
context: params.context,
|
|
160
|
+
executionContext: params.executionContext,
|
|
161
|
+
});
|
|
162
|
+
}
|
|
163
|
+
}
|
|
53
164
|
mergeMissingUsageFields(target, source) {
|
|
54
165
|
for (const [key, sourceValue] of Object.entries(source)) {
|
|
55
166
|
const targetValue = target[key];
|
|
@@ -121,8 +232,12 @@ export class ChatProcessor {
|
|
|
121
232
|
}
|
|
122
233
|
}
|
|
123
234
|
async listChats(params) {
|
|
124
|
-
// Build filter object from params
|
|
125
|
-
|
|
235
|
+
// Build filter object from params. Always exclude subagent chats:
|
|
236
|
+
// the repository translates ROOT into `sessionKind !== SUBAGENT`, which
|
|
237
|
+
// also includes legacy chats where the field is unset.
|
|
238
|
+
const filter = {
|
|
239
|
+
sessionKind: AgentSessionKind.ROOT
|
|
240
|
+
};
|
|
126
241
|
if (params?.userId) {
|
|
127
242
|
filter.userId = params.userId;
|
|
128
243
|
}
|
|
@@ -171,15 +286,48 @@ export class ChatProcessor {
|
|
|
171
286
|
return this.messageRepository.findByChatIdPaginated(chatId, options);
|
|
172
287
|
}
|
|
173
288
|
async deleteChat(chatId) {
|
|
289
|
+
// Collect all subagent descendants BEFORE removing the parent, otherwise the
|
|
290
|
+
// `parentChatId` link is severed and we'd orphan the subtree. Subagents can
|
|
291
|
+
// themselves spawn subagents, so traversal must be recursive.
|
|
292
|
+
const descendantIds = await this.collectDescendantChatIds(chatId);
|
|
174
293
|
const deleted = await this.chatRepository.delete(chatId);
|
|
175
294
|
if (!deleted) {
|
|
176
295
|
throw new Error('Chat not found');
|
|
177
296
|
}
|
|
297
|
+
// Delete descendant chat rows in parallel. Use `Promise.allSettled` so a
|
|
298
|
+
// missing/already-deleted descendant doesn't abort cleanup of the rest.
|
|
299
|
+
if (descendantIds.length > 0) {
|
|
300
|
+
await Promise.allSettled(descendantIds.map(id => this.chatRepository.delete(id)));
|
|
301
|
+
}
|
|
302
|
+
const allIds = [chatId, ...descendantIds];
|
|
178
303
|
await Promise.all([
|
|
179
|
-
this.messageRepository.deleteByChatId(
|
|
180
|
-
this.activityRepository.deleteByGroupId(
|
|
304
|
+
...allIds.map(id => this.messageRepository.deleteByChatId(id)),
|
|
305
|
+
...allIds.map(id => this.activityRepository.deleteByGroupId(id)),
|
|
181
306
|
]);
|
|
182
|
-
|
|
307
|
+
for (const id of allIds) {
|
|
308
|
+
this.artifactStore.deleteArtifacts(id);
|
|
309
|
+
}
|
|
310
|
+
}
|
|
311
|
+
/**
|
|
312
|
+
* Walk the subagent tree rooted at `rootChatId` and return every descendant
|
|
313
|
+
* chat id (excluding the root itself). BFS, with parallel fan-out per level
|
|
314
|
+
* so deeply nested trees don't serialize one DB roundtrip per depth.
|
|
315
|
+
*/
|
|
316
|
+
async collectDescendantChatIds(rootChatId) {
|
|
317
|
+
const descendants = [];
|
|
318
|
+
let frontier = [rootChatId];
|
|
319
|
+
while (frontier.length > 0) {
|
|
320
|
+
const childLists = await Promise.all(frontier.map(id => this.chatRepository.find({ parentChatId: id })));
|
|
321
|
+
const nextFrontier = [];
|
|
322
|
+
for (const list of childLists) {
|
|
323
|
+
for (const child of list) {
|
|
324
|
+
descendants.push(child.id);
|
|
325
|
+
nextFrontier.push(child.id);
|
|
326
|
+
}
|
|
327
|
+
}
|
|
328
|
+
frontier = nextFrontier;
|
|
329
|
+
}
|
|
330
|
+
return descendants;
|
|
183
331
|
}
|
|
184
332
|
async upsertAndGetChat(payload, excludeSocketId) {
|
|
185
333
|
const targetUserId = payload.userId ?? 'guest';
|
|
@@ -242,6 +390,7 @@ export class ChatProcessor {
|
|
|
242
390
|
title: this.getTemporaryTitle(contextKey),
|
|
243
391
|
type: ChatType.Chat,
|
|
244
392
|
status: AgentStatus.Streaming,
|
|
393
|
+
sessionKind: AgentSessionKind.ROOT,
|
|
245
394
|
contextKey,
|
|
246
395
|
userId: targetUserId,
|
|
247
396
|
metadata,
|
|
@@ -313,8 +462,11 @@ export class ChatProcessor {
|
|
|
313
462
|
* Update selected fields (input/output/status) on a specific tool call.
|
|
314
463
|
*/
|
|
315
464
|
async updateToolCall(params) {
|
|
316
|
-
if (params.input === undefined &&
|
|
317
|
-
|
|
465
|
+
if (params.input === undefined &&
|
|
466
|
+
params.output === undefined &&
|
|
467
|
+
params.status === undefined &&
|
|
468
|
+
params.subagentChatId === undefined) {
|
|
469
|
+
throw new Error('At least one tool call update field must be provided');
|
|
318
470
|
}
|
|
319
471
|
const chat = await this.chatRepository.findById(params.chatId);
|
|
320
472
|
if (!chat) {
|
|
@@ -342,6 +494,7 @@ export class ChatProcessor {
|
|
|
342
494
|
...(params.input !== undefined ? { input: params.input } : {}),
|
|
343
495
|
...(params.output !== undefined ? { output: params.output } : {}),
|
|
344
496
|
...(params.status !== undefined ? { status: params.status } : {}),
|
|
497
|
+
...(params.subagentChatId !== undefined ? { subagentChatId: params.subagentChatId } : {}),
|
|
345
498
|
});
|
|
346
499
|
if (!updatedMessage) {
|
|
347
500
|
throw new Error('Tool call not found in message');
|
|
@@ -351,8 +504,80 @@ export class ChatProcessor {
|
|
|
351
504
|
this.socketService.emitMessageUpdate(chat.userId, updatedMessage, params.excludeSocketId);
|
|
352
505
|
return updatedMessage;
|
|
353
506
|
}
|
|
507
|
+
async storeSubagentResponse(params) {
|
|
508
|
+
const { subagentChat, content, error, context, executionContext } = params;
|
|
509
|
+
if (!subagentChat.parentChatId || !subagentChat.parentMessageId) {
|
|
510
|
+
throw new Error(`Parent chat or message not found for subagent ${subagentChat.id}`);
|
|
511
|
+
}
|
|
512
|
+
const chat = await this.chatRepository.findById(subagentChat.parentChatId);
|
|
513
|
+
if (!chat) {
|
|
514
|
+
throw new Error(`Parent chat not found for subagent ${subagentChat.id}`);
|
|
515
|
+
}
|
|
516
|
+
const assistantMessage = await this.messageRepository.findById(subagentChat.parentMessageId);
|
|
517
|
+
if (!assistantMessage || assistantMessage.chat !== subagentChat.parentChatId) {
|
|
518
|
+
throw new Error(`Assistant message with id ${subagentChat.parentMessageId} not found`);
|
|
519
|
+
}
|
|
520
|
+
const toolCall = assistantMessage.toolCalls?.find(tc => tc.id === subagentChat.parentToolCallId);
|
|
521
|
+
if (!toolCall) {
|
|
522
|
+
throw new Error(`Tool call with id ${subagentChat.parentToolCallId} not found`);
|
|
523
|
+
}
|
|
524
|
+
if (error) {
|
|
525
|
+
toolCall.output = {
|
|
526
|
+
type: 'error',
|
|
527
|
+
message: error,
|
|
528
|
+
};
|
|
529
|
+
toolCall.status = AgentToolCallStatus.Failed;
|
|
530
|
+
}
|
|
531
|
+
else {
|
|
532
|
+
toolCall.output = content;
|
|
533
|
+
toolCall.status = AgentToolCallStatus.Succeeded;
|
|
534
|
+
}
|
|
535
|
+
await this.messageRepository.update(assistantMessage.id, assistantMessage);
|
|
536
|
+
const agentOptions = await this.aiHelper.getAgentOptions({
|
|
537
|
+
agentName: assistantMessage.agentName,
|
|
538
|
+
context: context,
|
|
539
|
+
executionContext: executionContext,
|
|
540
|
+
});
|
|
541
|
+
const executionTenants = this.getExecutionTenants(params);
|
|
542
|
+
const abortController = this.agentStore.registerAgentProcess(chat.id);
|
|
543
|
+
const existingMessages = await this.messageRepository.findByChatId(chat.id);
|
|
544
|
+
// Limit context to prevent exceeding token limits
|
|
545
|
+
const limitedMessages = ContextLimiter.limitContext(existingMessages, {
|
|
546
|
+
maxMessages: this.config.ai.maxContextMessages,
|
|
547
|
+
keepFirstUserMessage: true,
|
|
548
|
+
keepSystemMessages: true,
|
|
549
|
+
});
|
|
550
|
+
agentOptions.onStepFinish = (stepResult) => {
|
|
551
|
+
return this.storeStepActivity({
|
|
552
|
+
chat,
|
|
553
|
+
parentId: assistantMessage.activity,
|
|
554
|
+
stepResult,
|
|
555
|
+
name: ActivityOperationName.STEP_FINISHED,
|
|
556
|
+
sourceId: assistantMessage.id,
|
|
557
|
+
sourceType: 'AgentMessage',
|
|
558
|
+
tenants: executionTenants,
|
|
559
|
+
});
|
|
560
|
+
};
|
|
561
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(chat.userId, agentOptions.name);
|
|
562
|
+
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
563
|
+
await this.streamMessageStep({
|
|
564
|
+
chat,
|
|
565
|
+
existingMessages: limitedMessages,
|
|
566
|
+
assistantMessage,
|
|
567
|
+
agentOptions,
|
|
568
|
+
signal: abortController.signal,
|
|
569
|
+
tenants: executionTenants,
|
|
570
|
+
executionContext,
|
|
571
|
+
context,
|
|
572
|
+
});
|
|
573
|
+
}
|
|
354
574
|
async streamMessageStep(params) {
|
|
355
575
|
const { chat, assistantMessage, existingMessages, signal, excludeSocketId, agentOptions } = params;
|
|
576
|
+
const persistAndBroadcastToolCalls = async () => {
|
|
577
|
+
await this.messageRepository.update(assistantMessage.id, { toolCalls: assistantMessage.toolCalls ?? [] });
|
|
578
|
+
this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
|
|
579
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Update, data: assistantMessage });
|
|
580
|
+
};
|
|
356
581
|
if (signal.aborted) {
|
|
357
582
|
return;
|
|
358
583
|
}
|
|
@@ -366,12 +591,21 @@ export class ChatProcessor {
|
|
|
366
591
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Error, data: chunk.error });
|
|
367
592
|
assistantMessage.content = chunk.error.message || 'Sorry, I cannot process you request due to the error';
|
|
368
593
|
await this.messageRepository.update(assistantMessage.id, assistantMessage);
|
|
369
|
-
await this.
|
|
594
|
+
await this.updateChatAndEmit(chat, { status: AgentStatus.Error }, excludeSocketId);
|
|
595
|
+
if (chat.sessionKind === AgentSessionKind.SUBAGENT) {
|
|
596
|
+
await this.storeSubagentResponse({
|
|
597
|
+
subagentChat: chat,
|
|
598
|
+
content: undefined,
|
|
599
|
+
error: chunk.error,
|
|
600
|
+
context: params.context,
|
|
601
|
+
executionContext: params.executionContext,
|
|
602
|
+
});
|
|
603
|
+
}
|
|
370
604
|
continue;
|
|
371
605
|
}
|
|
372
606
|
if (chunk.type === 'abort') {
|
|
373
607
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Aborted, data: undefined });
|
|
374
|
-
await this.
|
|
608
|
+
await this.updateChatAndEmit(chat, { status: AgentStatus.Aborted }, excludeSocketId);
|
|
375
609
|
await this.messageRepository.update(assistantMessage.id, assistantMessage);
|
|
376
610
|
this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
|
|
377
611
|
continue;
|
|
@@ -390,8 +624,9 @@ export class ChatProcessor {
|
|
|
390
624
|
((totalUsage.promptTokens ?? 0) + (totalUsage.completionTokens ?? 0));
|
|
391
625
|
}
|
|
392
626
|
await this.messageRepository.update(assistantMessage.id, { ...assistantMessage });
|
|
627
|
+
this.socketService.emitMessageUpdate(chat.userId, assistantMessage, excludeSocketId);
|
|
393
628
|
if (chunk.finishReason === 'stop') {
|
|
394
|
-
await this.
|
|
629
|
+
let nextChat = await this.updateChatAndEmit(chat, { status: AgentStatus.Finished }, excludeSocketId);
|
|
395
630
|
if (chat.title && this.isTemporaryTitle(chat.title)) {
|
|
396
631
|
const title = await this.aiHelper.generateTitleForMessage(chat.contextKey, existingMessages.filter(m => m.role === MessageRole.User), (stepResult) => {
|
|
397
632
|
return this.storeStepActivity({
|
|
@@ -403,16 +638,51 @@ export class ChatProcessor {
|
|
|
403
638
|
tenants: params.tenants || {},
|
|
404
639
|
});
|
|
405
640
|
}, params.executionContext);
|
|
406
|
-
await this.
|
|
407
|
-
|
|
641
|
+
nextChat = await this.updateChatAndEmit(nextChat, { title }, excludeSocketId);
|
|
642
|
+
}
|
|
643
|
+
if (chat.sessionKind === AgentSessionKind.SUBAGENT) {
|
|
644
|
+
await this.storeSubagentResponse({
|
|
645
|
+
subagentChat: chat,
|
|
646
|
+
content: assistantMessage.content,
|
|
647
|
+
error: undefined,
|
|
648
|
+
context: params.context,
|
|
649
|
+
executionContext: params.executionContext,
|
|
650
|
+
});
|
|
408
651
|
}
|
|
409
652
|
}
|
|
410
653
|
if (chunk.finishReason === 'tool-calls') {
|
|
411
|
-
|
|
412
|
-
|
|
654
|
+
const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.output === undefined && toolCall.status === AgentToolCallStatus.Running);
|
|
655
|
+
if (!toolCall) {
|
|
413
656
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
657
|
+
await this.storeSubagentResponse({
|
|
658
|
+
subagentChat: chat,
|
|
659
|
+
content: JSON.stringify(assistantMessage.toolCalls),
|
|
660
|
+
error: undefined,
|
|
661
|
+
context: params.context,
|
|
662
|
+
executionContext: params.executionContext,
|
|
663
|
+
});
|
|
414
664
|
continue;
|
|
415
665
|
}
|
|
666
|
+
const toolConfig = ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
|
|
667
|
+
if (toolCall.requiresConfirmation && toolCall.approvalId) {
|
|
668
|
+
await this.updateChatAndEmit(chat, { status: AgentStatus.WaitingForUserAction }, excludeSocketId);
|
|
669
|
+
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
670
|
+
}
|
|
671
|
+
else if (toolConfig && toolConfig.type === AgentToolType.SUBAGENT) {
|
|
672
|
+
void this.startSubagentProcess({
|
|
673
|
+
parentChat: chat,
|
|
674
|
+
parentMessage: assistantMessage,
|
|
675
|
+
parentToolCallId: toolCall.id,
|
|
676
|
+
toolName: toolCall.name,
|
|
677
|
+
input: toolCall.input,
|
|
678
|
+
subAgentConfig: toolConfig.data.subAgent,
|
|
679
|
+
context: params.context,
|
|
680
|
+
executionContext: params.executionContext,
|
|
681
|
+
}).catch(error => {
|
|
682
|
+
logger.error(error);
|
|
683
|
+
//todo: store error in response
|
|
684
|
+
});
|
|
685
|
+
}
|
|
416
686
|
}
|
|
417
687
|
//todo: Handle other finish reasons // length, content, error, other, undefined
|
|
418
688
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: AgentProcessEventType.Finished, data: assistantMessage });
|
|
@@ -440,20 +710,43 @@ export class ChatProcessor {
|
|
|
440
710
|
if (chunk.type === 'tool-input-start') {
|
|
441
711
|
if (!assistantMessage.toolCalls)
|
|
442
712
|
assistantMessage.toolCalls = [];
|
|
443
|
-
assistantMessage.toolCalls.
|
|
444
|
-
|
|
445
|
-
name
|
|
446
|
-
status
|
|
447
|
-
|
|
448
|
-
|
|
713
|
+
const existingToolCall = assistantMessage.toolCalls.find((toolCall) => toolCall.id === chunk.id);
|
|
714
|
+
if (existingToolCall) {
|
|
715
|
+
existingToolCall.name = chunk.toolName;
|
|
716
|
+
if (!existingToolCall.status) {
|
|
717
|
+
existingToolCall.status = AgentToolCallStatus.Pending;
|
|
718
|
+
}
|
|
719
|
+
existingToolCall.input = existingToolCall.input ?? {};
|
|
720
|
+
}
|
|
721
|
+
else {
|
|
722
|
+
assistantMessage.toolCalls.push({
|
|
723
|
+
id: chunk.id,
|
|
724
|
+
name: chunk.toolName,
|
|
725
|
+
status: AgentToolCallStatus.Pending,
|
|
726
|
+
input: {},
|
|
727
|
+
});
|
|
728
|
+
}
|
|
729
|
+
await persistAndBroadcastToolCalls();
|
|
449
730
|
continue;
|
|
450
731
|
}
|
|
451
732
|
if (chunk.type === 'tool-call') {
|
|
452
|
-
|
|
453
|
-
|
|
733
|
+
if (!assistantMessage.toolCalls)
|
|
734
|
+
assistantMessage.toolCalls = [];
|
|
735
|
+
let toolCall = assistantMessage.toolCalls.find(toolCall => toolCall.id === chunk.toolCallId);
|
|
736
|
+
if (!toolCall) {
|
|
737
|
+
toolCall = {
|
|
738
|
+
id: chunk.toolCallId,
|
|
739
|
+
name: chunk.toolName,
|
|
740
|
+
status: AgentToolCallStatus.Running,
|
|
741
|
+
input: chunk.input,
|
|
742
|
+
};
|
|
743
|
+
assistantMessage.toolCalls.push(toolCall);
|
|
744
|
+
}
|
|
745
|
+
else {
|
|
454
746
|
toolCall.status = AgentToolCallStatus.Running;
|
|
455
747
|
toolCall.input = chunk.input;
|
|
456
748
|
}
|
|
749
|
+
await persistAndBroadcastToolCalls();
|
|
457
750
|
continue;
|
|
458
751
|
}
|
|
459
752
|
if (chunk.type === 'tool-error') {
|
|
@@ -461,29 +754,33 @@ export class ChatProcessor {
|
|
|
461
754
|
if (toolCall) {
|
|
462
755
|
toolCall.status = AgentToolCallStatus.Failed;
|
|
463
756
|
toolCall.error = chunk.error.message || JSON.stringify(chunk.error);
|
|
757
|
+
await persistAndBroadcastToolCalls();
|
|
464
758
|
}
|
|
465
759
|
continue;
|
|
466
760
|
}
|
|
467
761
|
if (chunk.type === 'tool-approval-request') {
|
|
468
762
|
const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCall.toolCallId);
|
|
469
763
|
if (toolCall) {
|
|
764
|
+
const toolConfig = ConfigStore.getInstance().getToolConfig(assistantMessage.agentName || '', toolCall.name);
|
|
765
|
+
if (toolConfig && toolConfig.type === AgentToolType.SUBAGENT) {
|
|
766
|
+
// TODO: allow needsApproval flow for the subagents too
|
|
767
|
+
continue;
|
|
768
|
+
}
|
|
470
769
|
toolCall.requiresConfirmation = true;
|
|
471
770
|
toolCall.approvalId = chunk.approvalId;
|
|
472
771
|
if (toolCall.name === AgentToolType.REQUEST_CLARIFICATION) {
|
|
473
772
|
toolCall.requiresUserAction = true;
|
|
474
773
|
}
|
|
774
|
+
await persistAndBroadcastToolCalls();
|
|
475
775
|
}
|
|
476
776
|
continue;
|
|
477
777
|
}
|
|
478
778
|
if (chunk.type === 'tool-result') {
|
|
479
779
|
const toolCall = assistantMessage.toolCalls?.find(toolCall => toolCall.id === chunk.toolCallId);
|
|
480
780
|
if (toolCall) {
|
|
481
|
-
if (chunk.output?.requiresApproval) {
|
|
482
|
-
toolCall.requiresConfirmation = true;
|
|
483
|
-
continue;
|
|
484
|
-
}
|
|
485
781
|
toolCall.status = AgentToolCallStatus.Succeeded;
|
|
486
782
|
toolCall.output = chunk.output;
|
|
783
|
+
await persistAndBroadcastToolCalls();
|
|
487
784
|
}
|
|
488
785
|
continue;
|
|
489
786
|
}
|
|
@@ -719,6 +1016,7 @@ export class ChatProcessor {
|
|
|
719
1016
|
signal: abortController.signal,
|
|
720
1017
|
tenants: executionTenants,
|
|
721
1018
|
executionContext: payload.executionContext,
|
|
1019
|
+
context: payload.context,
|
|
722
1020
|
});
|
|
723
1021
|
}
|
|
724
1022
|
catch (error) {
|
|
@@ -730,7 +1028,7 @@ export class ChatProcessor {
|
|
|
730
1028
|
const availableToolsMap = Object.fromEntries(toolNames.map(key => [key, true]));
|
|
731
1029
|
const agentsConfiguration = ConfigStore.getInstance().getAgentConfigByName(agentOptions.name);
|
|
732
1030
|
agentsConfiguration.tools.forEach((tool) => {
|
|
733
|
-
const toolTitle = tool
|
|
1031
|
+
const toolTitle = getAgentToolName(tool);
|
|
734
1032
|
if (tool.disabledByDefault) {
|
|
735
1033
|
availableToolsMap[toolTitle] = false;
|
|
736
1034
|
}
|