@multiplayer-app/ai-agent-node 0.1.0-beta.75 → 0.1.0-beta.77
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/processors/ChatProcessor.cjs +363 -28
- package/dist/cjs/processors/ChatProcessor.cjs.map +1 -1
- package/dist/cjs/processors/ChatProcessor.d.ts +23 -2
- package/dist/cjs/processors/ChatProcessor.d.ts.map +1 -1
- package/dist/cjs/processors/ChatProcessor.test.cjs +10 -10
- package/dist/cjs/processors/ChatProcessor.test.cjs.map +1 -1
- package/dist/cjs/tsconfig.cjs.tsbuildinfo +1 -1
- package/dist/esm/processors/ChatProcessor.d.ts +23 -2
- package/dist/esm/processors/ChatProcessor.d.ts.map +1 -1
- package/dist/esm/processors/ChatProcessor.js +363 -28
- package/dist/esm/processors/ChatProcessor.js.map +1 -1
- package/dist/esm/processors/ChatProcessor.test.js +10 -10
- package/dist/esm/processors/ChatProcessor.test.js.map +1 -1
- package/dist/esm/tsconfig.esm.tsbuildinfo +1 -1
- package/package.json +3 -3
|
@@ -10,6 +10,9 @@ const logger_1 = require("../libs/logger/index.cjs");
|
|
|
10
10
|
const ai_agent_types_2 = require("@multiplayer-app/ai-agent-types");
|
|
11
11
|
const utils_1 = require("../utils/utils.cjs");
|
|
12
12
|
class ChatProcessor {
|
|
13
|
+
getChatUserId(chat) {
|
|
14
|
+
return chat.tenants?.userId;
|
|
15
|
+
}
|
|
13
16
|
constructor(params) {
|
|
14
17
|
this.chatRepository = params.chatRepository;
|
|
15
18
|
this.messageRepository = params.messageRepository;
|
|
@@ -43,14 +46,34 @@ class ChatProcessor {
|
|
|
43
46
|
}
|
|
44
47
|
return payload.tenants ?? {};
|
|
45
48
|
}
|
|
49
|
+
getParticipantIds(chat) {
|
|
50
|
+
const primary = chat.userId ?? this.getChatUserId(chat);
|
|
51
|
+
return Array.from(new Set([primary, ...(chat.participantIds ?? [])].filter(Boolean)));
|
|
52
|
+
}
|
|
53
|
+
hasChatAccess(chat, userId) {
|
|
54
|
+
if (!userId)
|
|
55
|
+
return true;
|
|
56
|
+
return this.getParticipantIds(chat).includes(userId);
|
|
57
|
+
}
|
|
58
|
+
emitChatToParticipants(chat, chatToEmit, excludeSocketId) {
|
|
59
|
+
for (const userId of this.getParticipantIds(chatToEmit.participantIds ? chatToEmit : chat)) {
|
|
60
|
+
this.socketService.emitChatUpdate(userId, chatToEmit, excludeSocketId);
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
emitMessageToParticipants(chat, message, excludeSocketId) {
|
|
64
|
+
for (const userId of this.getParticipantIds(chat)) {
|
|
65
|
+
this.socketService.emitMessageUpdate(userId, message, excludeSocketId);
|
|
66
|
+
}
|
|
67
|
+
}
|
|
46
68
|
async updateChatAndEmit(chat, update, excludeSocketId) {
|
|
47
69
|
await this.chatRepository.update(chat.id, update);
|
|
48
70
|
const updatedChat = await this.chatRepository.findById(chat.id);
|
|
49
71
|
const chatToEmit = updatedChat ?? { ...chat, ...update };
|
|
50
|
-
this.
|
|
72
|
+
this.emitChatToParticipants(chat, chatToEmit, excludeSocketId);
|
|
51
73
|
return chatToEmit;
|
|
52
74
|
}
|
|
53
75
|
async startSubagentProcess(params) {
|
|
76
|
+
const parentUserId = params.parentChat.userId ?? this.getChatUserId(params.parentChat) ?? 'guest';
|
|
54
77
|
const childChat = await this.chatRepository.create({
|
|
55
78
|
title: `${params.subAgentConfig.name} subagent`,
|
|
56
79
|
type: ai_agent_types_1.ChatType.Agent,
|
|
@@ -60,7 +83,8 @@ class ChatProcessor {
|
|
|
60
83
|
parentMessageId: params.parentMessage.id,
|
|
61
84
|
parentToolCallId: params.parentToolCallId,
|
|
62
85
|
contextKey: params.parentChat.contextKey,
|
|
63
|
-
userId:
|
|
86
|
+
userId: parentUserId,
|
|
87
|
+
tenants: { ...params.parentChat.tenants },
|
|
64
88
|
model: params.subAgentConfig.defaultModel || params.parentChat.model,
|
|
65
89
|
});
|
|
66
90
|
try {
|
|
@@ -75,10 +99,10 @@ class ChatProcessor {
|
|
|
75
99
|
toolCalls: params.parentMessage.toolCalls,
|
|
76
100
|
});
|
|
77
101
|
if (updatedParentMessage) {
|
|
78
|
-
this.
|
|
102
|
+
this.emitMessageToParticipants(params.parentChat, updatedParentMessage);
|
|
79
103
|
}
|
|
80
104
|
}
|
|
81
|
-
this.
|
|
105
|
+
this.emitChatToParticipants(params.parentChat, childChat);
|
|
82
106
|
const executionTenants = this.getExecutionTenants(params);
|
|
83
107
|
const abortController = this.agentStore.registerSubAgentProcess(childChat.id, params.parentChat.id);
|
|
84
108
|
const userMessage = await this.createMessage(childChat, ai_agent_types_1.MessageRole.User, {
|
|
@@ -94,7 +118,7 @@ class ChatProcessor {
|
|
|
94
118
|
agentName: params.subAgentConfig.name
|
|
95
119
|
});
|
|
96
120
|
const parentActivity = await this.activityRepository.create({
|
|
97
|
-
ownerId:
|
|
121
|
+
ownerId: parentUserId,
|
|
98
122
|
groupId: params.parentChat.id,
|
|
99
123
|
name: ai_agent_types_1.ActivityOperationName.SUBAGENT,
|
|
100
124
|
tenants: executionTenants,
|
|
@@ -131,7 +155,7 @@ class ChatProcessor {
|
|
|
131
155
|
tenants: executionTenants,
|
|
132
156
|
});
|
|
133
157
|
};
|
|
134
|
-
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(
|
|
158
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(parentUserId, agentOptions.name);
|
|
135
159
|
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
136
160
|
await this.streamMessageStep({
|
|
137
161
|
chat: childChat,
|
|
@@ -232,11 +256,15 @@ class ChatProcessor {
|
|
|
232
256
|
sessionKind: ai_agent_types_2.AgentSessionKind.ROOT
|
|
233
257
|
};
|
|
234
258
|
if (params?.userId) {
|
|
259
|
+
// Chats visible to this user: owner, tenant owner, or participant (multi-user).
|
|
235
260
|
filter.userId = params.userId;
|
|
236
261
|
}
|
|
237
262
|
if (params?.contextKey) {
|
|
238
263
|
filter.contextKey = params.contextKey;
|
|
239
264
|
}
|
|
265
|
+
if (params?.multiUser !== undefined) {
|
|
266
|
+
filter.multiUser = params.multiUser;
|
|
267
|
+
}
|
|
240
268
|
// Build query options for sort and limit with defaults.
|
|
241
269
|
const options = {
|
|
242
270
|
sort: {
|
|
@@ -323,17 +351,12 @@ class ChatProcessor {
|
|
|
323
351
|
return descendants;
|
|
324
352
|
}
|
|
325
353
|
async upsertAndGetChat(payload, excludeSocketId) {
|
|
326
|
-
const targetUserId = payload.userId ?? 'guest';
|
|
327
354
|
let chat = null;
|
|
328
355
|
if (payload.chatId) {
|
|
329
356
|
chat = await this.chatRepository.findById(payload.chatId);
|
|
330
357
|
if (!chat) {
|
|
331
358
|
throw new Error('Chat not found');
|
|
332
359
|
}
|
|
333
|
-
// Ensure chat is associated with the caller's userId
|
|
334
|
-
if (chat.userId && chat.userId !== targetUserId) {
|
|
335
|
-
throw new Error('Chat does not belong to this user');
|
|
336
|
-
}
|
|
337
360
|
}
|
|
338
361
|
if (!chat) {
|
|
339
362
|
chat = await this.createChat(payload, excludeSocketId);
|
|
@@ -366,19 +389,22 @@ class ChatProcessor {
|
|
|
366
389
|
role,
|
|
367
390
|
content: messageData.content,
|
|
368
391
|
agentName: messageData.agentName,
|
|
392
|
+
sender: messageData.sender,
|
|
393
|
+
mentions: messageData.mentions ?? [],
|
|
394
|
+
annotations: messageData.annotations,
|
|
369
395
|
attachments: attachments ?? [],
|
|
370
396
|
reasoning: "",
|
|
371
397
|
toolCalls: []
|
|
372
398
|
});
|
|
373
|
-
|
|
374
|
-
this.socketService.emitMessageUpdate(chat.userId, message, excludeSocketId);
|
|
375
|
-
}
|
|
399
|
+
this.emitMessageToParticipants(chat, message, excludeSocketId);
|
|
376
400
|
return message;
|
|
377
401
|
}
|
|
378
402
|
async createChat(payload, excludeSocketId) {
|
|
379
|
-
const targetUserId = payload.userId ?? 'guest';
|
|
403
|
+
const targetUserId = payload.userId ?? payload.tenants?.userId ?? 'guest';
|
|
380
404
|
const contextKey = 'contextKey' in payload ? payload.contextKey : 'default';
|
|
381
405
|
const metadata = 'metadata' in payload ? payload.metadata : {};
|
|
406
|
+
const requestedParticipantIds = 'participantIds' in payload && Array.isArray(payload.participantIds) ? payload.participantIds : [];
|
|
407
|
+
const participantIds = Array.from(new Set([targetUserId, ...requestedParticipantIds].filter(Boolean)));
|
|
382
408
|
const chat = await this.chatRepository.create({
|
|
383
409
|
title: this.getTemporaryTitle(contextKey),
|
|
384
410
|
type: ai_agent_types_1.ChatType.Chat,
|
|
@@ -386,10 +412,15 @@ class ChatProcessor {
|
|
|
386
412
|
sessionKind: ai_agent_types_2.AgentSessionKind.ROOT,
|
|
387
413
|
contextKey,
|
|
388
414
|
userId: targetUserId,
|
|
415
|
+
tenants: {
|
|
416
|
+
...(payload.tenants ?? {}),
|
|
417
|
+
userId: targetUserId,
|
|
418
|
+
},
|
|
419
|
+
participantIds,
|
|
389
420
|
metadata,
|
|
390
421
|
...(payload.model ? { model: payload.model } : {})
|
|
391
422
|
});
|
|
392
|
-
this.
|
|
423
|
+
this.emitChatToParticipants(chat, chat, excludeSocketId);
|
|
393
424
|
return chat;
|
|
394
425
|
}
|
|
395
426
|
/**
|
|
@@ -444,7 +475,7 @@ class ChatProcessor {
|
|
|
444
475
|
await this.messageRepository.update(message.id, { toolCalls });
|
|
445
476
|
// Bump chat updatedAt so listChats ordering reflects the action.
|
|
446
477
|
await this.chatRepository.update(chat.id, { updatedAt: now });
|
|
447
|
-
this.
|
|
478
|
+
this.emitMessageToParticipants(chat, updatedMessage, params.excludeSocketId);
|
|
448
479
|
if (params.systemMessage) {
|
|
449
480
|
//todo discuss support for system messages
|
|
450
481
|
//await this.createMessage(chat, MessageRole.System, params.systemMessage, undefined, params.excludeSocketId);
|
|
@@ -465,7 +496,7 @@ class ChatProcessor {
|
|
|
465
496
|
if (!chat) {
|
|
466
497
|
throw new Error('Chat not found');
|
|
467
498
|
}
|
|
468
|
-
if (params.userId && chat
|
|
499
|
+
if (params.userId && !this.hasChatAccess(chat, params.userId)) {
|
|
469
500
|
throw new Error('Chat does not belong to this user');
|
|
470
501
|
}
|
|
471
502
|
const message = await this.messageRepository.findById(params.messageId);
|
|
@@ -494,7 +525,7 @@ class ChatProcessor {
|
|
|
494
525
|
}
|
|
495
526
|
const now = new Date().toISOString();
|
|
496
527
|
await this.chatRepository.update(chat.id, { updatedAt: now });
|
|
497
|
-
this.
|
|
528
|
+
this.emitMessageToParticipants(chat, updatedMessage, params.excludeSocketId);
|
|
498
529
|
return updatedMessage;
|
|
499
530
|
}
|
|
500
531
|
async storeSubagentResponse(params) {
|
|
@@ -551,7 +582,7 @@ class ChatProcessor {
|
|
|
551
582
|
tenants: executionTenants,
|
|
552
583
|
});
|
|
553
584
|
};
|
|
554
|
-
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(chat.userId, agentOptions.name);
|
|
585
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(this.getChatUserId(chat) ?? chat.userId ?? 'guest', agentOptions.name);
|
|
555
586
|
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
556
587
|
await this.streamMessageStep({
|
|
557
588
|
chat,
|
|
@@ -568,7 +599,7 @@ class ChatProcessor {
|
|
|
568
599
|
const { chat, assistantMessage, existingMessages, signal, excludeSocketId, agentOptions } = params;
|
|
569
600
|
const persistAndBroadcastToolCalls = async () => {
|
|
570
601
|
await this.messageRepository.update(assistantMessage.id, { toolCalls: assistantMessage.toolCalls ?? [] });
|
|
571
|
-
this.
|
|
602
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
572
603
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Update, data: assistantMessage });
|
|
573
604
|
};
|
|
574
605
|
if (signal.aborted) {
|
|
@@ -600,7 +631,7 @@ class ChatProcessor {
|
|
|
600
631
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Aborted, data: undefined });
|
|
601
632
|
await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Aborted }, excludeSocketId);
|
|
602
633
|
await this.messageRepository.update(assistantMessage.id, assistantMessage);
|
|
603
|
-
this.
|
|
634
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
604
635
|
continue;
|
|
605
636
|
}
|
|
606
637
|
if (chunk.type === 'finish') {
|
|
@@ -617,7 +648,7 @@ class ChatProcessor {
|
|
|
617
648
|
((totalUsage.promptTokens ?? 0) + (totalUsage.completionTokens ?? 0));
|
|
618
649
|
}
|
|
619
650
|
await this.messageRepository.update(assistantMessage.id, { ...assistantMessage });
|
|
620
|
-
this.
|
|
651
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
621
652
|
if (chunk.finishReason === 'stop') {
|
|
622
653
|
let nextChat = await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Finished }, excludeSocketId);
|
|
623
654
|
if (chat.title && this.isTemporaryTitle(chat.title)) {
|
|
@@ -695,7 +726,7 @@ class ChatProcessor {
|
|
|
695
726
|
...assistantMessage,
|
|
696
727
|
content: assistantMessage.content
|
|
697
728
|
};
|
|
698
|
-
this.
|
|
729
|
+
this.emitMessageToParticipants(chat, updatedMessage, excludeSocketId);
|
|
699
730
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Update, data: updatedMessage });
|
|
700
731
|
await this.messageRepository.update(assistantMessage.id, { content: assistantMessage.content });
|
|
701
732
|
continue;
|
|
@@ -781,7 +812,7 @@ class ChatProcessor {
|
|
|
781
812
|
if (!assistantMessage.reasoning)
|
|
782
813
|
assistantMessage.reasoning = '';
|
|
783
814
|
assistantMessage.reasoning += chunk.text;
|
|
784
|
-
this.
|
|
815
|
+
this.emitMessageToParticipants(chat, assistantMessage, excludeSocketId);
|
|
785
816
|
await this.messageRepository.update(assistantMessage.id, { reasoning: assistantMessage.reasoning });
|
|
786
817
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Update, data: assistantMessage });
|
|
787
818
|
continue;
|
|
@@ -864,11 +895,12 @@ class ChatProcessor {
|
|
|
864
895
|
}
|
|
865
896
|
async processNewUserMessage(chat, payload, prevMessages, excludeSocketId) {
|
|
866
897
|
const executionTenants = this.getExecutionTenants(payload);
|
|
898
|
+
const chatUserId = this.getChatUserId(chat) ?? 'guest';
|
|
867
899
|
const userMessage = await this.createMessage(chat, ai_agent_types_1.MessageRole.User, { content: payload.content, agentName: undefined }, payload.attachments);
|
|
868
900
|
await this.agentStore.shareAgentProcessEvent(chat.id, { type: store_1.AgentProcessEventType.Update, data: userMessage });
|
|
869
901
|
let assistantMessage = await this.createMessage(chat, ai_agent_types_1.MessageRole.Assistant, { content: '', agentName: undefined }, undefined, excludeSocketId);
|
|
870
902
|
const parentActivity = await this.activityRepository.create({
|
|
871
|
-
ownerId:
|
|
903
|
+
ownerId: chatUserId,
|
|
872
904
|
groupId: chat.id,
|
|
873
905
|
name: ai_agent_types_1.ActivityOperationName.MESSAGE_PROCESSING,
|
|
874
906
|
tenants: executionTenants,
|
|
@@ -930,12 +962,235 @@ class ChatProcessor {
|
|
|
930
962
|
agentOptions,
|
|
931
963
|
};
|
|
932
964
|
}
|
|
965
|
+
getAgentNamesForContext(contextKey) {
|
|
966
|
+
try {
|
|
967
|
+
return store_1.ConfigStore.getInstance().getAgentsForContext(contextKey).map((config) => config.name);
|
|
968
|
+
}
|
|
969
|
+
catch {
|
|
970
|
+
return [];
|
|
971
|
+
}
|
|
972
|
+
}
|
|
973
|
+
normalizeMentionName(value) {
|
|
974
|
+
return value.trim().replace(/^@/, '').toLowerCase();
|
|
975
|
+
}
|
|
976
|
+
extractMentionNames(content) {
|
|
977
|
+
const mentions = content.matchAll(/(^|[\s([{])@([a-zA-Z0-9_.-]+)/g);
|
|
978
|
+
return Array.from(mentions, (match) => match[2]).filter(Boolean);
|
|
979
|
+
}
|
|
980
|
+
getExplicitAgentMention(params) {
|
|
981
|
+
const agentNames = this.getAgentNamesForContext(params.contextKey);
|
|
982
|
+
const normalizedAgentNames = new Map(agentNames.map((name) => [this.normalizeMentionName(name), name]));
|
|
983
|
+
for (const mention of params.mentions ?? []) {
|
|
984
|
+
if (mention.type !== 'agent')
|
|
985
|
+
continue;
|
|
986
|
+
const normalized = this.normalizeMentionName(mention.name);
|
|
987
|
+
return { mentioned: true, agentName: normalizedAgentNames.get(normalized) };
|
|
988
|
+
}
|
|
989
|
+
for (const name of this.extractMentionNames(params.content)) {
|
|
990
|
+
const normalized = this.normalizeMentionName(name);
|
|
991
|
+
if (normalized === 'agent') {
|
|
992
|
+
return { mentioned: true };
|
|
993
|
+
}
|
|
994
|
+
const agentName = normalizedAgentNames.get(normalized);
|
|
995
|
+
if (agentName) {
|
|
996
|
+
return { mentioned: true, agentName };
|
|
997
|
+
}
|
|
998
|
+
}
|
|
999
|
+
return { mentioned: false };
|
|
1000
|
+
}
|
|
1001
|
+
hasNonAgentMention(params) {
|
|
1002
|
+
const agentNames = this.getAgentNamesForContext(params.contextKey);
|
|
1003
|
+
const normalizedAgentNames = new Set(['agent', ...agentNames.map((name) => this.normalizeMentionName(name))]);
|
|
1004
|
+
if ((params.mentions ?? []).some((mention) => mention.type === 'user')) {
|
|
1005
|
+
return true;
|
|
1006
|
+
}
|
|
1007
|
+
return this.extractMentionNames(params.content).some((name) => {
|
|
1008
|
+
const normalized = this.normalizeMentionName(name);
|
|
1009
|
+
return !normalizedAgentNames.has(normalized);
|
|
1010
|
+
});
|
|
1011
|
+
}
|
|
1012
|
+
renderMessageForDecision(message) {
|
|
1013
|
+
const sender = message.sender?.displayName || message.sender?.id || message.agentName || message.role;
|
|
1014
|
+
return `${sender}: ${message.content}`;
|
|
1015
|
+
}
|
|
1016
|
+
async analyzeAgentShouldRespond(params) {
|
|
1017
|
+
try {
|
|
1018
|
+
const recentMessages = [...params.previousMessages.slice(-8), params.userMessage]
|
|
1019
|
+
.map((message) => this.renderMessageForDecision(message))
|
|
1020
|
+
.join('\n');
|
|
1021
|
+
const response = await this.aiHelper.getAssistantResponse([
|
|
1022
|
+
{
|
|
1023
|
+
role: ai_agent_types_1.MessageRole.User,
|
|
1024
|
+
content: `Conversation:\n${recentMessages}\n\nShould the AI agent respond to the latest message?`
|
|
1025
|
+
}
|
|
1026
|
+
], {
|
|
1027
|
+
system: [
|
|
1028
|
+
'You decide whether an AI agent should reply in a multi-user chat.',
|
|
1029
|
+
'Reply with exactly YES or NO.',
|
|
1030
|
+
'YES only when the latest message asks for help, requests agent action, or is clearly addressed to the AI.',
|
|
1031
|
+
'NO when the latest message is human-to-human chatter, status, acknowledgement, or otherwise does not need the AI.'
|
|
1032
|
+
].join('\n'),
|
|
1033
|
+
temperature: 0,
|
|
1034
|
+
maxOutputTokens: 5,
|
|
1035
|
+
model: params.chat.model,
|
|
1036
|
+
executionContext: params.executionContext
|
|
1037
|
+
});
|
|
1038
|
+
return /^yes\b/i.test(response.trim());
|
|
1039
|
+
}
|
|
1040
|
+
catch (error) {
|
|
1041
|
+
logger_1.logger.warn({ error, chatId: params.chat.id }, 'Agent response decision failed');
|
|
1042
|
+
return false;
|
|
1043
|
+
}
|
|
1044
|
+
}
|
|
1045
|
+
async shouldRunAgentForMultiUserMessage(params) {
|
|
1046
|
+
const firstUserMessage = !params.previousMessages.some((message) => message.role === ai_agent_types_1.MessageRole.User);
|
|
1047
|
+
if (firstUserMessage) {
|
|
1048
|
+
return { shouldRun: true };
|
|
1049
|
+
}
|
|
1050
|
+
const explicitAgent = this.getExplicitAgentMention({
|
|
1051
|
+
content: params.payload.content,
|
|
1052
|
+
mentions: params.payload.mentions,
|
|
1053
|
+
contextKey: params.chat.contextKey,
|
|
1054
|
+
});
|
|
1055
|
+
if (explicitAgent.mentioned) {
|
|
1056
|
+
return { shouldRun: true, agentName: explicitAgent.agentName };
|
|
1057
|
+
}
|
|
1058
|
+
if (this.hasNonAgentMention({
|
|
1059
|
+
content: params.payload.content,
|
|
1060
|
+
mentions: params.payload.mentions,
|
|
1061
|
+
contextKey: params.chat.contextKey,
|
|
1062
|
+
})) {
|
|
1063
|
+
return { shouldRun: false };
|
|
1064
|
+
}
|
|
1065
|
+
const shouldRun = await this.analyzeAgentShouldRespond({
|
|
1066
|
+
chat: params.chat,
|
|
1067
|
+
previousMessages: params.previousMessages,
|
|
1068
|
+
userMessage: params.userMessage,
|
|
1069
|
+
executionContext: params.payload.executionContext,
|
|
1070
|
+
});
|
|
1071
|
+
return { shouldRun };
|
|
1072
|
+
}
|
|
1073
|
+
async prepareAgentResponseForUserMessage(chat, payload, prevMessages, userMessage, excludeSocketId, agentName) {
|
|
1074
|
+
const executionTenants = this.getExecutionTenants(payload);
|
|
1075
|
+
let assistantMessage = await this.createMessage(chat, ai_agent_types_1.MessageRole.Assistant, {
|
|
1076
|
+
content: '',
|
|
1077
|
+
agentName: agentName,
|
|
1078
|
+
sender: {
|
|
1079
|
+
id: agentName ?? 'agent',
|
|
1080
|
+
type: 'agent',
|
|
1081
|
+
displayName: agentName ?? 'Agent',
|
|
1082
|
+
}
|
|
1083
|
+
}, undefined, excludeSocketId);
|
|
1084
|
+
const ownerId = payload.userId ?? chat.userId ?? this.getChatUserId(chat) ?? 'guest';
|
|
1085
|
+
const parentActivity = await this.activityRepository.create({
|
|
1086
|
+
ownerId,
|
|
1087
|
+
groupId: chat.id,
|
|
1088
|
+
name: ai_agent_types_1.ActivityOperationName.MESSAGE_PROCESSING,
|
|
1089
|
+
tenants: executionTenants,
|
|
1090
|
+
sourceId: userMessage.id,
|
|
1091
|
+
sourceType: 'AgentMessage',
|
|
1092
|
+
metadata: {
|
|
1093
|
+
contextKey: chat.contextKey,
|
|
1094
|
+
modelId: this.aiHelper.getLanguageModelId(chat.model),
|
|
1095
|
+
},
|
|
1096
|
+
});
|
|
1097
|
+
const agentOptions = await this.aiHelper.getAgentOptions(agentName
|
|
1098
|
+
? {
|
|
1099
|
+
agentName,
|
|
1100
|
+
modelId: chat.model,
|
|
1101
|
+
context: payload.context,
|
|
1102
|
+
executionContext: payload.executionContext,
|
|
1103
|
+
}
|
|
1104
|
+
: {
|
|
1105
|
+
contextKey: chat.contextKey,
|
|
1106
|
+
messages: [...prevMessages, userMessage],
|
|
1107
|
+
context: payload.context,
|
|
1108
|
+
agentName: undefined,
|
|
1109
|
+
modelId: chat.model,
|
|
1110
|
+
executionContext: payload.executionContext,
|
|
1111
|
+
}, {
|
|
1112
|
+
onStepFinish: async (stepResult) => {
|
|
1113
|
+
await this.storeStepActivity({
|
|
1114
|
+
chat,
|
|
1115
|
+
parentId: parentActivity.id,
|
|
1116
|
+
stepResult,
|
|
1117
|
+
name: ai_agent_types_1.ActivityOperationName.AGENT_SELECTION,
|
|
1118
|
+
sourceId: userMessage.id,
|
|
1119
|
+
sourceType: 'AgentMessage',
|
|
1120
|
+
tenants: executionTenants,
|
|
1121
|
+
});
|
|
1122
|
+
}
|
|
1123
|
+
});
|
|
1124
|
+
const updatedAssistantMessage = await this.messageRepository.update(assistantMessage.id, {
|
|
1125
|
+
agentName: agentOptions.name,
|
|
1126
|
+
activity: parentActivity.id,
|
|
1127
|
+
sender: {
|
|
1128
|
+
id: agentOptions.name,
|
|
1129
|
+
type: 'agent',
|
|
1130
|
+
displayName: agentOptions.name,
|
|
1131
|
+
}
|
|
1132
|
+
});
|
|
1133
|
+
await this.activityRepository.update(parentActivity.id, {
|
|
1134
|
+
metadata: {
|
|
1135
|
+
...parentActivity.metadata,
|
|
1136
|
+
agentOptions: {
|
|
1137
|
+
name: agentOptions.name,
|
|
1138
|
+
modelId: this.aiHelper.getLanguageModelId(agentOptions.model),
|
|
1139
|
+
temperature: agentOptions.temperature,
|
|
1140
|
+
maxOutputTokens: agentOptions.maxOutputTokens,
|
|
1141
|
+
topP: agentOptions.topP,
|
|
1142
|
+
topK: agentOptions.topK,
|
|
1143
|
+
presencePenalty: agentOptions.presencePenalty,
|
|
1144
|
+
frequencyPenalty: agentOptions.frequencyPenalty,
|
|
1145
|
+
stopSequences: agentOptions.stopSequences,
|
|
1146
|
+
seed: agentOptions.seed,
|
|
1147
|
+
},
|
|
1148
|
+
},
|
|
1149
|
+
});
|
|
1150
|
+
if (!updatedAssistantMessage) {
|
|
1151
|
+
throw new Error(`Assistant message with id ${assistantMessage.id} not found after update`);
|
|
1152
|
+
}
|
|
1153
|
+
assistantMessage = updatedAssistantMessage;
|
|
1154
|
+
return {
|
|
1155
|
+
assistantMessage,
|
|
1156
|
+
agentOptions,
|
|
1157
|
+
executionTenants,
|
|
1158
|
+
};
|
|
1159
|
+
}
|
|
1160
|
+
async streamAgentResponseForUserMessage(params) {
|
|
1161
|
+
const { assistantMessage, agentOptions, executionTenants } = await this.prepareAgentResponseForUserMessage(params.chat, params.payload, params.previousMessages, params.userMessage, params.excludeSocketId, params.agentName);
|
|
1162
|
+
const messagesForAgent = [...params.previousMessages, params.userMessage];
|
|
1163
|
+
agentOptions.onStepFinish = (stepResult) => {
|
|
1164
|
+
return this.storeStepActivity({
|
|
1165
|
+
chat: params.chat,
|
|
1166
|
+
parentId: assistantMessage.activity,
|
|
1167
|
+
stepResult,
|
|
1168
|
+
name: ai_agent_types_1.ActivityOperationName.STEP_FINISHED,
|
|
1169
|
+
sourceId: assistantMessage.id,
|
|
1170
|
+
sourceType: 'AgentMessage',
|
|
1171
|
+
tenants: executionTenants,
|
|
1172
|
+
});
|
|
1173
|
+
};
|
|
1174
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(params.payload.userId ?? params.chat.userId ?? this.getChatUserId(params.chat) ?? 'guest', agentOptions.name);
|
|
1175
|
+
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
1176
|
+
await this.streamMessageStep({
|
|
1177
|
+
chat: params.chat,
|
|
1178
|
+
existingMessages: messagesForAgent,
|
|
1179
|
+
assistantMessage,
|
|
1180
|
+
agentOptions,
|
|
1181
|
+
excludeSocketId: params.excludeSocketId,
|
|
1182
|
+
signal: params.signal,
|
|
1183
|
+
tenants: executionTenants,
|
|
1184
|
+
executionContext: params.payload.executionContext,
|
|
1185
|
+
context: params.payload.context,
|
|
1186
|
+
});
|
|
1187
|
+
}
|
|
933
1188
|
async storeStepActivity(params) {
|
|
934
1189
|
try {
|
|
935
1190
|
const stepResult = params.stepResult;
|
|
936
1191
|
const mergedUsage = this.mergeUsageWithProviderMetadata(stepResult.usage, stepResult.providerMetadata);
|
|
937
1192
|
const activity = await this.activityRepository.create({
|
|
938
|
-
ownerId: params.chat
|
|
1193
|
+
ownerId: this.getChatUserId(params.chat) ?? 'guest',
|
|
939
1194
|
groupId: params.chat.id,
|
|
940
1195
|
name: params.name,
|
|
941
1196
|
tenants: params.tenants || {},
|
|
@@ -998,7 +1253,7 @@ class ChatProcessor {
|
|
|
998
1253
|
tenants: executionTenants,
|
|
999
1254
|
});
|
|
1000
1255
|
};
|
|
1001
|
-
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(chat
|
|
1256
|
+
const userPreferences = await this.agentConfigRepository.findByUserIdAndAgentName(this.getChatUserId(chat) ?? 'guest', agentOptions.name);
|
|
1002
1257
|
agentOptions.activeTools = this.getAvailableTools(agentOptions, userPreferences);
|
|
1003
1258
|
await this.streamMessageStep({
|
|
1004
1259
|
chat,
|
|
@@ -1128,6 +1383,86 @@ class ChatProcessor {
|
|
|
1128
1383
|
}
|
|
1129
1384
|
return stream;
|
|
1130
1385
|
}
|
|
1386
|
+
async upsertAndGetMultiUserChat(payload, excludeSocketId) {
|
|
1387
|
+
const targetUserId = payload.userId ?? 'guest';
|
|
1388
|
+
let chat = null;
|
|
1389
|
+
let isNewChat = false;
|
|
1390
|
+
if (payload.chatId) {
|
|
1391
|
+
chat = await this.chatRepository.findById(payload.chatId);
|
|
1392
|
+
if (!chat) {
|
|
1393
|
+
throw new Error('Chat not found');
|
|
1394
|
+
}
|
|
1395
|
+
if (!this.hasChatAccess(chat, targetUserId)) {
|
|
1396
|
+
throw new Error('Chat does not belong to this user');
|
|
1397
|
+
}
|
|
1398
|
+
const participantIds = Array.from(new Set([
|
|
1399
|
+
...this.getParticipantIds(chat),
|
|
1400
|
+
...(payload.participantIds ?? []),
|
|
1401
|
+
targetUserId,
|
|
1402
|
+
].filter(Boolean)));
|
|
1403
|
+
const updates = {};
|
|
1404
|
+
if (participantIds.length !== this.getParticipantIds(chat).length) {
|
|
1405
|
+
updates.participantIds = participantIds;
|
|
1406
|
+
}
|
|
1407
|
+
if ('model' in payload) {
|
|
1408
|
+
updates.model = payload.model || undefined;
|
|
1409
|
+
}
|
|
1410
|
+
if (Object.keys(updates).length > 0) {
|
|
1411
|
+
chat = await this.updateChatAndEmit(chat, updates, excludeSocketId);
|
|
1412
|
+
}
|
|
1413
|
+
}
|
|
1414
|
+
if (!chat) {
|
|
1415
|
+
chat = await this.createChat(payload, excludeSocketId);
|
|
1416
|
+
isNewChat = true;
|
|
1417
|
+
}
|
|
1418
|
+
return { chat, isNewChat };
|
|
1419
|
+
}
|
|
1420
|
+
async postMultiUserMessage(payload, excludeSocketId) {
|
|
1421
|
+
const { chat } = await this.upsertAndGetMultiUserChat(payload, excludeSocketId);
|
|
1422
|
+
const existingMessages = await this.messageRepository.findByChatId(chat.id);
|
|
1423
|
+
const sender = {
|
|
1424
|
+
id: payload.userId ?? 'guest',
|
|
1425
|
+
type: 'user',
|
|
1426
|
+
...(payload.senderDisplayName ? { displayName: payload.senderDisplayName } : {}),
|
|
1427
|
+
...(payload.senderAvatarUrl ? { avatarUrl: payload.senderAvatarUrl } : {}),
|
|
1428
|
+
};
|
|
1429
|
+
const userMessage = await this.createMessage(chat, ai_agent_types_1.MessageRole.User, {
|
|
1430
|
+
content: payload.content,
|
|
1431
|
+
agentName: undefined,
|
|
1432
|
+
sender,
|
|
1433
|
+
mentions: payload.mentions ?? [],
|
|
1434
|
+
}, payload.attachments, excludeSocketId);
|
|
1435
|
+
const decision = await this.shouldRunAgentForMultiUserMessage({
|
|
1436
|
+
chat,
|
|
1437
|
+
previousMessages: existingMessages,
|
|
1438
|
+
userMessage,
|
|
1439
|
+
payload,
|
|
1440
|
+
});
|
|
1441
|
+
if (!decision.shouldRun) {
|
|
1442
|
+
const updatedChat = await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Finished }, excludeSocketId);
|
|
1443
|
+
return { chat: updatedChat, message: userMessage, agentTriggered: false };
|
|
1444
|
+
}
|
|
1445
|
+
const updatedChat = await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Streaming }, undefined);
|
|
1446
|
+
const abortController = this.agentStore.registerAgentProcess(chat.id);
|
|
1447
|
+
const limitedMessages = helpers_1.ContextLimiter.limitContext(existingMessages, {
|
|
1448
|
+
maxMessages: this.config.ai.maxContextMessages,
|
|
1449
|
+
keepFirstUserMessage: true,
|
|
1450
|
+
keepSystemMessages: true,
|
|
1451
|
+
});
|
|
1452
|
+
void this.streamAgentResponseForUserMessage({
|
|
1453
|
+
chat,
|
|
1454
|
+
payload,
|
|
1455
|
+
previousMessages: limitedMessages,
|
|
1456
|
+
userMessage,
|
|
1457
|
+
excludeSocketId: undefined,
|
|
1458
|
+
signal: abortController.signal,
|
|
1459
|
+
agentName: decision.agentName,
|
|
1460
|
+
}).catch(async (error) => {
|
|
1461
|
+
logger_1.logger.error({ error, chatId: chat.id }, 'Multi-user background agent flow failed');
|
|
1462
|
+
await this.updateChatAndEmit(chat, { status: ai_agent_types_1.AgentStatus.Error }, undefined);
|
|
1463
|
+
});
|
|
1464
|
+
return { chat: updatedChat, message: userMessage, agentTriggered: true };
|
|
1465
|
+
}
|
|
1131
1466
|
async createMessageStream(payload, excludeSocketId, signal) {
|
|
1132
1467
|
const stream = new stream_1.PassThrough();
|
|
1133
1468
|
this.runMessageStream(stream, payload, excludeSocketId, signal)
|