@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.
- package/.claude/settings.local.json +5 -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/dist/agents/index.js
CHANGED
|
@@ -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
|
|
28
|
-
const
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
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
|
-
|
|
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
|
-
|
|
108
|
+
this.setExamplesCompat(examples);
|
|
40
109
|
}
|
|
41
110
|
}
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
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 ||
|
|
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 (
|
|
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,
|
|
225
|
+
crewInstance.trackAgentExecution(parentTaskId, this.agentName, values);
|
|
58
226
|
}
|
|
59
227
|
}
|
|
60
228
|
else {
|
|
61
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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 (
|
|
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
|
-
|
|
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 ||
|
|
265
|
+
const crewId = this.state?.crewId || this.state.get?.("crewId") || "default";
|
|
144
266
|
const labels = { crewId, agent: this.agentName };
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
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
|
-
|
|
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 =
|
|
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
|
-
|
|
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?.
|
|
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
|
+
});
|