@amitdeshmukh/ax-crew 6.0.0 → 8.0.0
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/.claude/settings.local.json +7 -0
- package/CHANGELOG.md +35 -0
- package/README.md +240 -0
- package/dist/agents/ace.d.ts +134 -0
- package/dist/agents/ace.js +477 -0
- package/dist/agents/agentConfig.d.ts +3 -2
- package/dist/agents/agentConfig.js +6 -2
- package/dist/agents/index.d.ts +86 -2
- package/dist/agents/index.js +364 -6
- package/dist/index.d.ts +3 -3
- package/dist/types.d.ts +52 -1
- package/examples/README.md +46 -8
- package/examples/ace-customer-support.ts +480 -0
- package/examples/ace-flight-finder.ts +329 -0
- package/examples/telemetry-demo.ts +165 -0
- package/package.json +3 -2
- package/plan.md +255 -0
- package/playbooks/customer-support.json +32 -0
- package/playbooks/flight-assistant.json +23 -0
- package/src/agents/ace.ts +594 -0
- package/src/agents/agentConfig.ts +8 -2
- package/src/agents/index.ts +416 -8
- package/src/index.ts +14 -2
- package/src/types.ts +67 -1
- package/tests/telemetry.test.ts +81 -0
package/src/agents/index.ts
CHANGED
|
@@ -15,7 +15,9 @@ import type {
|
|
|
15
15
|
FunctionRegistryType,
|
|
16
16
|
UsageCost,
|
|
17
17
|
AxCrewConfig,
|
|
18
|
+
AxCrewOptions,
|
|
18
19
|
MCPTransportConfig,
|
|
20
|
+
ACEConfig,
|
|
19
21
|
} from "../types.js";
|
|
20
22
|
|
|
21
23
|
import { createState } from "../state/index.js";
|
|
@@ -47,6 +49,13 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
47
49
|
private agentName: string;
|
|
48
50
|
private costTracker?: any;
|
|
49
51
|
private lastRecordedCostUSD: number = 0;
|
|
52
|
+
private debugEnabled: boolean = false;
|
|
53
|
+
// ACE-related optional state
|
|
54
|
+
private aceConfig?: ACEConfig;
|
|
55
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
56
|
+
private aceOptimizer?: any;
|
|
57
|
+
private acePlaybook?: any;
|
|
58
|
+
private aceBaseInstruction?: string; // Original description before playbook injection
|
|
50
59
|
private isAxAIService(obj: any): obj is AxAI {
|
|
51
60
|
return !!obj && typeof obj.getName === 'function' && typeof obj.chat === 'function';
|
|
52
61
|
}
|
|
@@ -65,10 +74,11 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
65
74
|
functions?: (AxFunction | (() => AxFunction))[] | undefined;
|
|
66
75
|
examples?: Array<Record<string, any>> | undefined;
|
|
67
76
|
mcpServers?: Record<string, MCPTransportConfig> | undefined;
|
|
77
|
+
debug?: boolean;
|
|
68
78
|
}>,
|
|
69
79
|
state: StateInstance
|
|
70
80
|
) {
|
|
71
|
-
const { examples, ...restOptions } = options;
|
|
81
|
+
const { examples, debug, ...restOptions } = options;
|
|
72
82
|
const formattedOptions = {
|
|
73
83
|
...restOptions,
|
|
74
84
|
functions: restOptions.functions?.map((fn) =>
|
|
@@ -79,6 +89,7 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
79
89
|
this.state = state;
|
|
80
90
|
this.axai = ai;
|
|
81
91
|
this.agentName = options.name;
|
|
92
|
+
this.debugEnabled = debug ?? false;
|
|
82
93
|
|
|
83
94
|
// Set examples if provided
|
|
84
95
|
if (examples && examples.length > 0) {
|
|
@@ -97,13 +108,40 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
97
108
|
third?: Readonly<AxProgramForwardOptions<any>>
|
|
98
109
|
): Promise<Record<string, any>> {
|
|
99
110
|
let result;
|
|
100
|
-
|
|
111
|
+
|
|
101
112
|
const start = performance.now();
|
|
102
113
|
const crewId = (this.state as any)?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
103
114
|
const labels = { crewId, agent: this.agentName } as any;
|
|
115
|
+
const input = this.isAxAIService(first) ? second : first;
|
|
116
|
+
const taskId = `task_${crewId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
117
|
+
|
|
118
|
+
// Track execution context in crew for ACE feedback routing
|
|
119
|
+
const crewInstance = (this.state as any)?.crew as AxCrew;
|
|
120
|
+
if (crewInstance) {
|
|
121
|
+
if (this.isAxAIService(first)) {
|
|
122
|
+
// For sub-agent calls, track under parent task ID
|
|
123
|
+
const parentTaskId = (this.state as any)?.currentTaskId;
|
|
124
|
+
if (parentTaskId) {
|
|
125
|
+
crewInstance.trackAgentExecution(parentTaskId, this.agentName, input);
|
|
126
|
+
}
|
|
127
|
+
} else {
|
|
128
|
+
// Root-level call - start new execution tracking
|
|
129
|
+
crewInstance.trackAgentExecution(taskId, this.agentName, input);
|
|
130
|
+
(this.state as any).currentTaskId = taskId;
|
|
131
|
+
}
|
|
132
|
+
}
|
|
104
133
|
|
|
105
|
-
//
|
|
106
|
-
// This ensures
|
|
134
|
+
// Before forward: compose instruction with current playbook (mirrors AxACE.compile behavior)
|
|
135
|
+
// This ensures the agent uses the latest playbook context for this call
|
|
136
|
+
if (this.debugEnabled) {
|
|
137
|
+
console.log(`[ACE Debug] forward() called, aceConfig=${!!this.aceConfig}`);
|
|
138
|
+
}
|
|
139
|
+
if (this.aceConfig) {
|
|
140
|
+
await this.composeInstructionWithPlaybook();
|
|
141
|
+
}
|
|
142
|
+
|
|
143
|
+
// Execute the forward call
|
|
144
|
+
// Note: OpenTelemetry spans are automatically created by AxAI (configured via AxCrewOptions.telemetry)
|
|
107
145
|
if (this.isAxAIService(first)) {
|
|
108
146
|
// Sub-agent case (called with AI service)
|
|
109
147
|
result = await super.forward(this.axai, second as Record<string, any>, third);
|
|
@@ -151,6 +189,24 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
151
189
|
} catch {}
|
|
152
190
|
}
|
|
153
191
|
|
|
192
|
+
// Record result in crew execution history for ACE feedback routing
|
|
193
|
+
if (crewInstance) {
|
|
194
|
+
if (this.isAxAIService(first)) {
|
|
195
|
+
// For sub-agent calls, record under parent task ID
|
|
196
|
+
const parentTaskId = (this.state as any)?.currentTaskId;
|
|
197
|
+
if (parentTaskId) {
|
|
198
|
+
crewInstance.recordAgentResult(parentTaskId, this.agentName, result);
|
|
199
|
+
}
|
|
200
|
+
} else {
|
|
201
|
+
// Root-level result - include taskId for feedback routing
|
|
202
|
+
crewInstance.recordAgentResult(taskId, this.agentName, result);
|
|
203
|
+
// Clean up current task ID
|
|
204
|
+
delete (this.state as any).currentTaskId;
|
|
205
|
+
// Attach taskId to result for feedback routing convenience
|
|
206
|
+
result._taskId = taskId;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
209
|
+
|
|
154
210
|
return result;
|
|
155
211
|
}
|
|
156
212
|
|
|
@@ -257,6 +313,217 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
257
313
|
MetricsRegistry.reset({ crewId, agent: this.agentName } as any);
|
|
258
314
|
}
|
|
259
315
|
|
|
316
|
+
// =============
|
|
317
|
+
// ACE API - Agentic Context Engineering for online learning
|
|
318
|
+
// Reference: https://axllm.dev/ace/
|
|
319
|
+
// =============
|
|
320
|
+
|
|
321
|
+
/**
|
|
322
|
+
* Initialize ACE (Agentic Context Engineering) for this agent.
|
|
323
|
+
* Builds the optimizer and loads any initial playbook from persistence.
|
|
324
|
+
* Sets up the optimizer for online-only mode if compileOnStart is false.
|
|
325
|
+
*/
|
|
326
|
+
async initACE(ace?: ACEConfig): Promise<void> {
|
|
327
|
+
this.aceConfig = ace;
|
|
328
|
+
if (!ace) return;
|
|
329
|
+
try {
|
|
330
|
+
// Capture base instruction BEFORE any playbook injection (mirrors AxACE.extractProgramInstruction)
|
|
331
|
+
this.aceBaseInstruction = this.getSignature().getDescription() || '';
|
|
332
|
+
|
|
333
|
+
const { buildACEOptimizer, loadInitialPlaybook, createEmptyPlaybook } = await import('./ace.js');
|
|
334
|
+
// Build optimizer with agent's AI as student
|
|
335
|
+
this.aceOptimizer = buildACEOptimizer(this.axai, ace);
|
|
336
|
+
|
|
337
|
+
// For online-only mode (no offline compile), we need to set the program
|
|
338
|
+
// reference so applyOnlineUpdate can work. AxACE requires compile() to
|
|
339
|
+
// set the program, but we can set it directly for online-only use.
|
|
340
|
+
if (!ace.compileOnStart) {
|
|
341
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
342
|
+
(this.aceOptimizer as any).program = this;
|
|
343
|
+
}
|
|
344
|
+
|
|
345
|
+
// Load initial playbook or create empty one
|
|
346
|
+
const initial = await loadInitialPlaybook(ace.persistence);
|
|
347
|
+
this.applyPlaybook(initial ?? createEmptyPlaybook());
|
|
348
|
+
|
|
349
|
+
if (this.debugEnabled) {
|
|
350
|
+
console.log(`[ACE Debug] Initialized for ${this.agentName}, base instruction: ${this.aceBaseInstruction?.slice(0, 50)}...`);
|
|
351
|
+
}
|
|
352
|
+
} catch (error) {
|
|
353
|
+
console.warn(`Failed to initialize ACE for agent ${this.agentName}:`, error);
|
|
354
|
+
}
|
|
355
|
+
}
|
|
356
|
+
|
|
357
|
+
/**
|
|
358
|
+
* Run offline ACE compilation with examples and metric.
|
|
359
|
+
* Compiles the playbook based on training examples.
|
|
360
|
+
*/
|
|
361
|
+
async optimizeOffline(params?: { metric?: any; examples?: any[] }): Promise<void> {
|
|
362
|
+
if (!this.aceConfig || !this.aceOptimizer) return;
|
|
363
|
+
try {
|
|
364
|
+
const { runOfflineCompile, resolveMetric } = await import('./ace.js');
|
|
365
|
+
const registry = (this as any).__functionsRegistry as FunctionRegistryType | undefined;
|
|
366
|
+
const metric = params?.metric || resolveMetric(this.aceConfig.metric, registry || {} as any);
|
|
367
|
+
const examples = params?.examples || [];
|
|
368
|
+
|
|
369
|
+
if (!metric || examples.length === 0) {
|
|
370
|
+
console.warn(`ACE offline compile skipped for ${this.agentName}: missing metric or examples`);
|
|
371
|
+
return;
|
|
372
|
+
}
|
|
373
|
+
|
|
374
|
+
const result = await runOfflineCompile({
|
|
375
|
+
program: this,
|
|
376
|
+
optimizer: this.aceOptimizer,
|
|
377
|
+
metric,
|
|
378
|
+
examples,
|
|
379
|
+
persistence: this.aceConfig.persistence
|
|
380
|
+
});
|
|
381
|
+
|
|
382
|
+
// Apply optimized playbook if compilation succeeded
|
|
383
|
+
if (result?.artifact?.playbook) {
|
|
384
|
+
await this.applyPlaybook(result.artifact.playbook);
|
|
385
|
+
}
|
|
386
|
+
} catch (error) {
|
|
387
|
+
console.warn(`ACE offline compile failed for ${this.agentName}:`, error);
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
|
|
391
|
+
/**
|
|
392
|
+
* Apply online ACE update based on user feedback.
|
|
393
|
+
*
|
|
394
|
+
* For preference-based feedback (e.g., "only show flights between 9am-12pm"),
|
|
395
|
+
* we use our own feedback analyzer that preserves specificity.
|
|
396
|
+
*
|
|
397
|
+
* Note: AxACE's built-in curator is designed for error correction (severity mismatches)
|
|
398
|
+
* and tends to over-abstract preference feedback into generic guidelines.
|
|
399
|
+
* We bypass it and directly use our feedback analyzer for better results.
|
|
400
|
+
*/
|
|
401
|
+
async applyOnlineUpdate(params: { example: any; prediction: any; feedback?: string }): Promise<void> {
|
|
402
|
+
if (!this.aceConfig) return;
|
|
403
|
+
if (!params.feedback?.trim()) return; // Nothing to do without feedback
|
|
404
|
+
|
|
405
|
+
try {
|
|
406
|
+
const { persistPlaybook, addFeedbackToPlaybook, createEmptyPlaybook } = await import('./ace.js');
|
|
407
|
+
|
|
408
|
+
// Get or create playbook
|
|
409
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
410
|
+
let playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
|
|
411
|
+
if (!playbook) {
|
|
412
|
+
playbook = createEmptyPlaybook();
|
|
413
|
+
}
|
|
414
|
+
|
|
415
|
+
if (this.debugEnabled) {
|
|
416
|
+
console.log(`[ACE Debug] Adding feedback to playbook: "${params.feedback}"`);
|
|
417
|
+
}
|
|
418
|
+
|
|
419
|
+
// Use teacher AI (or student AI as fallback) for smart categorization
|
|
420
|
+
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
421
|
+
const teacherAI = (this.aceOptimizer as any)?.teacherAI;
|
|
422
|
+
const aiForAnalysis = teacherAI ?? this.axai;
|
|
423
|
+
|
|
424
|
+
// Directly add feedback to playbook using our analyzer (preserves specificity)
|
|
425
|
+
await addFeedbackToPlaybook(playbook, params.feedback, aiForAnalysis, this.debugEnabled);
|
|
426
|
+
|
|
427
|
+
// Store updated playbook (injection happens in next forward() call)
|
|
428
|
+
this.applyPlaybook(playbook);
|
|
429
|
+
|
|
430
|
+
// Sync with optimizer if available
|
|
431
|
+
if (this.aceOptimizer) {
|
|
432
|
+
(this.aceOptimizer as any).playbook = playbook;
|
|
433
|
+
}
|
|
434
|
+
|
|
435
|
+
// Persist if auto-persist enabled
|
|
436
|
+
if (this.aceConfig.persistence?.autoPersist) {
|
|
437
|
+
await persistPlaybook(playbook, this.aceConfig.persistence);
|
|
438
|
+
}
|
|
439
|
+
|
|
440
|
+
if (this.debugEnabled) {
|
|
441
|
+
console.log(`[ACE Debug] Playbook updated, sections: ${Object.keys(playbook.sections || {}).join(', ')}`);
|
|
442
|
+
}
|
|
443
|
+
} catch (error) {
|
|
444
|
+
console.warn(`ACE online update failed for ${this.agentName}:`, error);
|
|
445
|
+
}
|
|
446
|
+
}
|
|
447
|
+
|
|
448
|
+
/**
|
|
449
|
+
* Get the current ACE playbook for this agent.
|
|
450
|
+
*/
|
|
451
|
+
getPlaybook(): any | undefined {
|
|
452
|
+
return this.acePlaybook;
|
|
453
|
+
}
|
|
454
|
+
|
|
455
|
+
/**
|
|
456
|
+
* Apply an ACE playbook to this agent.
|
|
457
|
+
* Stores the playbook for use in next forward() call.
|
|
458
|
+
* Note: Playbook is composed into instruction BEFORE each forward(), mirroring AxACE.compile behavior.
|
|
459
|
+
*/
|
|
460
|
+
applyPlaybook(pb: any): void {
|
|
461
|
+
this.acePlaybook = pb;
|
|
462
|
+
|
|
463
|
+
// Also update optimizer's internal playbook if possible
|
|
464
|
+
try {
|
|
465
|
+
(this.aceOptimizer as any).playbook = pb;
|
|
466
|
+
} catch {
|
|
467
|
+
// Ignore - optimizer may not be initialized yet
|
|
468
|
+
}
|
|
469
|
+
}
|
|
470
|
+
|
|
471
|
+
/**
|
|
472
|
+
* Compose instruction with current playbook and set on agent.
|
|
473
|
+
* This mirrors what AxACE does internally before each forward() during compile().
|
|
474
|
+
* Should be called BEFORE forward() to ensure playbook is in the prompt.
|
|
475
|
+
*/
|
|
476
|
+
private async composeInstructionWithPlaybook(): Promise<void> {
|
|
477
|
+
const playbook = this.acePlaybook ?? (this.aceOptimizer as any)?.playbook;
|
|
478
|
+
|
|
479
|
+
if (this.debugEnabled) {
|
|
480
|
+
console.log(`[ACE Debug] composeInstructionWithPlaybook called`);
|
|
481
|
+
console.log(`[ACE Debug] playbook exists: ${!!playbook}, sections: ${playbook ? Object.keys(playbook.sections || {}).length : 0}`);
|
|
482
|
+
console.log(`[ACE Debug] baseInstruction: "${this.aceBaseInstruction?.slice(0, 50)}..."`);
|
|
483
|
+
}
|
|
484
|
+
|
|
485
|
+
if (!playbook) return;
|
|
486
|
+
|
|
487
|
+
try {
|
|
488
|
+
const { renderPlaybook } = await import('./ace.js');
|
|
489
|
+
const rendered = renderPlaybook(playbook);
|
|
490
|
+
|
|
491
|
+
if (this.debugEnabled) {
|
|
492
|
+
console.log(`[ACE Debug] rendered playbook (${rendered.length} chars): ${rendered.slice(0, 100)}...`);
|
|
493
|
+
}
|
|
494
|
+
|
|
495
|
+
if (!rendered) return;
|
|
496
|
+
|
|
497
|
+
// Compose: base instruction + playbook (just like AxACE.composeInstruction)
|
|
498
|
+
const baseInstruction = this.aceBaseInstruction || '';
|
|
499
|
+
const combinedInstruction = [baseInstruction.trim(), '', rendered]
|
|
500
|
+
.filter((part) => part.trim().length > 0)
|
|
501
|
+
.join('\n\n');
|
|
502
|
+
|
|
503
|
+
if (this.debugEnabled) {
|
|
504
|
+
console.log(`[ACE Debug] combinedInstruction (${combinedInstruction.length} chars)`);
|
|
505
|
+
}
|
|
506
|
+
|
|
507
|
+
if (combinedInstruction.length >= 20) {
|
|
508
|
+
// Call setDescription on the internal program (like AxACE does)
|
|
509
|
+
// AxAgent.setDescription() only updates the signature, but we need
|
|
510
|
+
// to update the program's description which is used for the system prompt
|
|
511
|
+
const program = (this as any).program;
|
|
512
|
+
if (program?.setDescription) {
|
|
513
|
+
program.setDescription(combinedInstruction);
|
|
514
|
+
}
|
|
515
|
+
// Also update via AxAgent's setDescription for consistency
|
|
516
|
+
this.setDescription(combinedInstruction);
|
|
517
|
+
|
|
518
|
+
if (this.debugEnabled) {
|
|
519
|
+
console.log(`[ACE Debug] setDescription called successfully`);
|
|
520
|
+
console.log(`[ACE Debug] Verifying - signature desc length: ${this.getSignature().getDescription()?.length}`);
|
|
521
|
+
}
|
|
522
|
+
}
|
|
523
|
+
} catch (error) {
|
|
524
|
+
console.warn('[ACE Debug] Failed to compose instruction:', error);
|
|
525
|
+
}
|
|
526
|
+
}
|
|
260
527
|
}
|
|
261
528
|
|
|
262
529
|
/**
|
|
@@ -280,21 +547,34 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
280
547
|
*/
|
|
281
548
|
class AxCrew {
|
|
282
549
|
private crewConfig: AxCrewConfig;
|
|
550
|
+
private options?: AxCrewOptions;
|
|
283
551
|
functionsRegistry: FunctionRegistryType = {};
|
|
284
552
|
crewId: string;
|
|
285
553
|
agents: Map<string, StatefulAxAgent> | null;
|
|
286
554
|
state: StateInstance;
|
|
555
|
+
// Execution history for ACE feedback routing
|
|
556
|
+
private executionHistory: Map<string, {
|
|
557
|
+
taskId: string;
|
|
558
|
+
rootAgent: string;
|
|
559
|
+
involvedAgents: Set<string>;
|
|
560
|
+
taskInput: any;
|
|
561
|
+
agentResults: Map<string, any>;
|
|
562
|
+
startTime: number;
|
|
563
|
+
endTime?: number;
|
|
564
|
+
}> = new Map();
|
|
287
565
|
|
|
288
566
|
/**
|
|
289
567
|
* Creates an instance of AxCrew.
|
|
290
568
|
* @param {AxCrewConfig} crewConfig - JSON object with crew configuration.
|
|
291
569
|
* @param {FunctionRegistryType} [functionsRegistry={}] - The registry of functions to use in the crew.
|
|
570
|
+
* @param {AxCrewOptions} [options] - Optional settings for the crew (e.g., telemetry).
|
|
292
571
|
* @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
|
|
293
572
|
*/
|
|
294
573
|
constructor(
|
|
295
574
|
crewConfig: AxCrewConfig,
|
|
296
575
|
functionsRegistry: FunctionRegistryType = {},
|
|
297
|
-
|
|
576
|
+
options?: AxCrewOptions,
|
|
577
|
+
crewId: string = uuidv4(),
|
|
298
578
|
) {
|
|
299
579
|
// Basic validation of crew configuration
|
|
300
580
|
if (!crewConfig || typeof crewConfig !== 'object' || !('crew' in crewConfig)) {
|
|
@@ -311,6 +591,7 @@ class AxCrew {
|
|
|
311
591
|
this.crewConfig = crewConfig;
|
|
312
592
|
this.functionsRegistry = functionsRegistry;
|
|
313
593
|
this.crewId = crewId;
|
|
594
|
+
this.options = options;
|
|
314
595
|
this.agents = new Map<string, StatefulAxAgent>();
|
|
315
596
|
this.state = createState(crewId);
|
|
316
597
|
// Make crewId discoverable to metrics
|
|
@@ -329,7 +610,8 @@ class AxCrew {
|
|
|
329
610
|
agentName,
|
|
330
611
|
this.crewConfig,
|
|
331
612
|
this.functionsRegistry,
|
|
332
|
-
this.state
|
|
613
|
+
this.state,
|
|
614
|
+
this.options
|
|
333
615
|
);
|
|
334
616
|
|
|
335
617
|
// Destructure with type assertion
|
|
@@ -370,6 +652,8 @@ class AxCrew {
|
|
|
370
652
|
}
|
|
371
653
|
|
|
372
654
|
// Create an instance of StatefulAxAgent
|
|
655
|
+
// Set crew reference in state for execution tracking (ACE feedback routing)
|
|
656
|
+
const agentState = { ...this.state, crew: this };
|
|
373
657
|
const agent = new StatefulAxAgent(
|
|
374
658
|
ai,
|
|
375
659
|
{
|
|
@@ -380,10 +664,26 @@ class AxCrew {
|
|
|
380
664
|
functions: uniqueFunctions,
|
|
381
665
|
agents: uniqueSubAgents,
|
|
382
666
|
examples,
|
|
667
|
+
debug: (agentConfig as any).debug,
|
|
383
668
|
},
|
|
384
|
-
|
|
669
|
+
agentState as StateInstance
|
|
385
670
|
);
|
|
386
671
|
(agent as any).costTracker = tracker;
|
|
672
|
+
(agent as any).__functionsRegistry = this.functionsRegistry;
|
|
673
|
+
|
|
674
|
+
// Initialize ACE if configured
|
|
675
|
+
try {
|
|
676
|
+
const crewAgent = parseCrewConfig(this.crewConfig).crew.find(a => a.name === name) as any;
|
|
677
|
+
const ace: ACEConfig | undefined = crewAgent?.ace;
|
|
678
|
+
if (ace) {
|
|
679
|
+
await (agent as any).initACE?.(ace);
|
|
680
|
+
if (ace.compileOnStart) {
|
|
681
|
+
const { resolveMetric } = await import('./ace.js');
|
|
682
|
+
const metric = resolveMetric(ace.metric, this.functionsRegistry);
|
|
683
|
+
await (agent as any).optimizeOffline?.({ metric, examples });
|
|
684
|
+
}
|
|
685
|
+
}
|
|
686
|
+
} catch {}
|
|
387
687
|
|
|
388
688
|
return agent;
|
|
389
689
|
} catch (error) {
|
|
@@ -515,11 +815,119 @@ class AxCrew {
|
|
|
515
815
|
}
|
|
516
816
|
}
|
|
517
817
|
|
|
818
|
+
/**
|
|
819
|
+
* Track agent execution for ACE feedback routing
|
|
820
|
+
*/
|
|
821
|
+
trackAgentExecution(taskId: string, agentName: string, input: any): void {
|
|
822
|
+
if (!this.executionHistory.has(taskId)) {
|
|
823
|
+
this.executionHistory.set(taskId, {
|
|
824
|
+
taskId,
|
|
825
|
+
rootAgent: agentName,
|
|
826
|
+
involvedAgents: new Set([agentName]),
|
|
827
|
+
taskInput: input,
|
|
828
|
+
agentResults: new Map(),
|
|
829
|
+
startTime: Date.now()
|
|
830
|
+
});
|
|
831
|
+
} else {
|
|
832
|
+
// Add to involved agents if not already present
|
|
833
|
+
const context = this.executionHistory.get(taskId)!;
|
|
834
|
+
context.involvedAgents.add(agentName);
|
|
835
|
+
}
|
|
836
|
+
}
|
|
837
|
+
|
|
838
|
+
/**
|
|
839
|
+
* Record agent result for ACE feedback routing
|
|
840
|
+
*/
|
|
841
|
+
recordAgentResult(taskId: string, agentName: string, result: any): void {
|
|
842
|
+
const context = this.executionHistory.get(taskId);
|
|
843
|
+
if (context) {
|
|
844
|
+
context.agentResults.set(agentName, result);
|
|
845
|
+
context.endTime = Date.now();
|
|
846
|
+
}
|
|
847
|
+
}
|
|
848
|
+
|
|
849
|
+
/**
|
|
850
|
+
* Get agent involvement for a task (used for ACE feedback routing)
|
|
851
|
+
*/
|
|
852
|
+
getTaskAgentInvolvement(taskId: string): {
|
|
853
|
+
rootAgent: string;
|
|
854
|
+
involvedAgents: string[];
|
|
855
|
+
taskInput: any;
|
|
856
|
+
agentResults: Map<string, any>;
|
|
857
|
+
duration?: number;
|
|
858
|
+
} | null {
|
|
859
|
+
const context = this.executionHistory.get(taskId);
|
|
860
|
+
if (!context) return null;
|
|
861
|
+
|
|
862
|
+
return {
|
|
863
|
+
rootAgent: context.rootAgent,
|
|
864
|
+
involvedAgents: Array.from(context.involvedAgents),
|
|
865
|
+
taskInput: context.taskInput,
|
|
866
|
+
agentResults: context.agentResults,
|
|
867
|
+
duration: context.endTime ? context.endTime - context.startTime : undefined
|
|
868
|
+
};
|
|
869
|
+
}
|
|
870
|
+
|
|
871
|
+
/**
|
|
872
|
+
* Apply feedback to agents involved in a task for ACE online learning
|
|
873
|
+
*/
|
|
874
|
+
async applyTaskFeedback(params: {
|
|
875
|
+
taskId: string;
|
|
876
|
+
feedback: string;
|
|
877
|
+
strategy?: 'all' | 'primary' | 'weighted';
|
|
878
|
+
}): Promise<void> {
|
|
879
|
+
const involvement = this.getTaskAgentInvolvement(params.taskId);
|
|
880
|
+
if (!involvement) {
|
|
881
|
+
console.warn(`No execution history found for task ${params.taskId}`);
|
|
882
|
+
return;
|
|
883
|
+
}
|
|
884
|
+
|
|
885
|
+
const { involvedAgents, taskInput, agentResults } = involvement;
|
|
886
|
+
const strategy = params.strategy || 'all';
|
|
887
|
+
|
|
888
|
+
// Determine which agents to update based on strategy
|
|
889
|
+
let agentsToUpdate: string[] = [];
|
|
890
|
+
if (strategy === 'primary') {
|
|
891
|
+
agentsToUpdate = [involvement.rootAgent];
|
|
892
|
+
} else if (strategy === 'all' || strategy === 'weighted') {
|
|
893
|
+
agentsToUpdate = involvedAgents;
|
|
894
|
+
}
|
|
895
|
+
|
|
896
|
+
// Apply feedback to each involved agent
|
|
897
|
+
for (const agentName of agentsToUpdate) {
|
|
898
|
+
const agent = this.agents?.get(agentName);
|
|
899
|
+
if (agent && typeof (agent as any).applyOnlineUpdate === 'function') {
|
|
900
|
+
try {
|
|
901
|
+
await (agent as any).applyOnlineUpdate({
|
|
902
|
+
example: taskInput,
|
|
903
|
+
prediction: agentResults.get(agentName),
|
|
904
|
+
feedback: params.feedback
|
|
905
|
+
});
|
|
906
|
+
} catch (error) {
|
|
907
|
+
console.warn(`Failed to apply ACE feedback to agent ${agentName}:`, error);
|
|
908
|
+
}
|
|
909
|
+
}
|
|
910
|
+
}
|
|
911
|
+
}
|
|
912
|
+
|
|
913
|
+
/**
|
|
914
|
+
* Clean up old execution history (call periodically to prevent memory leaks)
|
|
915
|
+
*/
|
|
916
|
+
cleanupOldExecutions(maxAgeMs: number = 3600000): void { // Default 1 hour
|
|
917
|
+
const cutoffTime = Date.now() - maxAgeMs;
|
|
918
|
+
for (const [taskId, context] of this.executionHistory) {
|
|
919
|
+
if (context.startTime < cutoffTime) {
|
|
920
|
+
this.executionHistory.delete(taskId);
|
|
921
|
+
}
|
|
922
|
+
}
|
|
923
|
+
}
|
|
924
|
+
|
|
518
925
|
/**
|
|
519
926
|
* Cleans up the crew by dereferencing agents and resetting the state.
|
|
520
927
|
*/
|
|
521
928
|
destroy() {
|
|
522
929
|
this.agents = null;
|
|
930
|
+
this.executionHistory.clear();
|
|
523
931
|
this.state.reset();
|
|
524
932
|
}
|
|
525
933
|
|
|
@@ -559,4 +967,4 @@ class AxCrew {
|
|
|
559
967
|
}
|
|
560
968
|
|
|
561
969
|
export { AxCrew };
|
|
562
|
-
export type { StatefulAxAgent };
|
|
970
|
+
export type { StatefulAxAgent };
|
package/src/index.ts
CHANGED
|
@@ -1,13 +1,18 @@
|
|
|
1
1
|
import { AxCrew } from './agents/index.js';
|
|
2
2
|
import { AxCrewFunctions } from './functions/index.js';
|
|
3
|
-
import type { AxCrewConfig, AgentConfig } from './types.js';
|
|
3
|
+
import type { AxCrewConfig, AxCrewOptions, AgentConfig } from './types.js';
|
|
4
4
|
|
|
5
5
|
import type {
|
|
6
6
|
UsageCost,
|
|
7
7
|
AggregatedMetrics,
|
|
8
8
|
AggregatedCosts,
|
|
9
9
|
StateInstance,
|
|
10
|
-
FunctionRegistryType
|
|
10
|
+
FunctionRegistryType,
|
|
11
|
+
ACEConfig,
|
|
12
|
+
ACETeacherConfig,
|
|
13
|
+
ACEPersistenceConfig,
|
|
14
|
+
ACEOptionsConfig,
|
|
15
|
+
ACEMetricConfig,
|
|
11
16
|
} from './types.js';
|
|
12
17
|
/**
|
|
13
18
|
* Metrics types and helpers for request counts, token usage, and estimated cost.
|
|
@@ -46,6 +51,13 @@ export {
|
|
|
46
51
|
type AggregatedCosts,
|
|
47
52
|
type AgentConfig,
|
|
48
53
|
type AxCrewConfig,
|
|
54
|
+
type AxCrewOptions,
|
|
49
55
|
type StateInstance,
|
|
50
56
|
type UsageCost,
|
|
57
|
+
// ACE type exports
|
|
58
|
+
type ACEConfig,
|
|
59
|
+
type ACETeacherConfig,
|
|
60
|
+
type ACEPersistenceConfig,
|
|
61
|
+
type ACEOptionsConfig,
|
|
62
|
+
type ACEMetricConfig,
|
|
51
63
|
};
|
package/src/types.ts
CHANGED
|
@@ -147,6 +147,47 @@ interface MCPStreamableHTTPTransportConfig {
|
|
|
147
147
|
*/
|
|
148
148
|
type MCPTransportConfig = MCPStdioTransportConfig | MCPHTTPSSETransportConfig | MCPStreamableHTTPTransportConfig
|
|
149
149
|
|
|
150
|
+
// ========================
|
|
151
|
+
// ACE integration types
|
|
152
|
+
// ========================
|
|
153
|
+
|
|
154
|
+
interface ACETeacherConfig {
|
|
155
|
+
provider?: Provider;
|
|
156
|
+
providerKeyName?: string;
|
|
157
|
+
apiURL?: string;
|
|
158
|
+
ai?: AxModelConfig & { model: string };
|
|
159
|
+
providerArgs?: Record<string, unknown>;
|
|
160
|
+
}
|
|
161
|
+
|
|
162
|
+
interface ACEPersistenceConfig {
|
|
163
|
+
playbookPath?: string;
|
|
164
|
+
initialPlaybook?: Record<string, any>;
|
|
165
|
+
autoPersist?: boolean;
|
|
166
|
+
onPersist?: (pb: any) => Promise<void> | void;
|
|
167
|
+
onLoad?: () => Promise<any> | any;
|
|
168
|
+
}
|
|
169
|
+
|
|
170
|
+
interface ACEOptionsConfig {
|
|
171
|
+
maxEpochs?: number;
|
|
172
|
+
allowDynamicSections?: boolean;
|
|
173
|
+
tokenBudget?: number;
|
|
174
|
+
reflectorPrompt?: string;
|
|
175
|
+
curatorPrompt?: string;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
interface ACEMetricConfig {
|
|
179
|
+
metricFnName?: string;
|
|
180
|
+
primaryOutputField?: string;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
interface ACEConfig {
|
|
184
|
+
teacher?: ACETeacherConfig;
|
|
185
|
+
persistence?: ACEPersistenceConfig;
|
|
186
|
+
options?: ACEOptionsConfig;
|
|
187
|
+
metric?: ACEMetricConfig;
|
|
188
|
+
compileOnStart?: boolean;
|
|
189
|
+
}
|
|
190
|
+
|
|
150
191
|
/**
|
|
151
192
|
* The configuration for an agent.
|
|
152
193
|
*
|
|
@@ -193,6 +234,8 @@ interface AgentConfig {
|
|
|
193
234
|
agents?: string[];
|
|
194
235
|
examples?: Array<Record<string, any>>;
|
|
195
236
|
mcpServers?: Record<string, MCPTransportConfig>;
|
|
237
|
+
/** Optional AxACE configuration to enable optimization for this agent */
|
|
238
|
+
ace?: ACEConfig;
|
|
196
239
|
}
|
|
197
240
|
|
|
198
241
|
/**
|
|
@@ -236,9 +279,26 @@ interface AxCrewConfig {
|
|
|
236
279
|
crew: AgentConfig[]
|
|
237
280
|
}
|
|
238
281
|
|
|
282
|
+
/**
|
|
283
|
+
* Options for the AxCrew instance, specifically allowing optional OpenTelemetry injection.
|
|
284
|
+
*
|
|
285
|
+
* @property {Object} [telemetry] - Telemetry configuration.
|
|
286
|
+
* @property {any} [telemetry.tracer] - OpenTelemetry Tracer instance.
|
|
287
|
+
* @property {any} [telemetry.meter] - OpenTelemetry Meter instance.
|
|
288
|
+
*/
|
|
289
|
+
interface AxCrewOptions {
|
|
290
|
+
/** Enable debug logging for ACE and other internal operations */
|
|
291
|
+
debug?: boolean;
|
|
292
|
+
telemetry?: {
|
|
293
|
+
tracer?: any;
|
|
294
|
+
meter?: any;
|
|
295
|
+
}
|
|
296
|
+
}
|
|
297
|
+
|
|
239
298
|
export {
|
|
240
299
|
type AgentConfig,
|
|
241
300
|
type AxCrewConfig,
|
|
301
|
+
type AxCrewOptions,
|
|
242
302
|
type AggregatedMetrics,
|
|
243
303
|
type StateInstance,
|
|
244
304
|
type FunctionRegistryType,
|
|
@@ -249,5 +309,11 @@ export {
|
|
|
249
309
|
type ModelUsage,
|
|
250
310
|
type ModelInfo,
|
|
251
311
|
type UsageCost,
|
|
252
|
-
type AggregatedCosts
|
|
312
|
+
type AggregatedCosts,
|
|
313
|
+
// ACE exports
|
|
314
|
+
type ACEConfig,
|
|
315
|
+
type ACEMetricConfig,
|
|
316
|
+
type ACEOptionsConfig,
|
|
317
|
+
type ACEPersistenceConfig,
|
|
318
|
+
type ACETeacherConfig
|
|
253
319
|
}
|