@amitdeshmukh/ax-crew 8.0.3 → 8.2.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.
@@ -1,5 +1,5 @@
1
1
  import { v4 as uuidv4 } from "uuid";
2
- import { AxAgent } from "@ax-llm/ax";
2
+ import { AxAgent, AxGen } from "@ax-llm/ax";
3
3
  import { createState } from "../state/index.js";
4
4
  import { parseCrewConfig, parseAgentConfig } from "./agentConfig.js";
5
5
  import { MetricsRegistry } from "../metrics/index.js";
@@ -8,9 +8,13 @@ class StatefulAxAgent extends AxAgent {
8
8
  state;
9
9
  axai;
10
10
  agentName;
11
+ agentDefinition;
12
+ executionMode;
13
+ axGenProgram;
11
14
  costTracker;
12
- lastRecordedCostUSD = 0;
13
15
  debugEnabled = false;
16
+ static modernAxAgentRuntime = typeof AxAgent?.prototype?.getFunction === "function" &&
17
+ typeof AxAgent?.prototype?.setExamples !== "function";
14
18
  // ACE-related optional state
15
19
  aceConfig;
16
20
  // eslint-disable-next-line @typescript-eslint/no-explicit-any
@@ -20,137 +24,254 @@ class StatefulAxAgent extends AxAgent {
20
24
  isAxAIService(obj) {
21
25
  return !!obj && typeof obj.getName === 'function' && typeof obj.chat === 'function';
22
26
  }
23
- isAxAIInstance(obj) {
24
- return !!obj && typeof obj === 'object' && ('defaults' in obj || 'modelInfo' in obj);
25
- }
26
27
  constructor(ai, options, state) {
27
- const { examples, debug, ...restOptions } = options;
28
- const formattedOptions = {
29
- ...restOptions,
30
- functions: restOptions.functions?.map((fn) => typeof fn === "function" ? fn() : fn),
31
- };
32
- super(formattedOptions);
28
+ const { examples, debug } = options;
29
+ const resolvedFunctions = (options.functions ?? []).map((fn) => typeof fn === "function" ? fn() : fn);
30
+ const resolvedAgents = (options.agents ?? []);
31
+ const effectiveDefinition = (options.definition ?? options.description).trim();
32
+ if (StatefulAxAgent.modernAxAgentRuntime) {
33
+ const configuredAgentOptions = (options.axAgentOptions ?? {});
34
+ const configuredAgentGraph = (configuredAgentOptions.agents ?? {});
35
+ const configuredFunctionGraph = (configuredAgentOptions.functions ?? {});
36
+ const configuredActorOptions = (configuredAgentOptions.actorOptions ?? {});
37
+ const configuredResponderOptions = (configuredAgentOptions.responderOptions ?? {});
38
+ const modernOptions = {
39
+ ...configuredAgentOptions,
40
+ debug: debug ?? configuredAgentOptions.debug ?? false,
41
+ contextFields: Array.isArray(configuredAgentOptions.contextFields)
42
+ ? configuredAgentOptions.contextFields
43
+ : [],
44
+ };
45
+ modernOptions.agents = {
46
+ ...configuredAgentGraph,
47
+ local: resolvedAgents,
48
+ };
49
+ modernOptions.functions = {
50
+ ...configuredFunctionGraph,
51
+ local: resolvedFunctions,
52
+ };
53
+ if (effectiveDefinition.length > 0) {
54
+ modernOptions.actorOptions = {
55
+ ...configuredActorOptions,
56
+ description: configuredActorOptions.description ?? effectiveDefinition,
57
+ };
58
+ modernOptions.responderOptions = {
59
+ ...configuredResponderOptions,
60
+ description: configuredResponderOptions.description ?? effectiveDefinition,
61
+ };
62
+ }
63
+ else {
64
+ modernOptions.actorOptions = configuredActorOptions;
65
+ modernOptions.responderOptions = configuredResponderOptions;
66
+ }
67
+ super({
68
+ ai,
69
+ agentIdentity: {
70
+ name: options.name,
71
+ description: options.description,
72
+ },
73
+ signature: options.signature,
74
+ }, modernOptions);
75
+ }
76
+ else {
77
+ super({
78
+ name: options.name,
79
+ description: options.description,
80
+ definition: options.definition,
81
+ signature: options.signature,
82
+ agents: resolvedAgents,
83
+ functions: resolvedFunctions,
84
+ debug: debug ?? false,
85
+ }, {});
86
+ }
33
87
  this.state = state;
34
88
  this.axai = ai;
35
89
  this.agentName = options.name;
90
+ this.agentDefinition = effectiveDefinition;
91
+ this.executionMode = options.executionMode ?? "axgen";
36
92
  this.debugEnabled = debug ?? false;
37
- // Set examples if provided
93
+ this.axGenProgram = new AxGen(options.signature, {
94
+ description: effectiveDefinition,
95
+ functions: resolvedFunctions,
96
+ });
97
+ for (const agent of resolvedAgents) {
98
+ try {
99
+ const childName = agent.getFunction().name;
100
+ this.axGenProgram.register(agent, childName);
101
+ }
102
+ catch {
103
+ // Best-effort registration for optimizer/introspection support.
104
+ }
105
+ }
106
+ // Apply examples to compatibility layer if provided
38
107
  if (examples && examples.length > 0) {
39
- super.setExamples(examples);
108
+ this.setExamplesCompat(examples);
40
109
  }
41
110
  }
42
- // Implementation
43
- async forward(first, second, third) {
44
- let result;
111
+ /**
112
+ * @deprecated Use setExamplesCompat() to avoid Ax runtime version coupling.
113
+ */
114
+ setExamples(examples) {
115
+ this.setExamplesCompat(examples);
116
+ }
117
+ setExamplesCompat(examples) {
118
+ this.axGenProgram.setExamples(examples);
119
+ const baseSetExamples = AxAgent.prototype.setExamples;
120
+ if (typeof baseSetExamples === "function") {
121
+ baseSetExamples.call(this, examples);
122
+ return;
123
+ }
124
+ const internalProgram = this.program;
125
+ if (typeof internalProgram?.setExamples === "function") {
126
+ internalProgram.setExamples(examples);
127
+ }
128
+ }
129
+ /**
130
+ * @deprecated Use setDescriptionCompat() to avoid Ax runtime version coupling.
131
+ */
132
+ setDescription(description) {
133
+ this.setDescriptionCompat(description);
134
+ }
135
+ setDescriptionCompat(description) {
136
+ this.agentDefinition = description;
137
+ this.axGenProgram.setDescription(description);
138
+ const baseSetDescription = AxAgent.prototype.setDescription;
139
+ if (typeof baseSetDescription === "function") {
140
+ baseSetDescription.call(this, description);
141
+ return;
142
+ }
143
+ const agentRuntime = this;
144
+ if (typeof agentRuntime.program?.setDescription === "function") {
145
+ agentRuntime.program.setDescription(description);
146
+ }
147
+ agentRuntime.actorDescription = description;
148
+ agentRuntime.responderDescription = description;
149
+ if (typeof agentRuntime._buildSplitPrograms === "function") {
150
+ agentRuntime._buildSplitPrograms();
151
+ }
152
+ }
153
+ getUsage() {
154
+ if (this.executionMode === "axgen") {
155
+ return this.axGenProgram.getUsage();
156
+ }
157
+ return super.getUsage();
158
+ }
159
+ resetUsage() {
160
+ this.axGenProgram.resetUsage();
161
+ super.resetUsage();
162
+ }
163
+ resolveInvocationArgs(first, second, third) {
164
+ const calledWithAI = this.isAxAIService(first);
165
+ const ai = (calledWithAI ? first : this.axai);
166
+ if (!ai) {
167
+ throw new Error(`No AI instance is configured for agent "${this.agentName}"`);
168
+ }
169
+ const values = (calledWithAI ? second : first);
170
+ const options = (calledWithAI ? third : second);
171
+ return { ai, values, options, calledWithAI };
172
+ }
173
+ async executeForwardByMode(mode, ai, values, options) {
174
+ if (mode === "axgen") {
175
+ return this.axGenProgram.forward(ai, values, options);
176
+ }
177
+ return super.forward(ai, values, options);
178
+ }
179
+ recordUsageMetrics(labels, mode) {
180
+ const builtIn = mode === "axgen" ? this.axGenProgram.getUsage?.() : super.getUsage?.();
181
+ if (!Array.isArray(builtIn))
182
+ return;
183
+ const totals = builtIn.reduce((acc, u) => {
184
+ const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
185
+ const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
186
+ acc.promptTokens += typeof pt === "number" ? pt : 0;
187
+ acc.completionTokens += typeof ct === "number" ? ct : 0;
188
+ const model = u.model ||
189
+ this.axai?.getLastUsedChatModel?.() ||
190
+ this.axai?.defaults?.model;
191
+ if (model) {
192
+ acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
193
+ }
194
+ return acc;
195
+ }, { promptTokens: 0, completionTokens: 0, byModel: {} });
196
+ MetricsRegistry.recordTokens(labels, {
197
+ promptTokens: totals.promptTokens,
198
+ completionTokens: totals.completionTokens,
199
+ totalTokens: totals.promptTokens + totals.completionTokens,
200
+ });
201
+ const costTracker = this.costTracker;
202
+ try {
203
+ for (const [m, count] of Object.entries(totals.byModel)) {
204
+ costTracker?.trackTokens?.(count, m);
205
+ }
206
+ const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
207
+ if (!Number.isNaN(totalUSD) && totalUSD > 0) {
208
+ MetricsRegistry.recordEstimatedCost(labels, totalUSD);
209
+ }
210
+ }
211
+ catch { }
212
+ }
213
+ async runForwardInvocation(mode, first, second, third) {
214
+ const { ai, values, options, calledWithAI } = this.resolveInvocationArgs(first, second, third);
45
215
  const start = performance.now();
46
- const crewId = this.state?.crewId || (this.state.get?.('crewId')) || 'default';
216
+ const crewId = this.state?.crewId || this.state.get?.("crewId") || "default";
47
217
  const labels = { crewId, agent: this.agentName };
48
- const input = this.isAxAIService(first) ? second : first;
49
218
  const taskId = `task_${crewId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
50
219
  // Track execution context in crew for ACE feedback routing
51
220
  const crewInstance = this.state?.crew;
52
221
  if (crewInstance) {
53
- if (this.isAxAIService(first)) {
54
- // For sub-agent calls, track under parent task ID
222
+ if (calledWithAI) {
55
223
  const parentTaskId = this.state?.currentTaskId;
56
224
  if (parentTaskId) {
57
- crewInstance.trackAgentExecution(parentTaskId, this.agentName, input);
225
+ crewInstance.trackAgentExecution(parentTaskId, this.agentName, values);
58
226
  }
59
227
  }
60
228
  else {
61
- // Root-level call - start new execution tracking
62
- crewInstance.trackAgentExecution(taskId, this.agentName, input);
229
+ crewInstance.trackAgentExecution(taskId, this.agentName, values);
63
230
  this.state.currentTaskId = taskId;
64
231
  }
65
232
  }
66
- // Before forward: compose instruction with current playbook (mirrors AxACE.compile behavior)
67
- // This ensures the agent uses the latest playbook context for this call
68
233
  if (this.debugEnabled) {
69
- console.log(`[ACE Debug] forward() called, aceConfig=${!!this.aceConfig}`);
234
+ console.log(`[ACE Debug] forward() called, mode=${mode}, aceConfig=${!!this.aceConfig}`);
70
235
  }
71
236
  if (this.aceConfig) {
72
237
  await this.composeInstructionWithPlaybook();
73
238
  }
74
- // Execute the forward call
75
- // Note: OpenTelemetry spans are automatically created by AxAI (configured via AxCrewOptions.telemetry)
76
- if (this.isAxAIService(first)) {
77
- // Sub-agent case (called with AI service)
78
- result = await super.forward(this.axai, second, third);
79
- }
80
- else {
81
- // Direct call case
82
- result = await super.forward(this.axai, first, second);
83
- }
84
- // Track metrics and costs after the call using built-in usage
239
+ const result = await this.executeForwardByMode(mode, ai, values, options);
85
240
  const durationMs = performance.now() - start;
86
241
  MetricsRegistry.recordRequest(labels, false, durationMs);
87
- // Always record tokens from built-in usage array if present
88
- const builtIn = this.getUsage?.();
89
- if (Array.isArray(builtIn)) {
90
- const totals = builtIn.reduce((acc, u) => {
91
- const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
92
- const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
93
- acc.promptTokens += typeof pt === 'number' ? pt : 0;
94
- acc.completionTokens += typeof ct === 'number' ? ct : 0;
95
- // also aggregate per-model to feed Ax tracker
96
- const model = u.model || this.axai?.getLastUsedChatModel?.() || this.axai?.defaults?.model;
97
- if (model) {
98
- acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
99
- }
100
- return acc;
101
- }, { promptTokens: 0, completionTokens: 0, byModel: {} });
102
- MetricsRegistry.recordTokens(labels, {
103
- promptTokens: totals.promptTokens,
104
- completionTokens: totals.completionTokens,
105
- totalTokens: totals.promptTokens + totals.completionTokens,
106
- });
107
- // Feed Ax's cost tracker with token totals per model; Ax owns pricing
108
- const costTracker = this.costTracker;
109
- try {
110
- for (const [m, count] of Object.entries(totals.byModel)) {
111
- costTracker?.trackTokens?.(count, m);
112
- }
113
- const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
114
- if (!Number.isNaN(totalUSD) && totalUSD > 0) {
115
- MetricsRegistry.recordEstimatedCost(labels, totalUSD);
116
- }
117
- }
118
- catch { }
119
- }
120
- // Record result in crew execution history for ACE feedback routing
242
+ this.recordUsageMetrics(labels, mode);
121
243
  if (crewInstance) {
122
- if (this.isAxAIService(first)) {
123
- // For sub-agent calls, record under parent task ID
244
+ if (calledWithAI) {
124
245
  const parentTaskId = this.state?.currentTaskId;
125
246
  if (parentTaskId) {
126
247
  crewInstance.recordAgentResult(parentTaskId, this.agentName, result);
127
248
  }
128
249
  }
129
250
  else {
130
- // Root-level result - include taskId for feedback routing
131
251
  crewInstance.recordAgentResult(taskId, this.agentName, result);
132
- // Clean up current task ID
133
252
  delete this.state.currentTaskId;
134
- // Attach taskId to result for feedback routing convenience
135
253
  result._taskId = taskId;
136
254
  }
137
255
  }
138
256
  return result;
139
257
  }
140
258
  // Implementation
141
- streamingForward(first, second, third) {
259
+ async forward(first, second, third) {
260
+ return this.runForwardInvocation(this.executionMode, first, second, third);
261
+ }
262
+ runStreamingInvocation(mode, first, second, third) {
263
+ const { ai, values, options } = this.resolveInvocationArgs(first, second, third);
142
264
  const start = performance.now();
143
- const crewId = this.state?.crewId || (this.state.get?.('crewId')) || 'default';
265
+ const crewId = this.state?.crewId || this.state.get?.("crewId") || "default";
144
266
  const labels = { crewId, agent: this.agentName };
145
- let streamingResult;
146
- if (this.isAxAIService(first)) {
147
- streamingResult = super.streamingForward(this.axai, second, third);
148
- }
149
- else {
150
- streamingResult = super.streamingForward(this.axai, first, second);
151
- }
152
- // Create a new async generator that tracks costs after completion
267
+ const createStream = () => mode === "axgen"
268
+ ? this.axGenProgram.streamingForward(ai, values, options)
269
+ : super.streamingForward(ai, values, options);
153
270
  const wrappedGenerator = (async function* () {
271
+ if (this.aceConfig) {
272
+ await this.composeInstructionWithPlaybook();
273
+ }
274
+ const streamingResult = createStream();
154
275
  try {
155
276
  for await (const chunk of streamingResult) {
156
277
  yield chunk;
@@ -159,50 +280,15 @@ class StatefulAxAgent extends AxAgent {
159
280
  finally {
160
281
  const durationMs = performance.now() - start;
161
282
  MetricsRegistry.recordRequest(labels, true, durationMs);
162
- // Record tokens from built-in usage array if present
163
- const builtIn = this.getUsage?.();
164
- if (Array.isArray(builtIn)) {
165
- const totals = builtIn.reduce((acc, u) => {
166
- const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
167
- const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
168
- acc.promptTokens += typeof pt === 'number' ? pt : 0;
169
- acc.completionTokens += typeof ct === 'number' ? ct : 0;
170
- const model = u.model || this.axai?.getLastUsedChatModel?.() || this.axai?.defaults?.model;
171
- if (model) {
172
- acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
173
- }
174
- return acc;
175
- }, { promptTokens: 0, completionTokens: 0, byModel: {} });
176
- MetricsRegistry.recordTokens(labels, {
177
- promptTokens: totals.promptTokens,
178
- completionTokens: totals.completionTokens,
179
- totalTokens: totals.promptTokens + totals.completionTokens,
180
- });
181
- const costTracker = this.costTracker;
182
- try {
183
- for (const [m, count] of Object.entries(totals.byModel)) {
184
- costTracker?.trackTokens?.(count, m);
185
- }
186
- const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
187
- if (!Number.isNaN(totalUSD) && totalUSD > 0) {
188
- MetricsRegistry.recordEstimatedCost(labels, totalUSD);
189
- }
190
- }
191
- catch { }
192
- }
193
- // Record estimated cost (USD) via attached tracker if available
194
- const costTracker = this.costTracker;
195
- try {
196
- const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
197
- if (!Number.isNaN(totalUSD) && totalUSD > 0) {
198
- MetricsRegistry.recordEstimatedCost(labels, totalUSD);
199
- }
200
- }
201
- catch { }
283
+ this.recordUsageMetrics(labels, mode);
202
284
  }
203
285
  }).bind(this)();
204
286
  return wrappedGenerator;
205
287
  }
288
+ // Implementation
289
+ streamingForward(first, second, third) {
290
+ return this.runStreamingInvocation(this.executionMode, first, second, third);
291
+ }
206
292
  // Legacy cost API removed: rely on Ax trackers for cost reporting
207
293
  getLastUsageCost() { return null; }
208
294
  // Get the accumulated costs for all runs of this agent
@@ -241,7 +327,8 @@ class StatefulAxAgent extends AxAgent {
241
327
  return;
242
328
  try {
243
329
  // Capture base instruction BEFORE any playbook injection (mirrors AxACE.extractProgramInstruction)
244
- this.aceBaseInstruction = this.getSignature().getDescription() || '';
330
+ this.aceBaseInstruction =
331
+ this.agentDefinition || this.getSignature().getDescription() || '';
245
332
  const { buildACEOptimizer, loadInitialPlaybook, createEmptyPlaybook } = await import('./ace.js');
246
333
  // Build optimizer with agent's AI as student
247
334
  this.aceOptimizer = buildACEOptimizer(this.axai, ace);
@@ -482,7 +569,7 @@ class AxCrew {
482
569
  try {
483
570
  const agentConfig = await parseAgentConfig(agentName, this.crewConfig, this.functionsRegistry, this.state, this.options);
484
571
  // Destructure with type assertion
485
- const { ai, name, description, signature, functions, subAgentNames, examples, tracker } = agentConfig;
572
+ const { ai, name, executionMode, axAgentOptions, description, signature, functions, subAgentNames, examples, tracker } = agentConfig;
486
573
  // Get subagents for the AI agent
487
574
  const subAgents = subAgentNames.map((subAgentName) => {
488
575
  if (!this.agents?.get(subAgentName)) {
@@ -538,6 +625,8 @@ class AxCrew {
538
625
  const agentState = { ...this.state, crew: this };
539
626
  const agent = new StatefulAxAgent(ai, {
540
627
  name,
628
+ executionMode,
629
+ axAgentOptions,
541
630
  description,
542
631
  definition: agentConfig.definition,
543
632
  signature,
package/dist/index.d.ts CHANGED
@@ -1,6 +1,6 @@
1
1
  import { AxCrew } from './agents/index.js';
2
2
  import { AxCrewFunctions } from './functions/index.js';
3
- import type { AxCrewConfig, AxCrewOptions, AgentConfig } from './types.js';
3
+ import type { AxCrewConfig, AxCrewOptions, AgentConfig, AgentExecutionMode, AxCrewAxAgentOptions } from './types.js';
4
4
  import type { UsageCost, AggregatedMetrics, AggregatedCosts, StateInstance, FunctionRegistryType, ACEConfig, ACETeacherConfig, ACEPersistenceConfig, ACEOptionsConfig, ACEMetricConfig } from './types.js';
5
5
  /**
6
6
  * Metrics types and helpers for request counts, token usage, and estimated cost.
@@ -29,4 +29,4 @@ export {
29
29
  /** See class JSDoc on the `AxCrew` implementation. */
30
30
  _AxCrew as AxCrew,
31
31
  /** Built-in function registry; see file docs in `src/functions/index.ts`. */
32
- _AxCrewFunctions as AxCrewFunctions, FunctionRegistryType, type AggregatedMetrics, type AggregatedCosts, type AgentConfig, type AxCrewConfig, type AxCrewOptions, type StateInstance, type UsageCost, type ACEConfig, type ACETeacherConfig, type ACEPersistenceConfig, type ACEOptionsConfig, type ACEMetricConfig, };
32
+ _AxCrewFunctions as AxCrewFunctions, FunctionRegistryType, type AggregatedMetrics, type AggregatedCosts, type AgentConfig, type AgentExecutionMode, type AxCrewAxAgentOptions, type AxCrewConfig, type AxCrewOptions, type StateInstance, type UsageCost, type ACEConfig, type ACETeacherConfig, type ACEPersistenceConfig, type ACEOptionsConfig, type ACEMetricConfig, };
package/dist/types.d.ts CHANGED
@@ -1,5 +1,12 @@
1
- import type { AxFunction, AxSignature, AxModelConfig, AxMCPStreamableHTTPTransportOptions, AxProgramForwardOptions, AxAIArgs } from '@ax-llm/ax';
1
+ import type { AxFunction, AxSignature, AxModelConfig, AxMCPStreamableHTTPTransportOptions, AxProgramForwardOptions, AxAIArgs, AxAgentOptions } from '@ax-llm/ax';
2
2
  export type Provider = AxAIArgs<any>['name'];
3
+ export type AgentExecutionMode = 'axagent' | 'axgen';
4
+ export type AxCrewAxAgentOptions = Partial<Omit<AxAgentOptions, 'agents' | 'functions' | 'contextFields'>> & {
5
+ contextFields?: AxAgentOptions['contextFields'];
6
+ agents?: AxAgentOptions['agents'];
7
+ functions?: AxAgentOptions['functions'];
8
+ fields?: AxAgentOptions['fields'];
9
+ };
3
10
  /**
4
11
  * A state instance that is shared between agents.
5
12
  * This can be used to store data that becomes available to all agents and functions in an out-of-band manner.
@@ -181,6 +188,17 @@ interface ACEConfig {
181
188
  interface AgentConfig {
182
189
  name: string;
183
190
  description: string;
191
+ /**
192
+ * Choose the execution engine for this agent.
193
+ * - `axagent` (default): Uses AxAgent capabilities.
194
+ * - `axgen`: Uses AxGen capabilities.
195
+ */
196
+ executionMode?: AgentExecutionMode;
197
+ /**
198
+ * AxAgent runtime options (RLM mode, context fields, recursion, actor/responder options, etc).
199
+ * Only applies when executionMode is `axagent`.
200
+ */
201
+ axAgentOptions?: AxCrewAxAgentOptions;
184
202
  /**
185
203
  * Optional detailed persona/program definition. If provided, becomes the system prompt.
186
204
  * Must be at least 100 characters per Ax semantics.
@@ -86,13 +86,6 @@ Keep your responses clear and well-formatted.`,
86
86
  // Create a new instance of AxCrew with the config
87
87
  const crew = new AxCrew(config as AxCrewConfig);
88
88
 
89
- // Add the agents to the crew
90
- await crew.addAllAgents();
91
-
92
- // Get agent instances
93
- const managerAgent = crew.agents?.get("ManagerAgent");
94
- const databaseAgent = crew.agents?.get("DatabaseAgent");
95
-
96
89
  // Example queries to try:
97
90
  const queries = [
98
91
  "What tables are available in the database?",
@@ -109,11 +102,21 @@ console.log(`\n\nQuestion: ${userQuery}`);
109
102
 
110
103
  const main = async (): Promise<void> => {
111
104
  try {
112
- const managerResponse = await managerAgent?.forward({
105
+ // Initialize agents inside main so initialization failures are handled here.
106
+ await crew.addAllAgents();
107
+
108
+ const managerAgent = crew.agents?.get("ManagerAgent");
109
+ const databaseAgent = crew.agents?.get("DatabaseAgent");
110
+
111
+ if (!managerAgent) {
112
+ throw new Error("Failed to initialize ManagerAgent");
113
+ }
114
+
115
+ const managerResponse = await managerAgent.forward({
113
116
  question: userQuery,
114
117
  });
115
118
 
116
- console.log(`\nAnswer: ${JSON.stringify(managerResponse?.dbResult || managerResponse?.answer, null, 2)}`);
119
+ console.log(`\nAnswer: ${JSON.stringify(managerResponse?.answer, null, 2)}`);
117
120
 
118
121
  // Print metrics
119
122
  console.log("\nMetrics:\n+++++++++++++++++++++++++++++++++");
@@ -127,6 +130,9 @@ const main = async (): Promise<void> => {
127
130
  console.error("2. Check that GraphJin is accessible at http://localhost:8080");
128
131
  console.error("3. Verify graphjin is installed: which graphjin");
129
132
  console.error("4. Note: There's a known schema validation issue - see comments in the code");
133
+ throw error;
134
+ } finally {
135
+ crew.destroy();
130
136
  }
131
137
  };
132
138
 
@@ -0,0 +1,126 @@
1
+ import dotenv from "dotenv";
2
+ import { AxJSRuntime, AxJSRuntimePermission } from "@ax-llm/ax";
3
+ import { AxCrew } from "../dist/index.js";
4
+ import type { AxCrewConfig } from "../dist/index.js";
5
+
6
+ dotenv.config();
7
+
8
+ const runtime = new AxJSRuntime({
9
+ permissions: [AxJSRuntimePermission.TIMING],
10
+ });
11
+
12
+ const config: AxCrewConfig = {
13
+ crew: [
14
+ {
15
+ name: "Analyzer",
16
+ description:
17
+ "Analyzes a large dataset with semantic context management.",
18
+ executionMode: "axagent",
19
+ signature:
20
+ 'context:string, query:string -> answer:string, keyFindings:string[] "Analyzes context and returns findings"',
21
+ provider: "google-gemini",
22
+ providerKeyName: "GEMINI_API_KEY",
23
+ ai: {
24
+ model: "gemini-2.5-flash",
25
+ temperature: 0,
26
+ },
27
+ options: {
28
+ debug: true,
29
+ },
30
+ axAgentOptions: {
31
+ // Mandatory for AxAgent RLM mode
32
+ contextFields: ["context"],
33
+ runtime,
34
+ maxTurns: 20,
35
+ maxSubAgentCalls: 40,
36
+ mode: "simple",
37
+ contextManagement: {
38
+ errorPruning: true,
39
+ hindsightEvaluation: true,
40
+ pruneRank: 2,
41
+ tombstoning: {
42
+ model: "gemini-2.5-flash",
43
+ modelConfig: { maxTokens: 60 },
44
+ },
45
+ stateInspection: { contextThreshold: 3000 },
46
+ },
47
+ },
48
+ },
49
+ ],
50
+ };
51
+
52
+ const salesData = `
53
+ Region,Month,Product,Units,Revenue,ReturnRate
54
+ North,Jan,Widget-A,1200,48000,0.02
55
+ North,Jan,Widget-B,800,32000,0.05
56
+ North,Feb,Widget-A,1350,54000,0.018
57
+ North,Feb,Widget-B,720,28800,0.06
58
+ North,Mar,Widget-A,900,36000,0.03
59
+ North,Mar,Widget-B,1100,44000,0.04
60
+ South,Jan,Widget-A,980,39200,0.025
61
+ South,Jan,Widget-B,1500,60000,0.03
62
+ South,Feb,Widget-A,1100,44000,0.02
63
+ South,Feb,Widget-B,1300,52000,0.035
64
+ South,Mar,Widget-A,760,30400,0.045
65
+ South,Mar,Widget-B,1450,58000,0.025
66
+ East,Jan,Widget-A,2100,84000,0.015
67
+ East,Jan,Widget-B,600,24000,0.07
68
+ East,Feb,Widget-A,1950,78000,0.018
69
+ East,Feb,Widget-B,650,26000,0.065
70
+ East,Mar,Widget-A,2300,92000,0.012
71
+ East,Mar,Widget-B,700,28000,0.06
72
+ West,Jan,Widget-A,1700,68000,0.022
73
+ West,Jan,Widget-B,900,36000,0.04
74
+ West,Feb,Widget-A,1600,64000,0.025
75
+ West,Feb,Widget-B,950,38000,0.038
76
+ West,Mar,Widget-A,1800,72000,0.02
77
+ West,Mar,Widget-B,1000,40000,0.035
78
+ `.trim();
79
+
80
+ const main = async (): Promise<void> => {
81
+ const crew = new AxCrew(config);
82
+ try {
83
+ await crew.addAllAgents();
84
+
85
+ const analyzer = crew.agents?.get("Analyzer");
86
+ if (!analyzer) throw new Error("Failed to initialize Analyzer");
87
+
88
+ const result = await analyzer.forward({
89
+ context: salesData,
90
+ query:
91
+ "Which region and product combination has the highest revenue growth from Jan to Mar? " +
92
+ "Also flag combinations where return rate worsens month-over-month.",
93
+ });
94
+
95
+ console.log("\n=== Answer ===");
96
+ console.log(result.answer);
97
+ console.log("\n=== Key Findings ===");
98
+ for (const finding of result.keyFindings ?? []) {
99
+ console.log(" -", finding);
100
+ }
101
+
102
+ const agentMetrics = (analyzer as any).getMetrics?.();
103
+ const crewMetrics = crew.getCrewMetrics();
104
+
105
+ console.log("\n=== Cost & Usage (Agent) ===");
106
+ console.log(`Estimated USD: ${agentMetrics?.estimatedCostUSD ?? 0}`);
107
+ console.log(
108
+ `Tokens: prompt=${agentMetrics?.tokens?.promptTokens ?? 0}, completion=${agentMetrics?.tokens?.completionTokens ?? 0}, total=${agentMetrics?.tokens?.totalTokens ?? 0}`
109
+ );
110
+
111
+ console.log("\n=== Cost & Usage (Crew) ===");
112
+ console.log(`Estimated USD: ${crewMetrics?.estimatedCostUSD ?? 0}`);
113
+ console.log(
114
+ `Tokens: prompt=${crewMetrics?.tokens?.promptTokens ?? 0}, completion=${crewMetrics?.tokens?.completionTokens ?? 0}, total=${crewMetrics?.tokens?.totalTokens ?? 0}`
115
+ );
116
+ } finally {
117
+ crew.destroy();
118
+ }
119
+ };
120
+
121
+ main()
122
+ .then(() => process.exit(0))
123
+ .catch((error: unknown) => {
124
+ console.error(error);
125
+ process.exit(1);
126
+ });