@amitdeshmukh/ax-crew 8.0.3 → 8.1.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 +2 -1
- package/CHANGELOG.md +24 -1
- package/README.md +106 -7
- package/dist/agents/agentConfig.d.ts +3 -1
- package/dist/agents/agentConfig.js +3 -0
- package/dist/agents/index.d.ts +27 -3
- package/dist/agents/index.js +217 -128
- package/dist/index.d.ts +2 -2
- package/dist/types.d.ts +19 -1
- package/examples/graphjin-database-agent.ts +15 -9
- package/examples/rlm-long-task.ts +126 -0
- package/examples/rlm-shared-fields.ts +181 -0
- package/package.json +3 -3
- package/src/agents/agentConfig.ts +8 -1
- package/src/agents/index.ts +309 -144
- package/src/index.ts +4 -2
- package/src/types.ts +21 -1
- package/tests/execution-mode-metrics.test.ts +231 -0
- package/examples/factory.ts +0 -174
- package/examples/google-gemini-test.ts +0 -291
- package/examples/graphjin-README.md +0 -167
- package/examples/planner.ts +0 -107
package/src/agents/index.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import { v4 as uuidv4 } from "uuid";
|
|
2
|
-
import { AxAgent, AxAI } from "@ax-llm/ax";
|
|
2
|
+
import { AxAgent, AxAI, AxGen } from "@ax-llm/ax";
|
|
3
3
|
|
|
4
4
|
import type {
|
|
5
5
|
AxSignature,
|
|
@@ -18,6 +18,8 @@ import type {
|
|
|
18
18
|
AxCrewOptions,
|
|
19
19
|
MCPTransportConfig,
|
|
20
20
|
ACEConfig,
|
|
21
|
+
AgentExecutionMode,
|
|
22
|
+
AxCrewAxAgentOptions,
|
|
21
23
|
} from "../types.js";
|
|
22
24
|
|
|
23
25
|
import { createState } from "../state/index.js";
|
|
@@ -28,6 +30,8 @@ import { MetricsRegistry } from "../metrics/index.js";
|
|
|
28
30
|
interface ParsedAgentConfig {
|
|
29
31
|
ai: AxAI;
|
|
30
32
|
name: string;
|
|
33
|
+
executionMode: AgentExecutionMode;
|
|
34
|
+
axAgentOptions?: AxCrewAxAgentOptions;
|
|
31
35
|
description: string;
|
|
32
36
|
definition?: string;
|
|
33
37
|
signature: string | AxSignature;
|
|
@@ -47,9 +51,14 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
47
51
|
state: StateInstance;
|
|
48
52
|
axai: any;
|
|
49
53
|
private agentName: string;
|
|
54
|
+
private agentDefinition: string;
|
|
55
|
+
private executionMode: AgentExecutionMode;
|
|
56
|
+
private axGenProgram: AxGen<any, any>;
|
|
50
57
|
private costTracker?: any;
|
|
51
|
-
private lastRecordedCostUSD: number = 0;
|
|
52
58
|
private debugEnabled: boolean = false;
|
|
59
|
+
private static readonly modernAxAgentRuntime =
|
|
60
|
+
typeof (AxAgent as any)?.prototype?.getFunction === "function" &&
|
|
61
|
+
typeof (AxAgent as any)?.prototype?.setExamples !== "function";
|
|
53
62
|
// ACE-related optional state
|
|
54
63
|
private aceConfig?: ACEConfig;
|
|
55
64
|
// eslint-disable-next-line @typescript-eslint/no-explicit-any
|
|
@@ -59,15 +68,14 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
59
68
|
private isAxAIService(obj: any): obj is AxAI {
|
|
60
69
|
return !!obj && typeof obj.getName === 'function' && typeof obj.chat === 'function';
|
|
61
70
|
}
|
|
62
|
-
private isAxAIInstance(obj: any): obj is AxAI {
|
|
63
|
-
return !!obj && typeof obj === 'object' && ('defaults' in obj || 'modelInfo' in obj);
|
|
64
|
-
}
|
|
65
71
|
|
|
66
72
|
constructor(
|
|
67
73
|
ai: AxAI,
|
|
68
74
|
options: Readonly<{
|
|
69
75
|
name: string;
|
|
70
76
|
description: string;
|
|
77
|
+
executionMode?: AgentExecutionMode;
|
|
78
|
+
axAgentOptions?: AxCrewAxAgentOptions;
|
|
71
79
|
definition?: string;
|
|
72
80
|
signature: string | AxSignature;
|
|
73
81
|
agents?: AxAgentic<any, any>[] | undefined;
|
|
@@ -78,161 +86,342 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
78
86
|
}>,
|
|
79
87
|
state: StateInstance
|
|
80
88
|
) {
|
|
81
|
-
const { examples, debug
|
|
82
|
-
const
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
89
|
+
const { examples, debug } = options;
|
|
90
|
+
const resolvedFunctions = (options.functions ?? []).map((fn) =>
|
|
91
|
+
typeof fn === "function" ? fn() : fn
|
|
92
|
+
) as AxFunction[];
|
|
93
|
+
const resolvedAgents = (options.agents ?? []) as AxAgentic<any, any>[];
|
|
94
|
+
const effectiveDefinition = (options.definition ?? options.description).trim();
|
|
95
|
+
|
|
96
|
+
if (StatefulAxAgent.modernAxAgentRuntime) {
|
|
97
|
+
const configuredAgentOptions = (options.axAgentOptions ?? {}) as Record<string, any>;
|
|
98
|
+
const configuredAgentGraph = (configuredAgentOptions.agents ?? {}) as Record<string, any>;
|
|
99
|
+
const configuredFunctionGraph = (configuredAgentOptions.functions ?? {}) as Record<string, any>;
|
|
100
|
+
const configuredActorOptions = (configuredAgentOptions.actorOptions ?? {}) as Record<string, any>;
|
|
101
|
+
const configuredResponderOptions = (configuredAgentOptions.responderOptions ?? {}) as Record<string, any>;
|
|
102
|
+
|
|
103
|
+
const modernOptions: Record<string, unknown> = {
|
|
104
|
+
...configuredAgentOptions,
|
|
105
|
+
debug: debug ?? configuredAgentOptions.debug ?? false,
|
|
106
|
+
contextFields: Array.isArray(configuredAgentOptions.contextFields)
|
|
107
|
+
? configuredAgentOptions.contextFields
|
|
108
|
+
: [],
|
|
109
|
+
};
|
|
110
|
+
|
|
111
|
+
modernOptions.agents = {
|
|
112
|
+
...configuredAgentGraph,
|
|
113
|
+
local: resolvedAgents,
|
|
114
|
+
};
|
|
115
|
+
modernOptions.functions = {
|
|
116
|
+
...configuredFunctionGraph,
|
|
117
|
+
local: resolvedFunctions,
|
|
118
|
+
};
|
|
119
|
+
|
|
120
|
+
if (effectiveDefinition.length > 0) {
|
|
121
|
+
modernOptions.actorOptions = {
|
|
122
|
+
...configuredActorOptions,
|
|
123
|
+
description: configuredActorOptions.description ?? effectiveDefinition,
|
|
124
|
+
};
|
|
125
|
+
modernOptions.responderOptions = {
|
|
126
|
+
...configuredResponderOptions,
|
|
127
|
+
description: configuredResponderOptions.description ?? effectiveDefinition,
|
|
128
|
+
};
|
|
129
|
+
} else {
|
|
130
|
+
modernOptions.actorOptions = configuredActorOptions;
|
|
131
|
+
modernOptions.responderOptions = configuredResponderOptions;
|
|
132
|
+
}
|
|
133
|
+
|
|
134
|
+
super(
|
|
135
|
+
{
|
|
136
|
+
ai,
|
|
137
|
+
agentIdentity: {
|
|
138
|
+
name: options.name,
|
|
139
|
+
description: options.description,
|
|
140
|
+
},
|
|
141
|
+
signature: options.signature as any,
|
|
142
|
+
} as any,
|
|
143
|
+
modernOptions as any
|
|
144
|
+
);
|
|
145
|
+
} else {
|
|
146
|
+
super(
|
|
147
|
+
{
|
|
148
|
+
name: options.name,
|
|
149
|
+
description: options.description,
|
|
150
|
+
definition: options.definition,
|
|
151
|
+
signature: options.signature,
|
|
152
|
+
agents: resolvedAgents,
|
|
153
|
+
functions: resolvedFunctions,
|
|
154
|
+
debug: debug ?? false,
|
|
155
|
+
} as any,
|
|
156
|
+
{} as any
|
|
157
|
+
);
|
|
158
|
+
}
|
|
159
|
+
|
|
89
160
|
this.state = state;
|
|
90
161
|
this.axai = ai;
|
|
91
162
|
this.agentName = options.name;
|
|
163
|
+
this.agentDefinition = effectiveDefinition;
|
|
164
|
+
this.executionMode = options.executionMode ?? "axagent";
|
|
92
165
|
this.debugEnabled = debug ?? false;
|
|
166
|
+
this.axGenProgram = new AxGen(options.signature as any, {
|
|
167
|
+
description: effectiveDefinition,
|
|
168
|
+
functions: resolvedFunctions,
|
|
169
|
+
} as any);
|
|
170
|
+
|
|
171
|
+
for (const agent of resolvedAgents) {
|
|
172
|
+
try {
|
|
173
|
+
const childName = agent.getFunction().name;
|
|
174
|
+
this.axGenProgram.register(agent as any, childName);
|
|
175
|
+
} catch {
|
|
176
|
+
// Best-effort registration for optimizer/introspection support.
|
|
177
|
+
}
|
|
178
|
+
}
|
|
93
179
|
|
|
94
|
-
//
|
|
180
|
+
// Apply examples to compatibility layer if provided
|
|
95
181
|
if (examples && examples.length > 0) {
|
|
96
|
-
|
|
182
|
+
this.setExamplesCompat(examples);
|
|
97
183
|
}
|
|
98
184
|
}
|
|
99
185
|
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
|
|
105
|
-
|
|
186
|
+
/**
|
|
187
|
+
* @deprecated Use setExamplesCompat() to avoid Ax runtime version coupling.
|
|
188
|
+
*/
|
|
189
|
+
setExamples(examples: Readonly<Array<Record<string, any>>>): void {
|
|
190
|
+
this.setExamplesCompat(examples);
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
setExamplesCompat(examples: Readonly<Array<Record<string, any>>>): void {
|
|
194
|
+
this.axGenProgram.setExamples(examples as any);
|
|
195
|
+
|
|
196
|
+
const baseSetExamples = (AxAgent.prototype as any).setExamples;
|
|
197
|
+
if (typeof baseSetExamples === "function") {
|
|
198
|
+
baseSetExamples.call(this, examples);
|
|
199
|
+
return;
|
|
200
|
+
}
|
|
201
|
+
|
|
202
|
+
const internalProgram = (this as any).program;
|
|
203
|
+
if (typeof internalProgram?.setExamples === "function") {
|
|
204
|
+
internalProgram.setExamples(examples as any);
|
|
205
|
+
}
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
/**
|
|
209
|
+
* @deprecated Use setDescriptionCompat() to avoid Ax runtime version coupling.
|
|
210
|
+
*/
|
|
211
|
+
setDescription(description: string): void {
|
|
212
|
+
this.setDescriptionCompat(description);
|
|
213
|
+
}
|
|
214
|
+
|
|
215
|
+
setDescriptionCompat(description: string): void {
|
|
216
|
+
this.agentDefinition = description;
|
|
217
|
+
this.axGenProgram.setDescription(description);
|
|
218
|
+
|
|
219
|
+
const baseSetDescription = (AxAgent.prototype as any).setDescription;
|
|
220
|
+
if (typeof baseSetDescription === "function") {
|
|
221
|
+
baseSetDescription.call(this, description);
|
|
222
|
+
return;
|
|
223
|
+
}
|
|
224
|
+
|
|
225
|
+
const agentRuntime = this as any;
|
|
226
|
+
if (typeof agentRuntime.program?.setDescription === "function") {
|
|
227
|
+
agentRuntime.program.setDescription(description);
|
|
228
|
+
}
|
|
229
|
+
agentRuntime.actorDescription = description;
|
|
230
|
+
agentRuntime.responderDescription = description;
|
|
231
|
+
if (typeof agentRuntime._buildSplitPrograms === "function") {
|
|
232
|
+
agentRuntime._buildSplitPrograms();
|
|
233
|
+
}
|
|
234
|
+
}
|
|
235
|
+
|
|
236
|
+
override getUsage() {
|
|
237
|
+
if (this.executionMode === "axgen") {
|
|
238
|
+
return this.axGenProgram.getUsage();
|
|
239
|
+
}
|
|
240
|
+
return super.getUsage();
|
|
241
|
+
}
|
|
242
|
+
|
|
243
|
+
override resetUsage() {
|
|
244
|
+
this.axGenProgram.resetUsage();
|
|
245
|
+
super.resetUsage();
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
private resolveInvocationArgs<TOptions>(
|
|
249
|
+
first: Record<string, any> | AxAI,
|
|
250
|
+
second?: Record<string, any> | Readonly<TOptions>,
|
|
251
|
+
third?: Readonly<TOptions>
|
|
252
|
+
): {
|
|
253
|
+
ai: AxAI;
|
|
254
|
+
values: Record<string, any>;
|
|
255
|
+
options?: Readonly<TOptions>;
|
|
256
|
+
calledWithAI: boolean;
|
|
257
|
+
} {
|
|
258
|
+
const calledWithAI = this.isAxAIService(first);
|
|
259
|
+
const ai = (calledWithAI ? first : this.axai) as AxAI;
|
|
260
|
+
if (!ai) {
|
|
261
|
+
throw new Error(`No AI instance is configured for agent "${this.agentName}"`);
|
|
262
|
+
}
|
|
263
|
+
|
|
264
|
+
const values = (calledWithAI ? second : first) as Record<string, any>;
|
|
265
|
+
const options = (calledWithAI ? third : second) as Readonly<TOptions> | undefined;
|
|
266
|
+
|
|
267
|
+
return { ai, values, options, calledWithAI };
|
|
268
|
+
}
|
|
269
|
+
|
|
270
|
+
private async executeForwardByMode(
|
|
271
|
+
mode: AgentExecutionMode,
|
|
272
|
+
ai: AxAI,
|
|
273
|
+
values: Record<string, any>,
|
|
274
|
+
options?: Readonly<AxProgramForwardOptions<any>>
|
|
275
|
+
): Promise<Record<string, any>> {
|
|
276
|
+
if (mode === "axgen") {
|
|
277
|
+
return this.axGenProgram.forward(ai, values, options as any);
|
|
278
|
+
}
|
|
279
|
+
return super.forward(ai, values, options as any);
|
|
280
|
+
}
|
|
281
|
+
|
|
282
|
+
private recordUsageMetrics(
|
|
283
|
+
labels: { crewId: string; agent: string },
|
|
284
|
+
mode: AgentExecutionMode
|
|
285
|
+
): void {
|
|
286
|
+
const builtIn =
|
|
287
|
+
mode === "axgen" ? this.axGenProgram.getUsage?.() : super.getUsage?.();
|
|
288
|
+
if (!Array.isArray(builtIn)) return;
|
|
289
|
+
|
|
290
|
+
const totals = builtIn.reduce(
|
|
291
|
+
(acc: any, u: any) => {
|
|
292
|
+
const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
|
|
293
|
+
const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
|
|
294
|
+
acc.promptTokens += typeof pt === "number" ? pt : 0;
|
|
295
|
+
acc.completionTokens += typeof ct === "number" ? ct : 0;
|
|
296
|
+
const model =
|
|
297
|
+
u.model ||
|
|
298
|
+
(this.axai as any)?.getLastUsedChatModel?.() ||
|
|
299
|
+
(this.axai as any)?.defaults?.model;
|
|
300
|
+
if (model) {
|
|
301
|
+
acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
|
|
302
|
+
}
|
|
303
|
+
return acc;
|
|
304
|
+
},
|
|
305
|
+
{ promptTokens: 0, completionTokens: 0, byModel: {} as Record<string, number> }
|
|
306
|
+
);
|
|
307
|
+
|
|
308
|
+
MetricsRegistry.recordTokens(labels, {
|
|
309
|
+
promptTokens: totals.promptTokens,
|
|
310
|
+
completionTokens: totals.completionTokens,
|
|
311
|
+
totalTokens: totals.promptTokens + totals.completionTokens,
|
|
312
|
+
});
|
|
313
|
+
|
|
314
|
+
const costTracker = (this as any).costTracker;
|
|
315
|
+
try {
|
|
316
|
+
for (const [m, count] of Object.entries(totals.byModel)) {
|
|
317
|
+
costTracker?.trackTokens?.(count, m);
|
|
318
|
+
}
|
|
319
|
+
const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
|
|
320
|
+
if (!Number.isNaN(totalUSD) && totalUSD > 0) {
|
|
321
|
+
MetricsRegistry.recordEstimatedCost(labels, totalUSD);
|
|
322
|
+
}
|
|
323
|
+
} catch {}
|
|
324
|
+
}
|
|
325
|
+
|
|
326
|
+
private async runForwardInvocation(
|
|
327
|
+
mode: AgentExecutionMode,
|
|
106
328
|
first: Record<string, any> | AxAI,
|
|
107
329
|
second?: Record<string, any> | Readonly<AxProgramForwardOptions<any>>,
|
|
108
330
|
third?: Readonly<AxProgramForwardOptions<any>>
|
|
109
331
|
): Promise<Record<string, any>> {
|
|
110
|
-
|
|
332
|
+
const { ai, values, options, calledWithAI } = this.resolveInvocationArgs(
|
|
333
|
+
first,
|
|
334
|
+
second,
|
|
335
|
+
third
|
|
336
|
+
);
|
|
111
337
|
|
|
112
338
|
const start = performance.now();
|
|
113
|
-
const crewId = (this.state as any)?.crewId ||
|
|
114
|
-
const labels = { crewId, agent: this.agentName }
|
|
115
|
-
const input = this.isAxAIService(first) ? second : first;
|
|
339
|
+
const crewId = (this.state as any)?.crewId || this.state.get?.("crewId") || "default";
|
|
340
|
+
const labels = { crewId, agent: this.agentName };
|
|
116
341
|
const taskId = `task_${crewId}_${Date.now()}_${Math.random().toString(36).substr(2, 9)}`;
|
|
117
342
|
|
|
118
343
|
// Track execution context in crew for ACE feedback routing
|
|
119
344
|
const crewInstance = (this.state as any)?.crew as AxCrew;
|
|
120
345
|
if (crewInstance) {
|
|
121
|
-
if (
|
|
122
|
-
// For sub-agent calls, track under parent task ID
|
|
346
|
+
if (calledWithAI) {
|
|
123
347
|
const parentTaskId = (this.state as any)?.currentTaskId;
|
|
124
348
|
if (parentTaskId) {
|
|
125
|
-
crewInstance.trackAgentExecution(parentTaskId, this.agentName,
|
|
349
|
+
crewInstance.trackAgentExecution(parentTaskId, this.agentName, values);
|
|
126
350
|
}
|
|
127
351
|
} else {
|
|
128
|
-
|
|
129
|
-
crewInstance.trackAgentExecution(taskId, this.agentName, input);
|
|
352
|
+
crewInstance.trackAgentExecution(taskId, this.agentName, values);
|
|
130
353
|
(this.state as any).currentTaskId = taskId;
|
|
131
354
|
}
|
|
132
355
|
}
|
|
133
356
|
|
|
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
357
|
if (this.debugEnabled) {
|
|
137
|
-
console.log(`[ACE Debug] forward() called, aceConfig=${!!this.aceConfig}`);
|
|
358
|
+
console.log(`[ACE Debug] forward() called, mode=${mode}, aceConfig=${!!this.aceConfig}`);
|
|
138
359
|
}
|
|
139
360
|
if (this.aceConfig) {
|
|
140
361
|
await this.composeInstructionWithPlaybook();
|
|
141
362
|
}
|
|
142
363
|
|
|
143
|
-
|
|
144
|
-
// Note: OpenTelemetry spans are automatically created by AxAI (configured via AxCrewOptions.telemetry)
|
|
145
|
-
if (this.isAxAIService(first)) {
|
|
146
|
-
// Sub-agent case (called with AI service)
|
|
147
|
-
result = await super.forward(this.axai, second as Record<string, any>, third);
|
|
148
|
-
} else {
|
|
149
|
-
// Direct call case
|
|
150
|
-
result = await super.forward(this.axai, first, second as Readonly<AxProgramForwardOptions<any>>);
|
|
151
|
-
}
|
|
364
|
+
const result = await this.executeForwardByMode(mode, ai, values, options);
|
|
152
365
|
|
|
153
|
-
// Track metrics and costs after the call using built-in usage
|
|
154
366
|
const durationMs = performance.now() - start;
|
|
155
367
|
MetricsRegistry.recordRequest(labels, false, durationMs);
|
|
156
|
-
|
|
157
|
-
const builtIn = (this as any).getUsage?.();
|
|
158
|
-
if (Array.isArray(builtIn)) {
|
|
159
|
-
const totals = builtIn.reduce(
|
|
160
|
-
(acc: any, u: any) => {
|
|
161
|
-
const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
|
|
162
|
-
const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
|
|
163
|
-
acc.promptTokens += typeof pt === 'number' ? pt : 0;
|
|
164
|
-
acc.completionTokens += typeof ct === 'number' ? ct : 0;
|
|
165
|
-
// also aggregate per-model to feed Ax tracker
|
|
166
|
-
const model = u.model || (this.axai as any)?.getLastUsedChatModel?.() || (this.axai as any)?.defaults?.model;
|
|
167
|
-
if (model) {
|
|
168
|
-
acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
|
|
169
|
-
}
|
|
170
|
-
return acc;
|
|
171
|
-
},
|
|
172
|
-
{ promptTokens: 0, completionTokens: 0, byModel: {} as Record<string, number> }
|
|
173
|
-
);
|
|
174
|
-
MetricsRegistry.recordTokens(labels, {
|
|
175
|
-
promptTokens: totals.promptTokens,
|
|
176
|
-
completionTokens: totals.completionTokens,
|
|
177
|
-
totalTokens: totals.promptTokens + totals.completionTokens,
|
|
178
|
-
});
|
|
179
|
-
// Feed Ax's cost tracker with token totals per model; Ax owns pricing
|
|
180
|
-
const costTracker = (this as any).costTracker;
|
|
181
|
-
try {
|
|
182
|
-
for (const [m, count] of Object.entries(totals.byModel)) {
|
|
183
|
-
costTracker?.trackTokens?.(count, m);
|
|
184
|
-
}
|
|
185
|
-
const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
|
|
186
|
-
if (!Number.isNaN(totalUSD) && totalUSD > 0) {
|
|
187
|
-
MetricsRegistry.recordEstimatedCost(labels, totalUSD);
|
|
188
|
-
}
|
|
189
|
-
} catch {}
|
|
190
|
-
}
|
|
368
|
+
this.recordUsageMetrics(labels, mode);
|
|
191
369
|
|
|
192
|
-
// Record result in crew execution history for ACE feedback routing
|
|
193
370
|
if (crewInstance) {
|
|
194
|
-
if (
|
|
195
|
-
// For sub-agent calls, record under parent task ID
|
|
371
|
+
if (calledWithAI) {
|
|
196
372
|
const parentTaskId = (this.state as any)?.currentTaskId;
|
|
197
373
|
if (parentTaskId) {
|
|
198
374
|
crewInstance.recordAgentResult(parentTaskId, this.agentName, result);
|
|
199
375
|
}
|
|
200
376
|
} else {
|
|
201
|
-
// Root-level result - include taskId for feedback routing
|
|
202
377
|
crewInstance.recordAgentResult(taskId, this.agentName, result);
|
|
203
|
-
// Clean up current task ID
|
|
204
378
|
delete (this.state as any).currentTaskId;
|
|
205
|
-
|
|
206
|
-
result._taskId = taskId;
|
|
379
|
+
(result as any)._taskId = taskId;
|
|
207
380
|
}
|
|
208
381
|
}
|
|
209
382
|
|
|
210
383
|
return result;
|
|
211
384
|
}
|
|
212
385
|
|
|
213
|
-
//
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
386
|
+
// Function overloads for forward method
|
|
387
|
+
async forward(values: Record<string, any>, options?: Readonly<AxProgramForwardOptions<any>>): Promise<Record<string, any>>;
|
|
388
|
+
async forward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramForwardOptions<any>>): Promise<Record<string, any>>;
|
|
389
|
+
|
|
217
390
|
// Implementation
|
|
218
|
-
|
|
391
|
+
async forward(
|
|
392
|
+
first: Record<string, any> | AxAI,
|
|
393
|
+
second?: Record<string, any> | Readonly<AxProgramForwardOptions<any>>,
|
|
394
|
+
third?: Readonly<AxProgramForwardOptions<any>>
|
|
395
|
+
): Promise<Record<string, any>> {
|
|
396
|
+
return this.runForwardInvocation(this.executionMode, first, second, third);
|
|
397
|
+
}
|
|
398
|
+
|
|
399
|
+
private runStreamingInvocation(
|
|
400
|
+
mode: AgentExecutionMode,
|
|
219
401
|
first: Record<string, any> | AxAI,
|
|
220
402
|
second?: Record<string, any> | Readonly<AxProgramStreamingForwardOptions<any>>,
|
|
221
403
|
third?: Readonly<AxProgramStreamingForwardOptions<any>>
|
|
222
404
|
): AxGenStreamingOut<any> {
|
|
405
|
+
const { ai, values, options } = this.resolveInvocationArgs(
|
|
406
|
+
first,
|
|
407
|
+
second,
|
|
408
|
+
third
|
|
409
|
+
);
|
|
223
410
|
const start = performance.now();
|
|
224
|
-
const crewId = (this.state as any)?.crewId ||
|
|
225
|
-
const labels = { crewId, agent: this.agentName }
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
232
|
-
|
|
411
|
+
const crewId = (this.state as any)?.crewId || this.state.get?.("crewId") || "default";
|
|
412
|
+
const labels = { crewId, agent: this.agentName };
|
|
413
|
+
|
|
414
|
+
const createStream = () =>
|
|
415
|
+
mode === "axgen"
|
|
416
|
+
? this.axGenProgram.streamingForward(ai, values, options as any)
|
|
417
|
+
: super.streamingForward(ai, values, options as any);
|
|
418
|
+
|
|
419
|
+
const wrappedGenerator = (async function* (this: StatefulAxAgent) {
|
|
420
|
+
if (this.aceConfig) {
|
|
421
|
+
await this.composeInstructionWithPlaybook();
|
|
422
|
+
}
|
|
233
423
|
|
|
234
|
-
|
|
235
|
-
const wrappedGenerator = (async function*(this: StatefulAxAgent) {
|
|
424
|
+
const streamingResult = createStream();
|
|
236
425
|
try {
|
|
237
426
|
for await (const chunk of streamingResult) {
|
|
238
427
|
yield chunk;
|
|
@@ -240,53 +429,26 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
240
429
|
} finally {
|
|
241
430
|
const durationMs = performance.now() - start;
|
|
242
431
|
MetricsRegistry.recordRequest(labels, true, durationMs);
|
|
243
|
-
|
|
244
|
-
const builtIn = (this as any).getUsage?.();
|
|
245
|
-
if (Array.isArray(builtIn)) {
|
|
246
|
-
const totals = builtIn.reduce(
|
|
247
|
-
(acc: any, u: any) => {
|
|
248
|
-
const pt = u.tokens?.promptTokens ?? u.promptTokens ?? 0;
|
|
249
|
-
const ct = u.tokens?.completionTokens ?? u.completionTokens ?? 0;
|
|
250
|
-
acc.promptTokens += typeof pt === 'number' ? pt : 0;
|
|
251
|
-
acc.completionTokens += typeof ct === 'number' ? ct : 0;
|
|
252
|
-
const model = u.model || (this.axai as any)?.getLastUsedChatModel?.() || (this.axai as any)?.defaults?.model;
|
|
253
|
-
if (model) {
|
|
254
|
-
acc.byModel[model] = (acc.byModel[model] || 0) + (pt + ct);
|
|
255
|
-
}
|
|
256
|
-
return acc;
|
|
257
|
-
},
|
|
258
|
-
{ promptTokens: 0, completionTokens: 0, byModel: {} as Record<string, number> }
|
|
259
|
-
);
|
|
260
|
-
MetricsRegistry.recordTokens(labels, {
|
|
261
|
-
promptTokens: totals.promptTokens,
|
|
262
|
-
completionTokens: totals.completionTokens,
|
|
263
|
-
totalTokens: totals.promptTokens + totals.completionTokens,
|
|
264
|
-
});
|
|
265
|
-
const costTracker = (this as any).costTracker;
|
|
266
|
-
try {
|
|
267
|
-
for (const [m, count] of Object.entries(totals.byModel)) {
|
|
268
|
-
costTracker?.trackTokens?.(count, m);
|
|
269
|
-
}
|
|
270
|
-
const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
|
|
271
|
-
if (!Number.isNaN(totalUSD) && totalUSD > 0) {
|
|
272
|
-
MetricsRegistry.recordEstimatedCost(labels, totalUSD);
|
|
273
|
-
}
|
|
274
|
-
} catch {}
|
|
275
|
-
}
|
|
276
|
-
// Record estimated cost (USD) via attached tracker if available
|
|
277
|
-
const costTracker = (this as any).costTracker;
|
|
278
|
-
try {
|
|
279
|
-
const totalUSD = Number(costTracker?.getCurrentCost?.() ?? 0);
|
|
280
|
-
if (!Number.isNaN(totalUSD) && totalUSD > 0) {
|
|
281
|
-
MetricsRegistry.recordEstimatedCost(labels, totalUSD);
|
|
282
|
-
}
|
|
283
|
-
} catch {}
|
|
432
|
+
this.recordUsageMetrics(labels, mode);
|
|
284
433
|
}
|
|
285
434
|
}).bind(this)();
|
|
286
435
|
|
|
287
436
|
return wrappedGenerator as AxGenStreamingOut<any>;
|
|
288
437
|
}
|
|
289
438
|
|
|
439
|
+
// Add streaming forward method overloads
|
|
440
|
+
streamingForward(values: Record<string, any>, options?: Readonly<AxProgramStreamingForwardOptions<any>>): AxGenStreamingOut<any>;
|
|
441
|
+
streamingForward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramStreamingForwardOptions<any>>): AxGenStreamingOut<any>;
|
|
442
|
+
|
|
443
|
+
// Implementation
|
|
444
|
+
streamingForward(
|
|
445
|
+
first: Record<string, any> | AxAI,
|
|
446
|
+
second?: Record<string, any> | Readonly<AxProgramStreamingForwardOptions<any>>,
|
|
447
|
+
third?: Readonly<AxProgramStreamingForwardOptions<any>>
|
|
448
|
+
): AxGenStreamingOut<any> {
|
|
449
|
+
return this.runStreamingInvocation(this.executionMode, first, second, third);
|
|
450
|
+
}
|
|
451
|
+
|
|
290
452
|
// Legacy cost API removed: rely on Ax trackers for cost reporting
|
|
291
453
|
getLastUsageCost(): UsageCost | null { return null; }
|
|
292
454
|
|
|
@@ -328,7 +490,8 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
328
490
|
if (!ace) return;
|
|
329
491
|
try {
|
|
330
492
|
// Capture base instruction BEFORE any playbook injection (mirrors AxACE.extractProgramInstruction)
|
|
331
|
-
this.aceBaseInstruction =
|
|
493
|
+
this.aceBaseInstruction =
|
|
494
|
+
this.agentDefinition || this.getSignature().getDescription() || '';
|
|
332
495
|
|
|
333
496
|
const { buildACEOptimizer, loadInitialPlaybook, createEmptyPlaybook } = await import('./ace.js');
|
|
334
497
|
// Build optimizer with agent's AI as student
|
|
@@ -615,7 +778,7 @@ class AxCrew {
|
|
|
615
778
|
);
|
|
616
779
|
|
|
617
780
|
// Destructure with type assertion
|
|
618
|
-
const { ai, name, description, signature, functions, subAgentNames, examples, tracker } = agentConfig;
|
|
781
|
+
const { ai, name, executionMode, axAgentOptions, description, signature, functions, subAgentNames, examples, tracker } = agentConfig;
|
|
619
782
|
|
|
620
783
|
// Get subagents for the AI agent
|
|
621
784
|
const subAgents = subAgentNames.map((subAgentName: string) => {
|
|
@@ -685,6 +848,8 @@ class AxCrew {
|
|
|
685
848
|
ai,
|
|
686
849
|
{
|
|
687
850
|
name,
|
|
851
|
+
executionMode,
|
|
852
|
+
axAgentOptions,
|
|
688
853
|
description,
|
|
689
854
|
definition: (agentConfig as any).definition,
|
|
690
855
|
signature,
|
package/src/index.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
|
|
|
5
5
|
import type {
|
|
6
6
|
UsageCost,
|
|
@@ -50,6 +50,8 @@ export {
|
|
|
50
50
|
type AggregatedMetrics,
|
|
51
51
|
type AggregatedCosts,
|
|
52
52
|
type AgentConfig,
|
|
53
|
+
type AgentExecutionMode,
|
|
54
|
+
type AxCrewAxAgentOptions,
|
|
53
55
|
type AxCrewConfig,
|
|
54
56
|
type AxCrewOptions,
|
|
55
57
|
type StateInstance,
|
|
@@ -60,4 +62,4 @@ export {
|
|
|
60
62
|
type ACEPersistenceConfig,
|
|
61
63
|
type ACEOptionsConfig,
|
|
62
64
|
type ACEMetricConfig,
|
|
63
|
-
};
|
|
65
|
+
};
|
package/src/types.ts
CHANGED
|
@@ -4,12 +4,21 @@ import type {
|
|
|
4
4
|
AxModelConfig,
|
|
5
5
|
AxMCPStreamableHTTPTransportOptions,
|
|
6
6
|
AxProgramForwardOptions,
|
|
7
|
-
AxAIArgs
|
|
7
|
+
AxAIArgs,
|
|
8
|
+
AxAgentOptions
|
|
8
9
|
} from '@ax-llm/ax';
|
|
9
10
|
|
|
10
11
|
// Provider ids are derived from Ax's factory arg type so new providers added in Ax
|
|
11
12
|
// are picked up at compile time without updating AxCrew.
|
|
12
13
|
export type Provider = AxAIArgs<any>['name'];
|
|
14
|
+
export type AgentExecutionMode = 'axagent' | 'axgen';
|
|
15
|
+
export type AxCrewAxAgentOptions =
|
|
16
|
+
Partial<Omit<AxAgentOptions, 'agents' | 'functions' | 'contextFields'>> & {
|
|
17
|
+
contextFields?: AxAgentOptions['contextFields'];
|
|
18
|
+
agents?: AxAgentOptions['agents'];
|
|
19
|
+
functions?: AxAgentOptions['functions'];
|
|
20
|
+
fields?: AxAgentOptions['fields'];
|
|
21
|
+
};
|
|
13
22
|
|
|
14
23
|
/**
|
|
15
24
|
* A state instance that is shared between agents.
|
|
@@ -208,6 +217,17 @@ interface ACEConfig {
|
|
|
208
217
|
interface AgentConfig {
|
|
209
218
|
name: string;
|
|
210
219
|
description: string;
|
|
220
|
+
/**
|
|
221
|
+
* Choose the execution engine for this agent.
|
|
222
|
+
* - `axagent` (default): Uses AxAgent capabilities.
|
|
223
|
+
* - `axgen`: Uses AxGen capabilities.
|
|
224
|
+
*/
|
|
225
|
+
executionMode?: AgentExecutionMode;
|
|
226
|
+
/**
|
|
227
|
+
* AxAgent runtime options (RLM mode, context fields, recursion, actor/responder options, etc).
|
|
228
|
+
* Only applies when executionMode is `axagent`.
|
|
229
|
+
*/
|
|
230
|
+
axAgentOptions?: AxCrewAxAgentOptions;
|
|
211
231
|
/**
|
|
212
232
|
* Optional detailed persona/program definition. If provided, becomes the system prompt.
|
|
213
233
|
* Must be at least 100 characters per Ax semantics.
|