@wundr.io/autogen-orchestrator 1.0.3
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/README.md +1088 -0
- package/dist/group-chat.d.ts +327 -0
- package/dist/group-chat.d.ts.map +1 -0
- package/dist/group-chat.js +724 -0
- package/dist/group-chat.js.map +1 -0
- package/dist/index.d.ts +16 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +69 -0
- package/dist/index.js.map +1 -0
- package/dist/nested-chat.d.ts +296 -0
- package/dist/nested-chat.d.ts.map +1 -0
- package/dist/nested-chat.js +600 -0
- package/dist/nested-chat.js.map +1 -0
- package/dist/speaker-selection.d.ts +195 -0
- package/dist/speaker-selection.d.ts.map +1 -0
- package/dist/speaker-selection.js +569 -0
- package/dist/speaker-selection.js.map +1 -0
- package/dist/termination.d.ts +237 -0
- package/dist/termination.d.ts.map +1 -0
- package/dist/termination.js +566 -0
- package/dist/termination.js.map +1 -0
- package/dist/types.d.ts +1248 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +201 -0
- package/dist/types.js.map +1 -0
- package/package.json +59 -0
- package/src/group-chat.ts +980 -0
- package/src/index.ts +145 -0
- package/src/nested-chat.ts +795 -0
- package/src/speaker-selection.ts +794 -0
- package/src/termination.ts +704 -0
- package/src/types.ts +876 -0
|
@@ -0,0 +1,724 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* GroupChatManager - Core orchestration for AutoGen-style multi-agent conversations
|
|
4
|
+
*
|
|
5
|
+
* Implements conversational patterns for coordinating multiple AI agents in a
|
|
6
|
+
* group chat setting with configurable speaker selection and termination conditions.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.GroupChatBuilder = exports.GroupChatManager = void 0;
|
|
10
|
+
exports.createParticipant = createParticipant;
|
|
11
|
+
const eventemitter3_1 = require("eventemitter3");
|
|
12
|
+
const uuid_1 = require("uuid");
|
|
13
|
+
const nested_chat_1 = require("./nested-chat");
|
|
14
|
+
const speaker_selection_1 = require("./speaker-selection");
|
|
15
|
+
const termination_1 = require("./termination");
|
|
16
|
+
const types_1 = require("./types");
|
|
17
|
+
/**
|
|
18
|
+
* GroupChatManager - Orchestrates multi-agent conversations
|
|
19
|
+
*/
|
|
20
|
+
class GroupChatManager extends eventemitter3_1.EventEmitter {
|
|
21
|
+
config;
|
|
22
|
+
participants = new Map();
|
|
23
|
+
messages = [];
|
|
24
|
+
context;
|
|
25
|
+
status = 'initializing';
|
|
26
|
+
startTime;
|
|
27
|
+
endTime;
|
|
28
|
+
chatId;
|
|
29
|
+
speakerManager;
|
|
30
|
+
terminationManager;
|
|
31
|
+
nestedChatManager;
|
|
32
|
+
responseGenerator;
|
|
33
|
+
metrics;
|
|
34
|
+
nestedResults = [];
|
|
35
|
+
/**
|
|
36
|
+
* Create a new GroupChatManager
|
|
37
|
+
* @param config - Group chat configuration
|
|
38
|
+
*/
|
|
39
|
+
constructor(config) {
|
|
40
|
+
super();
|
|
41
|
+
// Validate configuration
|
|
42
|
+
const validationResult = types_1.GroupChatConfigSchema.safeParse(config);
|
|
43
|
+
if (!validationResult.success) {
|
|
44
|
+
throw new Error(`Invalid GroupChatConfig: ${validationResult.error.message}`);
|
|
45
|
+
}
|
|
46
|
+
this.config = config;
|
|
47
|
+
this.chatId = config.id || (0, uuid_1.v4)();
|
|
48
|
+
// Initialize participants
|
|
49
|
+
for (const participant of config.participants) {
|
|
50
|
+
this.participants.set(participant.name, { ...participant });
|
|
51
|
+
}
|
|
52
|
+
// Initialize context
|
|
53
|
+
this.context = {
|
|
54
|
+
chatId: this.chatId,
|
|
55
|
+
currentRound: 0,
|
|
56
|
+
messageCount: 0,
|
|
57
|
+
activeParticipants: config.participants.map(p => p.name),
|
|
58
|
+
startTime: new Date(),
|
|
59
|
+
state: {},
|
|
60
|
+
};
|
|
61
|
+
// Initialize managers
|
|
62
|
+
this.speakerManager = new speaker_selection_1.SpeakerSelectionManager(config.speakerSelectionMethod);
|
|
63
|
+
this.terminationManager = new termination_1.TerminationManager(config.terminationConditions || []);
|
|
64
|
+
this.nestedChatManager = new nested_chat_1.NestedChatManager(config.nestedChatConfigs || []);
|
|
65
|
+
// Initialize metrics
|
|
66
|
+
this.metrics = {
|
|
67
|
+
totalTokens: 0,
|
|
68
|
+
avgResponseTimeMs: 0,
|
|
69
|
+
messagesPerParticipant: {},
|
|
70
|
+
tokensPerParticipant: {},
|
|
71
|
+
successfulResponses: 0,
|
|
72
|
+
failedResponses: 0,
|
|
73
|
+
};
|
|
74
|
+
// Setup nested chat event forwarding
|
|
75
|
+
this.setupNestedChatEvents();
|
|
76
|
+
}
|
|
77
|
+
/**
|
|
78
|
+
* Setup event forwarding from nested chat manager
|
|
79
|
+
*/
|
|
80
|
+
setupNestedChatEvents() {
|
|
81
|
+
this.nestedChatManager.on('nested:started', ({ nestedChatId }) => {
|
|
82
|
+
this.emit('nested:started', { chatId: this.chatId, nestedChatId });
|
|
83
|
+
});
|
|
84
|
+
this.nestedChatManager.on('nested:completed', ({ nestedChatId, result }) => {
|
|
85
|
+
const nestedResult = {
|
|
86
|
+
nestedChatId,
|
|
87
|
+
configId: '', // Will be filled properly
|
|
88
|
+
result,
|
|
89
|
+
parentMessageId: '',
|
|
90
|
+
};
|
|
91
|
+
this.nestedResults.push(nestedResult);
|
|
92
|
+
this.emit('nested:ended', {
|
|
93
|
+
chatId: this.chatId,
|
|
94
|
+
nestedChatId,
|
|
95
|
+
result: nestedResult,
|
|
96
|
+
});
|
|
97
|
+
});
|
|
98
|
+
}
|
|
99
|
+
/**
|
|
100
|
+
* Set the response generator function
|
|
101
|
+
* @param generator - Function to generate participant responses
|
|
102
|
+
*/
|
|
103
|
+
setResponseGenerator(generator) {
|
|
104
|
+
this.responseGenerator = generator;
|
|
105
|
+
}
|
|
106
|
+
/**
|
|
107
|
+
* Start the group chat
|
|
108
|
+
* @param options - Optional start options
|
|
109
|
+
* @returns Chat result when completed
|
|
110
|
+
*/
|
|
111
|
+
async start(options = {}) {
|
|
112
|
+
if (this.status !== 'initializing') {
|
|
113
|
+
throw new Error(`Cannot start chat in status: ${this.status}`);
|
|
114
|
+
}
|
|
115
|
+
this.status = 'active';
|
|
116
|
+
this.startTime = new Date();
|
|
117
|
+
this.context.startTime = this.startTime;
|
|
118
|
+
// Initialize state if provided
|
|
119
|
+
if (options.initialState) {
|
|
120
|
+
this.context.state = { ...options.initialState };
|
|
121
|
+
}
|
|
122
|
+
this.emitEvent('chat_started', { config: this.config });
|
|
123
|
+
this.emit('chat:started', { chatId: this.chatId, config: this.config });
|
|
124
|
+
try {
|
|
125
|
+
// Add initial message if provided
|
|
126
|
+
if (options.initialMessage) {
|
|
127
|
+
const senderName = options.initialSender || this.config.adminName || 'user';
|
|
128
|
+
await this.addMessage({
|
|
129
|
+
role: 'user',
|
|
130
|
+
content: options.initialMessage,
|
|
131
|
+
name: senderName,
|
|
132
|
+
});
|
|
133
|
+
}
|
|
134
|
+
// Run the conversation loop
|
|
135
|
+
const result = await this.runConversationLoop(options);
|
|
136
|
+
return result;
|
|
137
|
+
}
|
|
138
|
+
catch (error) {
|
|
139
|
+
return this.handleError(error);
|
|
140
|
+
}
|
|
141
|
+
}
|
|
142
|
+
/**
|
|
143
|
+
* Run the main conversation loop
|
|
144
|
+
* @param options - Start options
|
|
145
|
+
* @returns Chat result
|
|
146
|
+
*/
|
|
147
|
+
async runConversationLoop(options) {
|
|
148
|
+
const maxRounds = this.config.maxRounds || 100;
|
|
149
|
+
const maxMessages = this.config.maxMessages || 1000;
|
|
150
|
+
while (this.status === 'active') {
|
|
151
|
+
// Check termination conditions
|
|
152
|
+
const terminationResult = await this.terminationManager.evaluate(this.messages, Array.from(this.participants.values()), this.context);
|
|
153
|
+
if (terminationResult.shouldTerminate) {
|
|
154
|
+
return this.endChat('completed', terminationResult.reason);
|
|
155
|
+
}
|
|
156
|
+
// Check limits
|
|
157
|
+
if (this.context.currentRound >= maxRounds) {
|
|
158
|
+
return this.endChat('terminated', `Maximum rounds reached: ${maxRounds}`);
|
|
159
|
+
}
|
|
160
|
+
if (this.messages.length >= maxMessages) {
|
|
161
|
+
return this.endChat('terminated', `Maximum messages reached: ${maxMessages}`);
|
|
162
|
+
}
|
|
163
|
+
// Start new round
|
|
164
|
+
this.context.currentRound++;
|
|
165
|
+
this.emitEvent('round_started', { round: this.context.currentRound });
|
|
166
|
+
this.emit('round:started', {
|
|
167
|
+
chatId: this.chatId,
|
|
168
|
+
round: this.context.currentRound,
|
|
169
|
+
});
|
|
170
|
+
// Select next speaker
|
|
171
|
+
const skipSelection = options.skipInitialSelection && this.context.currentRound === 1;
|
|
172
|
+
if (!skipSelection) {
|
|
173
|
+
const selectionResult = await this.speakerManager.selectSpeaker(Array.from(this.participants.values()), this.messages, this.context, this.config.speakerSelectionConfig);
|
|
174
|
+
this.context.previousSpeaker = this.context.currentSpeaker;
|
|
175
|
+
this.context.currentSpeaker = selectionResult.speaker;
|
|
176
|
+
this.emitEvent('speaker_selected', {
|
|
177
|
+
speaker: selectionResult.speaker,
|
|
178
|
+
reason: selectionResult.reason,
|
|
179
|
+
});
|
|
180
|
+
this.emit('speaker:selected', {
|
|
181
|
+
chatId: this.chatId,
|
|
182
|
+
speaker: selectionResult.speaker,
|
|
183
|
+
reason: selectionResult.reason,
|
|
184
|
+
});
|
|
185
|
+
// Generate and add response
|
|
186
|
+
const participant = this.participants.get(selectionResult.speaker);
|
|
187
|
+
if (participant) {
|
|
188
|
+
const response = await this.generateResponse(participant);
|
|
189
|
+
if (response) {
|
|
190
|
+
const message = await this.addMessage({
|
|
191
|
+
role: 'assistant',
|
|
192
|
+
content: response,
|
|
193
|
+
name: participant.name,
|
|
194
|
+
});
|
|
195
|
+
// Check for nested chat triggers
|
|
196
|
+
if (this.config.allowNestedChats) {
|
|
197
|
+
await this.checkNestedChatTriggers(message);
|
|
198
|
+
}
|
|
199
|
+
}
|
|
200
|
+
}
|
|
201
|
+
}
|
|
202
|
+
// End round
|
|
203
|
+
this.emitEvent('round_ended', { round: this.context.currentRound });
|
|
204
|
+
this.emit('round:ended', {
|
|
205
|
+
chatId: this.chatId,
|
|
206
|
+
round: this.context.currentRound,
|
|
207
|
+
});
|
|
208
|
+
}
|
|
209
|
+
// If we exit the loop due to status change
|
|
210
|
+
return this.endChat(this.status, 'Chat stopped');
|
|
211
|
+
}
|
|
212
|
+
/**
|
|
213
|
+
* Generate a response for a participant
|
|
214
|
+
* @param participant - Participant to generate response for
|
|
215
|
+
* @returns Generated response content
|
|
216
|
+
*/
|
|
217
|
+
async generateResponse(participant) {
|
|
218
|
+
const startTime = Date.now();
|
|
219
|
+
try {
|
|
220
|
+
// Update participant status
|
|
221
|
+
participant.status = 'busy';
|
|
222
|
+
let response;
|
|
223
|
+
if (this.responseGenerator) {
|
|
224
|
+
response = await this.responseGenerator(participant, this.messages, this.context);
|
|
225
|
+
}
|
|
226
|
+
else {
|
|
227
|
+
// Default placeholder response
|
|
228
|
+
response = this.generatePlaceholderResponse(participant);
|
|
229
|
+
}
|
|
230
|
+
// Update metrics
|
|
231
|
+
const latency = Date.now() - startTime;
|
|
232
|
+
this.updateMetrics(participant.name, latency, response.length);
|
|
233
|
+
participant.status = 'idle';
|
|
234
|
+
this.metrics.successfulResponses++;
|
|
235
|
+
return response;
|
|
236
|
+
}
|
|
237
|
+
catch (error) {
|
|
238
|
+
participant.status = 'error';
|
|
239
|
+
this.metrics.failedResponses++;
|
|
240
|
+
const errorMessage = error instanceof Error ? error.message : String(error);
|
|
241
|
+
console.error(`Error generating response for ${participant.name}: ${errorMessage}`);
|
|
242
|
+
return null;
|
|
243
|
+
}
|
|
244
|
+
}
|
|
245
|
+
/**
|
|
246
|
+
* Generate a placeholder response when no generator is set
|
|
247
|
+
* @param participant - Participant to generate for
|
|
248
|
+
* @returns Placeholder response
|
|
249
|
+
*/
|
|
250
|
+
generatePlaceholderResponse(participant) {
|
|
251
|
+
const prompts = [
|
|
252
|
+
`[${participant.name}]: I acknowledge the message and am ready to contribute.`,
|
|
253
|
+
`[${participant.name}]: Based on my expertise in ${participant.capabilities.join(', ')}, I suggest we proceed.`,
|
|
254
|
+
`[${participant.name}]: Let me analyze this from my perspective.`,
|
|
255
|
+
];
|
|
256
|
+
return prompts[Math.floor(Math.random() * prompts.length)];
|
|
257
|
+
}
|
|
258
|
+
/**
|
|
259
|
+
* Update metrics after a response
|
|
260
|
+
* @param participantName - Name of the participant
|
|
261
|
+
* @param latencyMs - Response latency
|
|
262
|
+
* @param tokenEstimate - Estimated token count
|
|
263
|
+
*/
|
|
264
|
+
updateMetrics(participantName, latencyMs, tokenEstimate) {
|
|
265
|
+
// Update per-participant metrics
|
|
266
|
+
this.metrics.messagesPerParticipant[participantName] =
|
|
267
|
+
(this.metrics.messagesPerParticipant[participantName] || 0) + 1;
|
|
268
|
+
const estimatedTokens = Math.ceil(tokenEstimate / 4); // Rough estimate
|
|
269
|
+
this.metrics.tokensPerParticipant[participantName] =
|
|
270
|
+
(this.metrics.tokensPerParticipant[participantName] || 0) +
|
|
271
|
+
estimatedTokens;
|
|
272
|
+
this.metrics.totalTokens += estimatedTokens;
|
|
273
|
+
// Update average response time
|
|
274
|
+
const totalResponses = this.metrics.successfulResponses + this.metrics.failedResponses;
|
|
275
|
+
this.metrics.avgResponseTimeMs =
|
|
276
|
+
(this.metrics.avgResponseTimeMs * (totalResponses - 1) + latencyMs) /
|
|
277
|
+
totalResponses;
|
|
278
|
+
}
|
|
279
|
+
/**
|
|
280
|
+
* Check if a message triggers a nested chat
|
|
281
|
+
* @param message - Message to check
|
|
282
|
+
*/
|
|
283
|
+
async checkNestedChatTriggers(message) {
|
|
284
|
+
const triggeredConfig = this.nestedChatManager.checkTrigger(message, Array.from(this.participants.values()), this.context);
|
|
285
|
+
if (triggeredConfig) {
|
|
286
|
+
await this.runNestedChat(triggeredConfig, message.id);
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
/**
|
|
290
|
+
* Run a nested chat session
|
|
291
|
+
* @param config - Nested chat configuration
|
|
292
|
+
* @param triggerMessageId - ID of the triggering message
|
|
293
|
+
*/
|
|
294
|
+
async runNestedChat(config, triggerMessageId) {
|
|
295
|
+
const nestedChatId = this.nestedChatManager.startNestedChat(config, this.chatId, triggerMessageId, Array.from(this.participants.values()), this.context);
|
|
296
|
+
// Run nested chat rounds
|
|
297
|
+
const maxRounds = config.maxRounds || 5;
|
|
298
|
+
let round = 0;
|
|
299
|
+
while (round < maxRounds && this.nestedChatManager.hasActiveChats()) {
|
|
300
|
+
const nestedContext = this.nestedChatManager.getContext(nestedChatId);
|
|
301
|
+
if (!nestedContext) {
|
|
302
|
+
break;
|
|
303
|
+
}
|
|
304
|
+
// Select speaker for nested chat
|
|
305
|
+
const nestedState = this.nestedChatManager.getActiveChat(nestedChatId);
|
|
306
|
+
if (!nestedState) {
|
|
307
|
+
break;
|
|
308
|
+
}
|
|
309
|
+
const selectionResult = await this.speakerManager.selectSpeaker(nestedState.participants, nestedState.messages, nestedContext);
|
|
310
|
+
// Generate response
|
|
311
|
+
const participant = nestedState.participants.find(p => p.name === selectionResult.speaker);
|
|
312
|
+
if (participant && this.responseGenerator) {
|
|
313
|
+
const response = await this.responseGenerator(participant, nestedState.messages, nestedContext);
|
|
314
|
+
if (response) {
|
|
315
|
+
this.nestedChatManager.addMessage(nestedChatId, {
|
|
316
|
+
id: (0, uuid_1.v4)(),
|
|
317
|
+
role: 'assistant',
|
|
318
|
+
content: response,
|
|
319
|
+
name: participant.name,
|
|
320
|
+
timestamp: new Date(),
|
|
321
|
+
status: 'delivered',
|
|
322
|
+
});
|
|
323
|
+
}
|
|
324
|
+
}
|
|
325
|
+
this.nestedChatManager.incrementRound(nestedChatId);
|
|
326
|
+
round++;
|
|
327
|
+
}
|
|
328
|
+
// End nested chat
|
|
329
|
+
const result = await this.nestedChatManager.endNestedChat(nestedChatId, 'completed', `Completed after ${round} rounds`);
|
|
330
|
+
this.nestedResults.push(result);
|
|
331
|
+
// Add summary to main chat if available
|
|
332
|
+
if (result.summary) {
|
|
333
|
+
await this.addMessage({
|
|
334
|
+
role: 'system',
|
|
335
|
+
content: `[Nested Chat Summary]: ${result.summary}`,
|
|
336
|
+
name: 'system',
|
|
337
|
+
});
|
|
338
|
+
}
|
|
339
|
+
}
|
|
340
|
+
/**
|
|
341
|
+
* Add a message to the chat
|
|
342
|
+
* @param options - Message options
|
|
343
|
+
* @returns Created message
|
|
344
|
+
*/
|
|
345
|
+
async addMessage(options) {
|
|
346
|
+
const message = {
|
|
347
|
+
id: (0, uuid_1.v4)(),
|
|
348
|
+
role: options.role,
|
|
349
|
+
content: options.content,
|
|
350
|
+
name: options.name,
|
|
351
|
+
timestamp: new Date(),
|
|
352
|
+
contentType: options.contentType || 'text',
|
|
353
|
+
functionCall: options.functionCall,
|
|
354
|
+
metadata: {
|
|
355
|
+
...options.metadata,
|
|
356
|
+
tokenCount: Math.ceil(options.content.length / 4),
|
|
357
|
+
},
|
|
358
|
+
status: 'delivered',
|
|
359
|
+
};
|
|
360
|
+
this.messages.push(message);
|
|
361
|
+
this.context.messageCount = this.messages.length;
|
|
362
|
+
this.emitEvent('message_received', { message });
|
|
363
|
+
this.emit('message:received', { chatId: this.chatId, message });
|
|
364
|
+
return message;
|
|
365
|
+
}
|
|
366
|
+
/**
|
|
367
|
+
* Add a participant to the chat
|
|
368
|
+
* @param options - Participant options
|
|
369
|
+
* @returns Created participant
|
|
370
|
+
*/
|
|
371
|
+
addParticipant(options) {
|
|
372
|
+
const participant = {
|
|
373
|
+
id: (0, uuid_1.v4)(),
|
|
374
|
+
name: options.name,
|
|
375
|
+
type: options.type,
|
|
376
|
+
systemPrompt: options.systemPrompt,
|
|
377
|
+
status: 'active',
|
|
378
|
+
capabilities: options.capabilities || [],
|
|
379
|
+
modelConfig: options.modelConfig,
|
|
380
|
+
functions: options.functions,
|
|
381
|
+
maxConsecutiveReplies: options.maxConsecutiveReplies,
|
|
382
|
+
description: options.description,
|
|
383
|
+
};
|
|
384
|
+
this.participants.set(participant.name, participant);
|
|
385
|
+
this.context.activeParticipants.push(participant.name);
|
|
386
|
+
return participant;
|
|
387
|
+
}
|
|
388
|
+
/**
|
|
389
|
+
* Remove a participant from the chat
|
|
390
|
+
* @param name - Participant name
|
|
391
|
+
*/
|
|
392
|
+
removeParticipant(name) {
|
|
393
|
+
this.participants.delete(name);
|
|
394
|
+
this.context.activeParticipants = this.context.activeParticipants.filter(n => n !== name);
|
|
395
|
+
}
|
|
396
|
+
/**
|
|
397
|
+
* Update a participant's status
|
|
398
|
+
* @param name - Participant name
|
|
399
|
+
* @param status - New status
|
|
400
|
+
*/
|
|
401
|
+
updateParticipantStatus(name, status) {
|
|
402
|
+
const participant = this.participants.get(name);
|
|
403
|
+
if (participant) {
|
|
404
|
+
participant.status = status;
|
|
405
|
+
}
|
|
406
|
+
}
|
|
407
|
+
/**
|
|
408
|
+
* Add a termination condition
|
|
409
|
+
* @param condition - Condition to add
|
|
410
|
+
*/
|
|
411
|
+
addTerminationCondition(condition) {
|
|
412
|
+
this.terminationManager.addCondition(condition);
|
|
413
|
+
}
|
|
414
|
+
/**
|
|
415
|
+
* Add a nested chat configuration
|
|
416
|
+
* @param config - Nested chat config
|
|
417
|
+
*/
|
|
418
|
+
addNestedChatConfig(config) {
|
|
419
|
+
this.nestedChatManager.addConfig(config);
|
|
420
|
+
}
|
|
421
|
+
/**
|
|
422
|
+
* Pause the chat
|
|
423
|
+
*/
|
|
424
|
+
pause() {
|
|
425
|
+
if (this.status === 'active') {
|
|
426
|
+
this.status = 'paused';
|
|
427
|
+
}
|
|
428
|
+
}
|
|
429
|
+
/**
|
|
430
|
+
* Resume the chat
|
|
431
|
+
*/
|
|
432
|
+
resume() {
|
|
433
|
+
if (this.status === 'paused') {
|
|
434
|
+
this.status = 'active';
|
|
435
|
+
}
|
|
436
|
+
}
|
|
437
|
+
/**
|
|
438
|
+
* Stop the chat
|
|
439
|
+
* @param reason - Reason for stopping
|
|
440
|
+
* @returns Chat result
|
|
441
|
+
*/
|
|
442
|
+
async stop(reason) {
|
|
443
|
+
return this.endChat('terminated', reason || 'Manually stopped');
|
|
444
|
+
}
|
|
445
|
+
/**
|
|
446
|
+
* End the chat and produce a result
|
|
447
|
+
* @param status - Final status
|
|
448
|
+
* @param reason - Termination reason
|
|
449
|
+
* @returns Chat result
|
|
450
|
+
*/
|
|
451
|
+
endChat(status, reason) {
|
|
452
|
+
this.status = status;
|
|
453
|
+
this.endTime = new Date();
|
|
454
|
+
const durationMs = this.endTime.getTime() -
|
|
455
|
+
(this.startTime?.getTime() || this.endTime.getTime());
|
|
456
|
+
const result = {
|
|
457
|
+
chatId: this.chatId,
|
|
458
|
+
status,
|
|
459
|
+
messages: [...this.messages],
|
|
460
|
+
summary: this.generateSummary(),
|
|
461
|
+
terminationReason: reason,
|
|
462
|
+
totalRounds: this.context.currentRound,
|
|
463
|
+
totalMessages: this.messages.length,
|
|
464
|
+
participants: Array.from(this.participants.keys()),
|
|
465
|
+
durationMs,
|
|
466
|
+
metrics: { ...this.metrics },
|
|
467
|
+
nestedResults: [...this.nestedResults],
|
|
468
|
+
startedAt: this.startTime || new Date(),
|
|
469
|
+
endedAt: this.endTime,
|
|
470
|
+
};
|
|
471
|
+
if (reason) {
|
|
472
|
+
this.emitEvent('termination_triggered', { reason });
|
|
473
|
+
this.emit('termination:triggered', { chatId: this.chatId, reason });
|
|
474
|
+
}
|
|
475
|
+
this.emitEvent('chat_ended', { result });
|
|
476
|
+
this.emit('chat:ended', { chatId: this.chatId, result });
|
|
477
|
+
return result;
|
|
478
|
+
}
|
|
479
|
+
/**
|
|
480
|
+
* Handle an error during chat execution
|
|
481
|
+
* @param error - Error that occurred
|
|
482
|
+
* @returns Chat result with error
|
|
483
|
+
*/
|
|
484
|
+
handleError(error) {
|
|
485
|
+
const chatError = {
|
|
486
|
+
code: 'CHAT_ERROR',
|
|
487
|
+
message: error instanceof Error ? error.message : String(error),
|
|
488
|
+
stack: error instanceof Error ? error.stack : undefined,
|
|
489
|
+
context: {
|
|
490
|
+
round: this.context.currentRound,
|
|
491
|
+
messageCount: this.messages.length,
|
|
492
|
+
},
|
|
493
|
+
recoverable: false,
|
|
494
|
+
};
|
|
495
|
+
this.emit('chat:error', { chatId: this.chatId, error: chatError });
|
|
496
|
+
const result = this.endChat('error', chatError.message);
|
|
497
|
+
result.error = chatError;
|
|
498
|
+
return result;
|
|
499
|
+
}
|
|
500
|
+
/**
|
|
501
|
+
* Generate a summary of the conversation
|
|
502
|
+
* @returns Summary string
|
|
503
|
+
*/
|
|
504
|
+
generateSummary() {
|
|
505
|
+
const participantCounts = this.metrics.messagesPerParticipant;
|
|
506
|
+
const topContributors = Object.entries(participantCounts)
|
|
507
|
+
.sort(([, a], [, b]) => b - a)
|
|
508
|
+
.slice(0, 3)
|
|
509
|
+
.map(([name, count]) => `${name} (${count})`)
|
|
510
|
+
.join(', ');
|
|
511
|
+
return (`Chat completed with ${this.messages.length} messages over ${this.context.currentRound} rounds. ` +
|
|
512
|
+
`Top contributors: ${topContributors || 'none'}. ` +
|
|
513
|
+
`Duration: ${Math.round((this.endTime?.getTime() || Date.now()) - (this.startTime?.getTime() || Date.now())) / 1000}s.`);
|
|
514
|
+
}
|
|
515
|
+
/**
|
|
516
|
+
* Emit a chat event
|
|
517
|
+
* @param type - Event type
|
|
518
|
+
* @param data - Event data typed based on event type
|
|
519
|
+
*/
|
|
520
|
+
emitEvent(type, data) {
|
|
521
|
+
const event = {
|
|
522
|
+
type,
|
|
523
|
+
timestamp: new Date(),
|
|
524
|
+
chatId: this.chatId,
|
|
525
|
+
data,
|
|
526
|
+
};
|
|
527
|
+
// Internal event tracking if needed
|
|
528
|
+
this.context.state['lastEvent'] = event;
|
|
529
|
+
}
|
|
530
|
+
/**
|
|
531
|
+
* Get the current chat status
|
|
532
|
+
* @returns Current status
|
|
533
|
+
*/
|
|
534
|
+
getStatus() {
|
|
535
|
+
return this.status;
|
|
536
|
+
}
|
|
537
|
+
/**
|
|
538
|
+
* Get the chat ID
|
|
539
|
+
* @returns Chat ID
|
|
540
|
+
*/
|
|
541
|
+
getChatId() {
|
|
542
|
+
return this.chatId;
|
|
543
|
+
}
|
|
544
|
+
/**
|
|
545
|
+
* Get all messages
|
|
546
|
+
* @returns Message array
|
|
547
|
+
*/
|
|
548
|
+
getMessages() {
|
|
549
|
+
return [...this.messages];
|
|
550
|
+
}
|
|
551
|
+
/**
|
|
552
|
+
* Get all participants
|
|
553
|
+
* @returns Participant array
|
|
554
|
+
*/
|
|
555
|
+
getParticipants() {
|
|
556
|
+
return Array.from(this.participants.values());
|
|
557
|
+
}
|
|
558
|
+
/**
|
|
559
|
+
* Get the current context
|
|
560
|
+
* @returns Chat context
|
|
561
|
+
*/
|
|
562
|
+
getContext() {
|
|
563
|
+
return { ...this.context };
|
|
564
|
+
}
|
|
565
|
+
/**
|
|
566
|
+
* Get current metrics
|
|
567
|
+
* @returns Chat metrics
|
|
568
|
+
*/
|
|
569
|
+
getMetrics() {
|
|
570
|
+
return { ...this.metrics };
|
|
571
|
+
}
|
|
572
|
+
/**
|
|
573
|
+
* Update context state
|
|
574
|
+
* @param key - State key
|
|
575
|
+
* @param value - State value (typed for common use cases)
|
|
576
|
+
*/
|
|
577
|
+
updateState(key, value) {
|
|
578
|
+
this.context.state[key] = value;
|
|
579
|
+
}
|
|
580
|
+
/**
|
|
581
|
+
* Get context state value
|
|
582
|
+
* @param key - State key
|
|
583
|
+
* @returns State value
|
|
584
|
+
*/
|
|
585
|
+
getState(key) {
|
|
586
|
+
return this.context.state[key];
|
|
587
|
+
}
|
|
588
|
+
}
|
|
589
|
+
exports.GroupChatManager = GroupChatManager;
|
|
590
|
+
/**
|
|
591
|
+
* Builder for creating GroupChatManager instances
|
|
592
|
+
*/
|
|
593
|
+
class GroupChatBuilder {
|
|
594
|
+
config = {
|
|
595
|
+
participants: [],
|
|
596
|
+
terminationConditions: [],
|
|
597
|
+
nestedChatConfigs: [],
|
|
598
|
+
};
|
|
599
|
+
/**
|
|
600
|
+
* Set the chat name
|
|
601
|
+
* @param name - Chat name
|
|
602
|
+
*/
|
|
603
|
+
withName(name) {
|
|
604
|
+
this.config.name = name;
|
|
605
|
+
return this;
|
|
606
|
+
}
|
|
607
|
+
/**
|
|
608
|
+
* Set the chat description
|
|
609
|
+
* @param description - Chat description
|
|
610
|
+
*/
|
|
611
|
+
withDescription(description) {
|
|
612
|
+
this.config.description = description;
|
|
613
|
+
return this;
|
|
614
|
+
}
|
|
615
|
+
/**
|
|
616
|
+
* Add a participant
|
|
617
|
+
* @param participant - Participant to add
|
|
618
|
+
*/
|
|
619
|
+
withParticipant(participant) {
|
|
620
|
+
this.config.participants = this.config.participants || [];
|
|
621
|
+
this.config.participants.push(participant);
|
|
622
|
+
return this;
|
|
623
|
+
}
|
|
624
|
+
/**
|
|
625
|
+
* Set the speaker selection method
|
|
626
|
+
* @param method - Selection method
|
|
627
|
+
*/
|
|
628
|
+
withSpeakerSelection(method) {
|
|
629
|
+
this.config.speakerSelectionMethod = method;
|
|
630
|
+
return this;
|
|
631
|
+
}
|
|
632
|
+
/**
|
|
633
|
+
* Set maximum rounds
|
|
634
|
+
* @param maxRounds - Maximum rounds
|
|
635
|
+
*/
|
|
636
|
+
withMaxRounds(maxRounds) {
|
|
637
|
+
this.config.maxRounds = maxRounds;
|
|
638
|
+
return this;
|
|
639
|
+
}
|
|
640
|
+
/**
|
|
641
|
+
* Set maximum messages
|
|
642
|
+
* @param maxMessages - Maximum messages
|
|
643
|
+
*/
|
|
644
|
+
withMaxMessages(maxMessages) {
|
|
645
|
+
this.config.maxMessages = maxMessages;
|
|
646
|
+
return this;
|
|
647
|
+
}
|
|
648
|
+
/**
|
|
649
|
+
* Add a termination condition
|
|
650
|
+
* @param condition - Termination condition
|
|
651
|
+
*/
|
|
652
|
+
withTerminationCondition(condition) {
|
|
653
|
+
this.config.terminationConditions = this.config.terminationConditions || [];
|
|
654
|
+
this.config.terminationConditions.push(condition);
|
|
655
|
+
return this;
|
|
656
|
+
}
|
|
657
|
+
/**
|
|
658
|
+
* Enable nested chats
|
|
659
|
+
*/
|
|
660
|
+
withNestedChats() {
|
|
661
|
+
this.config.allowNestedChats = true;
|
|
662
|
+
return this;
|
|
663
|
+
}
|
|
664
|
+
/**
|
|
665
|
+
* Add a nested chat configuration
|
|
666
|
+
* @param config - Nested chat config
|
|
667
|
+
*/
|
|
668
|
+
withNestedChatConfig(config) {
|
|
669
|
+
this.config.nestedChatConfigs = this.config.nestedChatConfigs || [];
|
|
670
|
+
this.config.nestedChatConfigs.push(config);
|
|
671
|
+
return this;
|
|
672
|
+
}
|
|
673
|
+
/**
|
|
674
|
+
* Set the admin name
|
|
675
|
+
* @param name - Admin name
|
|
676
|
+
*/
|
|
677
|
+
withAdmin(name) {
|
|
678
|
+
this.config.adminName = name;
|
|
679
|
+
return this;
|
|
680
|
+
}
|
|
681
|
+
/**
|
|
682
|
+
* Set timeout
|
|
683
|
+
* @param timeoutMs - Timeout in milliseconds
|
|
684
|
+
*/
|
|
685
|
+
withTimeout(timeoutMs) {
|
|
686
|
+
this.config.timeoutMs = timeoutMs;
|
|
687
|
+
return this;
|
|
688
|
+
}
|
|
689
|
+
/**
|
|
690
|
+
* Build the GroupChatManager
|
|
691
|
+
* @returns Configured GroupChatManager
|
|
692
|
+
*/
|
|
693
|
+
build() {
|
|
694
|
+
if (!this.config.name) {
|
|
695
|
+
this.config.name = `group-chat-${(0, uuid_1.v4)().slice(0, 8)}`;
|
|
696
|
+
}
|
|
697
|
+
if (!this.config.speakerSelectionMethod) {
|
|
698
|
+
this.config.speakerSelectionMethod = 'round_robin';
|
|
699
|
+
}
|
|
700
|
+
if (!this.config.participants || this.config.participants.length < 2) {
|
|
701
|
+
throw new Error('GroupChat requires at least 2 participants');
|
|
702
|
+
}
|
|
703
|
+
return new GroupChatManager(this.config);
|
|
704
|
+
}
|
|
705
|
+
}
|
|
706
|
+
exports.GroupChatBuilder = GroupChatBuilder;
|
|
707
|
+
/**
|
|
708
|
+
* Create a simple participant configuration
|
|
709
|
+
* @param name - Participant name
|
|
710
|
+
* @param systemPrompt - System prompt
|
|
711
|
+
* @param capabilities - Participant capabilities
|
|
712
|
+
* @returns Participant configuration
|
|
713
|
+
*/
|
|
714
|
+
function createParticipant(name, systemPrompt, capabilities = []) {
|
|
715
|
+
return {
|
|
716
|
+
id: (0, uuid_1.v4)(),
|
|
717
|
+
name,
|
|
718
|
+
type: 'agent',
|
|
719
|
+
systemPrompt,
|
|
720
|
+
status: 'active',
|
|
721
|
+
capabilities,
|
|
722
|
+
};
|
|
723
|
+
}
|
|
724
|
+
//# sourceMappingURL=group-chat.js.map
|