@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,704 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Termination Condition Handlers for AutoGen-style Group Chat
|
|
3
|
+
*
|
|
4
|
+
* Implements various termination conditions for multi-agent conversations
|
|
5
|
+
* including keyword-based, round-based, timeout, and custom evaluators.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type {
|
|
9
|
+
Message,
|
|
10
|
+
ChatParticipant,
|
|
11
|
+
ChatContext,
|
|
12
|
+
TerminationCondition,
|
|
13
|
+
TerminationConditionType,
|
|
14
|
+
TerminationConditionValue,
|
|
15
|
+
TerminationResult,
|
|
16
|
+
TerminationEvaluator,
|
|
17
|
+
ConsensusConfigType,
|
|
18
|
+
} from './types';
|
|
19
|
+
|
|
20
|
+
/**
|
|
21
|
+
* Handler interface for termination conditions
|
|
22
|
+
*/
|
|
23
|
+
export interface TerminationHandler {
|
|
24
|
+
/**
|
|
25
|
+
* Evaluate whether the termination condition is met
|
|
26
|
+
* @param messages - Conversation messages
|
|
27
|
+
* @param participants - Chat participants
|
|
28
|
+
* @param context - Current chat context
|
|
29
|
+
* @returns Termination result
|
|
30
|
+
*/
|
|
31
|
+
evaluate(
|
|
32
|
+
messages: Message[],
|
|
33
|
+
participants: ChatParticipant[],
|
|
34
|
+
context: ChatContext
|
|
35
|
+
): Promise<TerminationResult>;
|
|
36
|
+
}
|
|
37
|
+
|
|
38
|
+
/**
|
|
39
|
+
* Type guard to check if value is a number
|
|
40
|
+
*/
|
|
41
|
+
function isNumber(value: TerminationConditionValue): value is number {
|
|
42
|
+
return typeof value === 'number';
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Type guard to check if value is a string or string array
|
|
47
|
+
*/
|
|
48
|
+
function isStringOrStringArray(
|
|
49
|
+
value: TerminationConditionValue,
|
|
50
|
+
): value is string | string[] {
|
|
51
|
+
return typeof value === 'string' || Array.isArray(value);
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
/**
|
|
55
|
+
* Type guard to check if value is a ConsensusConfigType
|
|
56
|
+
*/
|
|
57
|
+
function isConsensusConfig(
|
|
58
|
+
value: TerminationConditionValue,
|
|
59
|
+
): value is ConsensusConfigType {
|
|
60
|
+
return (
|
|
61
|
+
typeof value === 'object' &&
|
|
62
|
+
value !== null &&
|
|
63
|
+
'threshold' in value &&
|
|
64
|
+
'agreementKeywords' in value
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
/**
|
|
69
|
+
* Factory function to create termination handlers
|
|
70
|
+
* @param condition - Termination condition configuration
|
|
71
|
+
* @returns Appropriate termination handler
|
|
72
|
+
*/
|
|
73
|
+
export function createTerminationHandler(
|
|
74
|
+
condition: TerminationCondition,
|
|
75
|
+
): TerminationHandler {
|
|
76
|
+
switch (condition.type) {
|
|
77
|
+
case 'max_rounds':
|
|
78
|
+
if (!isNumber(condition.value)) {
|
|
79
|
+
throw new Error('max_rounds condition requires a number value');
|
|
80
|
+
}
|
|
81
|
+
return new MaxRoundsHandler(condition.value);
|
|
82
|
+
case 'max_messages':
|
|
83
|
+
if (!isNumber(condition.value)) {
|
|
84
|
+
throw new Error('max_messages condition requires a number value');
|
|
85
|
+
}
|
|
86
|
+
return new MaxMessagesHandler(condition.value);
|
|
87
|
+
case 'keyword':
|
|
88
|
+
if (!isStringOrStringArray(condition.value)) {
|
|
89
|
+
throw new Error(
|
|
90
|
+
'keyword condition requires a string or string[] value',
|
|
91
|
+
);
|
|
92
|
+
}
|
|
93
|
+
return new KeywordHandler(condition.value);
|
|
94
|
+
case 'timeout':
|
|
95
|
+
if (!isNumber(condition.value)) {
|
|
96
|
+
throw new Error('timeout condition requires a number value');
|
|
97
|
+
}
|
|
98
|
+
return new TimeoutHandler(condition.value);
|
|
99
|
+
case 'function':
|
|
100
|
+
if (!condition.evaluator) {
|
|
101
|
+
throw new Error('function condition requires an evaluator');
|
|
102
|
+
}
|
|
103
|
+
return new FunctionHandler(condition.evaluator);
|
|
104
|
+
case 'consensus':
|
|
105
|
+
if (!isConsensusConfig(condition.value)) {
|
|
106
|
+
throw new Error(
|
|
107
|
+
'consensus condition requires a ConsensusConfigType value',
|
|
108
|
+
);
|
|
109
|
+
}
|
|
110
|
+
return new ConsensusHandler(condition.value);
|
|
111
|
+
case 'custom':
|
|
112
|
+
return new CustomHandler(condition);
|
|
113
|
+
default: {
|
|
114
|
+
const exhaustiveCheck: never = condition.type;
|
|
115
|
+
throw new Error(`Unknown termination condition type: ${exhaustiveCheck}`);
|
|
116
|
+
}
|
|
117
|
+
}
|
|
118
|
+
}
|
|
119
|
+
|
|
120
|
+
/**
|
|
121
|
+
* Handler for maximum rounds termination
|
|
122
|
+
*/
|
|
123
|
+
export class MaxRoundsHandler implements TerminationHandler {
|
|
124
|
+
private maxRounds: number;
|
|
125
|
+
|
|
126
|
+
/**
|
|
127
|
+
* Create a max rounds handler
|
|
128
|
+
* @param maxRounds - Maximum number of rounds allowed
|
|
129
|
+
*/
|
|
130
|
+
constructor(maxRounds: number) {
|
|
131
|
+
this.maxRounds = maxRounds;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
/**
|
|
135
|
+
* Evaluate if max rounds has been reached
|
|
136
|
+
* @param messages - Conversation messages
|
|
137
|
+
* @param participants - Chat participants
|
|
138
|
+
* @param context - Current chat context
|
|
139
|
+
* @returns Termination result
|
|
140
|
+
*/
|
|
141
|
+
async evaluate(
|
|
142
|
+
_messages: Message[],
|
|
143
|
+
_participants: ChatParticipant[],
|
|
144
|
+
context: ChatContext,
|
|
145
|
+
): Promise<TerminationResult> {
|
|
146
|
+
const shouldTerminate = context.currentRound >= this.maxRounds;
|
|
147
|
+
|
|
148
|
+
return {
|
|
149
|
+
shouldTerminate,
|
|
150
|
+
reason: shouldTerminate
|
|
151
|
+
? `Maximum rounds reached: ${context.currentRound}/${this.maxRounds}`
|
|
152
|
+
: undefined,
|
|
153
|
+
summary: shouldTerminate
|
|
154
|
+
? `Conversation ended after ${this.maxRounds} rounds.`
|
|
155
|
+
: undefined,
|
|
156
|
+
};
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
/**
|
|
161
|
+
* Handler for maximum messages termination
|
|
162
|
+
*/
|
|
163
|
+
export class MaxMessagesHandler implements TerminationHandler {
|
|
164
|
+
private maxMessages: number;
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* Create a max messages handler
|
|
168
|
+
* @param maxMessages - Maximum number of messages allowed
|
|
169
|
+
*/
|
|
170
|
+
constructor(maxMessages: number) {
|
|
171
|
+
this.maxMessages = maxMessages;
|
|
172
|
+
}
|
|
173
|
+
|
|
174
|
+
/**
|
|
175
|
+
* Evaluate if max messages has been reached
|
|
176
|
+
* @param messages - Conversation messages
|
|
177
|
+
* @param participants - Chat participants
|
|
178
|
+
* @param context - Current chat context
|
|
179
|
+
* @returns Termination result
|
|
180
|
+
*/
|
|
181
|
+
async evaluate(
|
|
182
|
+
messages: Message[],
|
|
183
|
+
_participants: ChatParticipant[],
|
|
184
|
+
_context: ChatContext,
|
|
185
|
+
): Promise<TerminationResult> {
|
|
186
|
+
const shouldTerminate = messages.length >= this.maxMessages;
|
|
187
|
+
|
|
188
|
+
return {
|
|
189
|
+
shouldTerminate,
|
|
190
|
+
reason: shouldTerminate
|
|
191
|
+
? `Maximum messages reached: ${messages.length}/${this.maxMessages}`
|
|
192
|
+
: undefined,
|
|
193
|
+
summary: shouldTerminate
|
|
194
|
+
? `Conversation ended after ${this.maxMessages} messages.`
|
|
195
|
+
: undefined,
|
|
196
|
+
};
|
|
197
|
+
}
|
|
198
|
+
}
|
|
199
|
+
|
|
200
|
+
/**
|
|
201
|
+
* Handler for keyword-based termination
|
|
202
|
+
*/
|
|
203
|
+
export class KeywordHandler implements TerminationHandler {
|
|
204
|
+
private keywords: string[];
|
|
205
|
+
private caseSensitive: boolean;
|
|
206
|
+
private requireAll: boolean;
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* Create a keyword handler
|
|
210
|
+
* @param keywords - Keywords to detect
|
|
211
|
+
* @param caseSensitive - Whether matching is case-sensitive
|
|
212
|
+
* @param requireAll - Whether all keywords must be present
|
|
213
|
+
*/
|
|
214
|
+
constructor(
|
|
215
|
+
keywords: string | string[],
|
|
216
|
+
caseSensitive = false,
|
|
217
|
+
requireAll = false,
|
|
218
|
+
) {
|
|
219
|
+
this.keywords = Array.isArray(keywords) ? keywords : [keywords];
|
|
220
|
+
this.caseSensitive = caseSensitive;
|
|
221
|
+
this.requireAll = requireAll;
|
|
222
|
+
}
|
|
223
|
+
|
|
224
|
+
/**
|
|
225
|
+
* Evaluate if termination keywords are found
|
|
226
|
+
* @param messages - Conversation messages
|
|
227
|
+
* @param participants - Chat participants
|
|
228
|
+
* @param context - Current chat context
|
|
229
|
+
* @returns Termination result
|
|
230
|
+
*/
|
|
231
|
+
async evaluate(
|
|
232
|
+
messages: Message[],
|
|
233
|
+
_participants: ChatParticipant[],
|
|
234
|
+
_context: ChatContext,
|
|
235
|
+
): Promise<TerminationResult> {
|
|
236
|
+
const lastMessage = messages[messages.length - 1];
|
|
237
|
+
if (!lastMessage) {
|
|
238
|
+
return { shouldTerminate: false };
|
|
239
|
+
}
|
|
240
|
+
|
|
241
|
+
const content = this.caseSensitive
|
|
242
|
+
? lastMessage.content
|
|
243
|
+
: lastMessage.content.toLowerCase();
|
|
244
|
+
|
|
245
|
+
const matchedKeywords: string[] = [];
|
|
246
|
+
for (const keyword of this.keywords) {
|
|
247
|
+
const searchKeyword = this.caseSensitive
|
|
248
|
+
? keyword
|
|
249
|
+
: keyword.toLowerCase();
|
|
250
|
+
if (content.includes(searchKeyword)) {
|
|
251
|
+
matchedKeywords.push(keyword);
|
|
252
|
+
}
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
const shouldTerminate = this.requireAll
|
|
256
|
+
? matchedKeywords.length === this.keywords.length
|
|
257
|
+
: matchedKeywords.length > 0;
|
|
258
|
+
|
|
259
|
+
return {
|
|
260
|
+
shouldTerminate,
|
|
261
|
+
reason: shouldTerminate
|
|
262
|
+
? `Termination keyword(s) detected: ${matchedKeywords.join(', ')}`
|
|
263
|
+
: undefined,
|
|
264
|
+
summary: shouldTerminate
|
|
265
|
+
? `Conversation terminated by keyword: "${matchedKeywords[0]}"`
|
|
266
|
+
: undefined,
|
|
267
|
+
data: shouldTerminate ? { matchedKeywords } : undefined,
|
|
268
|
+
};
|
|
269
|
+
}
|
|
270
|
+
}
|
|
271
|
+
|
|
272
|
+
/**
|
|
273
|
+
* Handler for timeout-based termination
|
|
274
|
+
*/
|
|
275
|
+
export class TimeoutHandler implements TerminationHandler {
|
|
276
|
+
private timeoutMs: number;
|
|
277
|
+
|
|
278
|
+
/**
|
|
279
|
+
* Create a timeout handler
|
|
280
|
+
* @param timeoutMs - Timeout duration in milliseconds
|
|
281
|
+
*/
|
|
282
|
+
constructor(timeoutMs: number) {
|
|
283
|
+
this.timeoutMs = timeoutMs;
|
|
284
|
+
}
|
|
285
|
+
|
|
286
|
+
/**
|
|
287
|
+
* Evaluate if timeout has been reached
|
|
288
|
+
* @param messages - Conversation messages
|
|
289
|
+
* @param participants - Chat participants
|
|
290
|
+
* @param context - Current chat context
|
|
291
|
+
* @returns Termination result
|
|
292
|
+
*/
|
|
293
|
+
async evaluate(
|
|
294
|
+
_messages: Message[],
|
|
295
|
+
_participants: ChatParticipant[],
|
|
296
|
+
context: ChatContext,
|
|
297
|
+
): Promise<TerminationResult> {
|
|
298
|
+
const elapsed = Date.now() - context.startTime.getTime();
|
|
299
|
+
const shouldTerminate = elapsed >= this.timeoutMs;
|
|
300
|
+
|
|
301
|
+
return {
|
|
302
|
+
shouldTerminate,
|
|
303
|
+
reason: shouldTerminate
|
|
304
|
+
? `Timeout reached: ${Math.round(elapsed / 1000)}s / ${Math.round(this.timeoutMs / 1000)}s`
|
|
305
|
+
: undefined,
|
|
306
|
+
summary: shouldTerminate
|
|
307
|
+
? `Conversation timed out after ${Math.round(elapsed / 1000)} seconds.`
|
|
308
|
+
: undefined,
|
|
309
|
+
data: shouldTerminate
|
|
310
|
+
? { elapsedMs: elapsed, timeoutMs: this.timeoutMs }
|
|
311
|
+
: undefined,
|
|
312
|
+
};
|
|
313
|
+
}
|
|
314
|
+
}
|
|
315
|
+
|
|
316
|
+
/**
|
|
317
|
+
* Handler for function-based termination
|
|
318
|
+
*/
|
|
319
|
+
export class FunctionHandler implements TerminationHandler {
|
|
320
|
+
private evaluator: TerminationEvaluator;
|
|
321
|
+
|
|
322
|
+
/**
|
|
323
|
+
* Create a function handler
|
|
324
|
+
* @param evaluator - Custom evaluation function
|
|
325
|
+
*/
|
|
326
|
+
constructor(evaluator: TerminationEvaluator) {
|
|
327
|
+
this.evaluator = evaluator;
|
|
328
|
+
}
|
|
329
|
+
|
|
330
|
+
/**
|
|
331
|
+
* Evaluate using the custom function
|
|
332
|
+
* @param messages - Conversation messages
|
|
333
|
+
* @param participants - Chat participants
|
|
334
|
+
* @param context - Current chat context
|
|
335
|
+
* @returns Termination result
|
|
336
|
+
*/
|
|
337
|
+
async evaluate(
|
|
338
|
+
messages: Message[],
|
|
339
|
+
participants: ChatParticipant[],
|
|
340
|
+
context: ChatContext,
|
|
341
|
+
): Promise<TerminationResult> {
|
|
342
|
+
try {
|
|
343
|
+
return await this.evaluator(messages, participants, context);
|
|
344
|
+
} catch (error) {
|
|
345
|
+
const errorMessage =
|
|
346
|
+
error instanceof Error ? error.message : String(error);
|
|
347
|
+
return {
|
|
348
|
+
shouldTerminate: false,
|
|
349
|
+
reason: `Function evaluator error: ${errorMessage}`,
|
|
350
|
+
};
|
|
351
|
+
}
|
|
352
|
+
}
|
|
353
|
+
}
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* ConsensusConfig is re-exported for backwards compatibility
|
|
357
|
+
* @deprecated Use ConsensusConfigType from ./types instead
|
|
358
|
+
*/
|
|
359
|
+
export type ConsensusConfig = ConsensusConfigType;
|
|
360
|
+
|
|
361
|
+
/**
|
|
362
|
+
* Handler for consensus-based termination
|
|
363
|
+
*/
|
|
364
|
+
export class ConsensusHandler implements TerminationHandler {
|
|
365
|
+
private config: ConsensusConfigType;
|
|
366
|
+
|
|
367
|
+
/**
|
|
368
|
+
* Create a consensus handler
|
|
369
|
+
* @param config - Consensus configuration
|
|
370
|
+
*/
|
|
371
|
+
constructor(config: ConsensusConfigType) {
|
|
372
|
+
this.config = {
|
|
373
|
+
threshold: config.threshold,
|
|
374
|
+
agreementKeywords: config.agreementKeywords || [
|
|
375
|
+
'agree',
|
|
376
|
+
'consensus',
|
|
377
|
+
'approved',
|
|
378
|
+
'done',
|
|
379
|
+
'complete',
|
|
380
|
+
'finished',
|
|
381
|
+
],
|
|
382
|
+
disagreementKeywords: config.disagreementKeywords || [
|
|
383
|
+
'disagree',
|
|
384
|
+
'no',
|
|
385
|
+
'reject',
|
|
386
|
+
'veto',
|
|
387
|
+
'continue',
|
|
388
|
+
],
|
|
389
|
+
minParticipants: config.minParticipants || 2,
|
|
390
|
+
windowSize: config.windowSize || 10,
|
|
391
|
+
};
|
|
392
|
+
}
|
|
393
|
+
|
|
394
|
+
/**
|
|
395
|
+
* Evaluate if consensus has been reached
|
|
396
|
+
* @param messages - Conversation messages
|
|
397
|
+
* @param participants - Chat participants
|
|
398
|
+
* @param context - Current chat context
|
|
399
|
+
* @returns Termination result
|
|
400
|
+
*/
|
|
401
|
+
async evaluate(
|
|
402
|
+
messages: Message[],
|
|
403
|
+
_participants: ChatParticipant[],
|
|
404
|
+
_context: ChatContext,
|
|
405
|
+
): Promise<TerminationResult> {
|
|
406
|
+
const windowSize = this.config.windowSize || 10;
|
|
407
|
+
const recentMessages = messages.slice(-windowSize);
|
|
408
|
+
|
|
409
|
+
// Track votes per participant
|
|
410
|
+
const votes: Map<string, 'agree' | 'disagree' | 'neutral'> = new Map();
|
|
411
|
+
|
|
412
|
+
for (const message of recentMessages) {
|
|
413
|
+
const contentLower = message.content.toLowerCase();
|
|
414
|
+
|
|
415
|
+
// Check for agreement keywords
|
|
416
|
+
const hasAgreement = this.config.agreementKeywords.some(keyword =>
|
|
417
|
+
contentLower.includes(keyword.toLowerCase()),
|
|
418
|
+
);
|
|
419
|
+
|
|
420
|
+
// Check for disagreement keywords
|
|
421
|
+
const hasDisagreement = this.config.disagreementKeywords.some(keyword =>
|
|
422
|
+
contentLower.includes(keyword.toLowerCase()),
|
|
423
|
+
);
|
|
424
|
+
|
|
425
|
+
if (hasAgreement && !hasDisagreement) {
|
|
426
|
+
votes.set(message.name, 'agree');
|
|
427
|
+
} else if (hasDisagreement) {
|
|
428
|
+
votes.set(message.name, 'disagree');
|
|
429
|
+
}
|
|
430
|
+
}
|
|
431
|
+
|
|
432
|
+
const totalVoters = votes.size;
|
|
433
|
+
const agreements = Array.from(votes.values()).filter(
|
|
434
|
+
v => v === 'agree',
|
|
435
|
+
).length;
|
|
436
|
+
|
|
437
|
+
const minParticipants = this.config.minParticipants || 2;
|
|
438
|
+
|
|
439
|
+
// Check if enough participants have voted
|
|
440
|
+
if (totalVoters < minParticipants) {
|
|
441
|
+
return {
|
|
442
|
+
shouldTerminate: false,
|
|
443
|
+
reason: `Not enough participants voted: ${totalVoters}/${minParticipants}`,
|
|
444
|
+
};
|
|
445
|
+
}
|
|
446
|
+
|
|
447
|
+
const agreementRate = agreements / totalVoters;
|
|
448
|
+
const shouldTerminate = agreementRate >= this.config.threshold;
|
|
449
|
+
|
|
450
|
+
return {
|
|
451
|
+
shouldTerminate,
|
|
452
|
+
reason: shouldTerminate
|
|
453
|
+
? `Consensus reached: ${Math.round(agreementRate * 100)}% agreement (threshold: ${Math.round(this.config.threshold * 100)}%)`
|
|
454
|
+
: `No consensus: ${Math.round(agreementRate * 100)}% agreement (need: ${Math.round(this.config.threshold * 100)}%)`,
|
|
455
|
+
summary: shouldTerminate
|
|
456
|
+
? `Conversation ended with ${Math.round(agreementRate * 100)}% participant agreement.`
|
|
457
|
+
: undefined,
|
|
458
|
+
data: {
|
|
459
|
+
agreementRate,
|
|
460
|
+
totalVoters,
|
|
461
|
+
agreements,
|
|
462
|
+
threshold: this.config.threshold,
|
|
463
|
+
},
|
|
464
|
+
};
|
|
465
|
+
}
|
|
466
|
+
}
|
|
467
|
+
|
|
468
|
+
/**
|
|
469
|
+
* Handler for custom termination conditions
|
|
470
|
+
*/
|
|
471
|
+
export class CustomHandler implements TerminationHandler {
|
|
472
|
+
private condition: TerminationCondition;
|
|
473
|
+
|
|
474
|
+
/**
|
|
475
|
+
* Create a custom handler
|
|
476
|
+
* @param condition - Custom termination condition
|
|
477
|
+
*/
|
|
478
|
+
constructor(condition: TerminationCondition) {
|
|
479
|
+
this.condition = condition;
|
|
480
|
+
}
|
|
481
|
+
|
|
482
|
+
/**
|
|
483
|
+
* Evaluate the custom condition
|
|
484
|
+
* @param messages - Conversation messages
|
|
485
|
+
* @param participants - Chat participants
|
|
486
|
+
* @param context - Current chat context
|
|
487
|
+
* @returns Termination result
|
|
488
|
+
*/
|
|
489
|
+
async evaluate(
|
|
490
|
+
messages: Message[],
|
|
491
|
+
participants: ChatParticipant[],
|
|
492
|
+
context: ChatContext,
|
|
493
|
+
): Promise<TerminationResult> {
|
|
494
|
+
// If there's a custom evaluator, use it
|
|
495
|
+
if (this.condition.evaluator) {
|
|
496
|
+
return this.condition.evaluator(messages, participants, context);
|
|
497
|
+
}
|
|
498
|
+
|
|
499
|
+
// Otherwise, try to interpret the value
|
|
500
|
+
const value = this.condition.value;
|
|
501
|
+
|
|
502
|
+
if (typeof value === 'function') {
|
|
503
|
+
return (value as TerminationEvaluator)(messages, participants, context);
|
|
504
|
+
}
|
|
505
|
+
|
|
506
|
+
// Default: no termination
|
|
507
|
+
return {
|
|
508
|
+
shouldTerminate: false,
|
|
509
|
+
reason: 'Custom condition not properly configured',
|
|
510
|
+
};
|
|
511
|
+
}
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
/**
|
|
515
|
+
* Manager for multiple termination conditions
|
|
516
|
+
*/
|
|
517
|
+
export class TerminationManager {
|
|
518
|
+
private handlers: Map<string, TerminationHandler> = new Map();
|
|
519
|
+
private conditions: TerminationCondition[] = [];
|
|
520
|
+
|
|
521
|
+
/**
|
|
522
|
+
* Create a termination manager
|
|
523
|
+
* @param conditions - Initial termination conditions
|
|
524
|
+
*/
|
|
525
|
+
constructor(conditions: TerminationCondition[] = []) {
|
|
526
|
+
for (const condition of conditions) {
|
|
527
|
+
this.addCondition(condition);
|
|
528
|
+
}
|
|
529
|
+
}
|
|
530
|
+
|
|
531
|
+
/**
|
|
532
|
+
* Add a termination condition
|
|
533
|
+
* @param condition - Condition to add
|
|
534
|
+
*/
|
|
535
|
+
addCondition(condition: TerminationCondition): void {
|
|
536
|
+
const id = `${condition.type}-${this.conditions.length}`;
|
|
537
|
+
const handler = createTerminationHandler(condition);
|
|
538
|
+
this.handlers.set(id, handler);
|
|
539
|
+
this.conditions.push(condition);
|
|
540
|
+
}
|
|
541
|
+
|
|
542
|
+
/**
|
|
543
|
+
* Remove a termination condition by type
|
|
544
|
+
* @param type - Condition type to remove
|
|
545
|
+
*/
|
|
546
|
+
removeCondition(type: TerminationConditionType): void {
|
|
547
|
+
const indicesToRemove: number[] = [];
|
|
548
|
+
|
|
549
|
+
this.conditions.forEach((condition, index) => {
|
|
550
|
+
if (condition.type === type) {
|
|
551
|
+
indicesToRemove.push(index);
|
|
552
|
+
}
|
|
553
|
+
});
|
|
554
|
+
|
|
555
|
+
// Remove in reverse order to maintain indices
|
|
556
|
+
for (let i = indicesToRemove.length - 1; i >= 0; i--) {
|
|
557
|
+
const index = indicesToRemove[i];
|
|
558
|
+
if (index !== undefined) {
|
|
559
|
+
const id = `${type}-${index}`;
|
|
560
|
+
this.handlers.delete(id);
|
|
561
|
+
this.conditions.splice(index, 1);
|
|
562
|
+
}
|
|
563
|
+
}
|
|
564
|
+
}
|
|
565
|
+
|
|
566
|
+
/**
|
|
567
|
+
* Clear all termination conditions
|
|
568
|
+
*/
|
|
569
|
+
clearConditions(): void {
|
|
570
|
+
this.handlers.clear();
|
|
571
|
+
this.conditions = [];
|
|
572
|
+
}
|
|
573
|
+
|
|
574
|
+
/**
|
|
575
|
+
* Evaluate all termination conditions
|
|
576
|
+
* @param messages - Conversation messages
|
|
577
|
+
* @param participants - Chat participants
|
|
578
|
+
* @param context - Current chat context
|
|
579
|
+
* @returns Combined termination result
|
|
580
|
+
*/
|
|
581
|
+
async evaluate(
|
|
582
|
+
messages: Message[],
|
|
583
|
+
participants: ChatParticipant[],
|
|
584
|
+
context: ChatContext,
|
|
585
|
+
): Promise<TerminationResult> {
|
|
586
|
+
const results: Array<TerminationResult & { type: string }> = [];
|
|
587
|
+
|
|
588
|
+
for (const [id, handler] of this.handlers.entries()) {
|
|
589
|
+
const result = await handler.evaluate(messages, participants, context);
|
|
590
|
+
results.push({ ...result, type: id });
|
|
591
|
+
|
|
592
|
+
// Early exit if any condition triggers termination
|
|
593
|
+
if (result.shouldTerminate) {
|
|
594
|
+
return {
|
|
595
|
+
shouldTerminate: true,
|
|
596
|
+
reason: result.reason,
|
|
597
|
+
summary: result.summary,
|
|
598
|
+
data: {
|
|
599
|
+
triggeredBy: id,
|
|
600
|
+
allResults: results,
|
|
601
|
+
},
|
|
602
|
+
};
|
|
603
|
+
}
|
|
604
|
+
}
|
|
605
|
+
|
|
606
|
+
return {
|
|
607
|
+
shouldTerminate: false,
|
|
608
|
+
data: { allResults: results },
|
|
609
|
+
};
|
|
610
|
+
}
|
|
611
|
+
|
|
612
|
+
/**
|
|
613
|
+
* Get all configured conditions
|
|
614
|
+
* @returns Array of termination conditions
|
|
615
|
+
*/
|
|
616
|
+
getConditions(): TerminationCondition[] {
|
|
617
|
+
return [...this.conditions];
|
|
618
|
+
}
|
|
619
|
+
|
|
620
|
+
/**
|
|
621
|
+
* Check if a specific condition type is configured
|
|
622
|
+
* @param type - Condition type to check
|
|
623
|
+
* @returns Whether the condition type exists
|
|
624
|
+
*/
|
|
625
|
+
hasCondition(type: TerminationConditionType): boolean {
|
|
626
|
+
return this.conditions.some(c => c.type === type);
|
|
627
|
+
}
|
|
628
|
+
}
|
|
629
|
+
|
|
630
|
+
/**
|
|
631
|
+
* Common termination condition presets
|
|
632
|
+
*/
|
|
633
|
+
export const TerminationPresets = {
|
|
634
|
+
/**
|
|
635
|
+
* Create a preset for task completion detection
|
|
636
|
+
*/
|
|
637
|
+
taskCompletion(): TerminationCondition {
|
|
638
|
+
return {
|
|
639
|
+
type: 'keyword',
|
|
640
|
+
value: ['TASK_COMPLETE', 'DONE', 'FINISHED', 'COMPLETED', 'END_TASK'],
|
|
641
|
+
description: 'Terminate when task completion keyword is detected',
|
|
642
|
+
};
|
|
643
|
+
},
|
|
644
|
+
|
|
645
|
+
/**
|
|
646
|
+
* Create a preset for approval workflows
|
|
647
|
+
*/
|
|
648
|
+
approval(): TerminationCondition {
|
|
649
|
+
const consensusValue: ConsensusConfigType = {
|
|
650
|
+
threshold: 0.75,
|
|
651
|
+
agreementKeywords: ['approve', 'approved', 'lgtm', 'ship it'],
|
|
652
|
+
disagreementKeywords: ['reject', 'denied', 'needs work'],
|
|
653
|
+
minParticipants: 2,
|
|
654
|
+
};
|
|
655
|
+
return {
|
|
656
|
+
type: 'consensus',
|
|
657
|
+
value: consensusValue,
|
|
658
|
+
description: 'Terminate when approval consensus is reached',
|
|
659
|
+
};
|
|
660
|
+
},
|
|
661
|
+
|
|
662
|
+
/**
|
|
663
|
+
* Create a preset for quick discussions
|
|
664
|
+
* @param rounds - Maximum rounds
|
|
665
|
+
*/
|
|
666
|
+
quickDiscussion(rounds = 5): TerminationCondition[] {
|
|
667
|
+
return [
|
|
668
|
+
{
|
|
669
|
+
type: 'max_rounds',
|
|
670
|
+
value: rounds,
|
|
671
|
+
description: `Maximum ${rounds} rounds`,
|
|
672
|
+
},
|
|
673
|
+
{
|
|
674
|
+
type: 'keyword',
|
|
675
|
+
value: ['TERMINATE', 'END'],
|
|
676
|
+
description: 'Manual termination keywords',
|
|
677
|
+
},
|
|
678
|
+
];
|
|
679
|
+
},
|
|
680
|
+
|
|
681
|
+
/**
|
|
682
|
+
* Create a preset for long-running tasks
|
|
683
|
+
* @param timeoutMinutes - Timeout in minutes
|
|
684
|
+
*/
|
|
685
|
+
longRunning(timeoutMinutes = 30): TerminationCondition[] {
|
|
686
|
+
return [
|
|
687
|
+
{
|
|
688
|
+
type: 'timeout',
|
|
689
|
+
value: timeoutMinutes * 60 * 1000,
|
|
690
|
+
description: `${timeoutMinutes} minute timeout`,
|
|
691
|
+
},
|
|
692
|
+
{
|
|
693
|
+
type: 'max_messages',
|
|
694
|
+
value: 100,
|
|
695
|
+
description: 'Maximum 100 messages',
|
|
696
|
+
},
|
|
697
|
+
{
|
|
698
|
+
type: 'keyword',
|
|
699
|
+
value: ['TERMINATE', 'ABORT', 'CANCEL'],
|
|
700
|
+
description: 'Emergency termination keywords',
|
|
701
|
+
},
|
|
702
|
+
];
|
|
703
|
+
},
|
|
704
|
+
};
|