@amitdeshmukh/ax-crew 4.1.1 → 5.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/CHANGELOG.md +30 -0
- package/README.md +28 -0
- package/dist/agents/agentConfig.js +30 -0
- package/dist/agents/agentUseCosts.d.ts +14 -0
- package/dist/agents/agentUseCosts.js +14 -0
- package/dist/agents/index.d.ts +22 -1
- package/dist/agents/index.js +22 -1
- package/dist/config/index.js +2 -0
- package/dist/metrics/registry.d.ts +27 -0
- package/dist/metrics/registry.js +27 -0
- package/dist/types.d.ts +5 -0
- package/examples/providerArgs.ts +41 -0
- package/package.json +2 -2
- package/src/agents/agentConfig.ts +37 -0
- package/src/agents/agentUseCosts.ts +14 -0
- package/src/agents/index.ts +22 -1
- package/src/config/index.ts +2 -0
- package/src/metrics/registry.ts +27 -0
- package/src/types.ts +5 -0
- package/tsconfig.json +2 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,4 +1,34 @@
|
|
|
1
1
|
# Changelog
|
|
2
|
+
|
|
3
|
+
## [5.0.0] - 2025-10-16
|
|
4
|
+
|
|
5
|
+
### Added
|
|
6
|
+
- Provider-specific arguments via `providerArgs` in `AgentConfig` to pass through typed provider params to the underlying Ax factory.
|
|
7
|
+
- Typed forwarding for common providers in `parseAgentConfig`:
|
|
8
|
+
- azure-openai: `resourceName`, `deploymentName`, `version` (falls back to `apiURL` as `resourceName` if provided)
|
|
9
|
+
- anthropic: `projectId`, `region` (on Vertex AI)
|
|
10
|
+
- google-gemini: `projectId`, `region`, `endpointId`
|
|
11
|
+
- openrouter: `referer`, `title`
|
|
12
|
+
- ollama: `url`
|
|
13
|
+
- Example `examples/providerArgs.ts` demonstrating `providerArgs` with Azure OpenAI.
|
|
14
|
+
- JSDoc function hints for cost/usage APIs (`getMetrics`, `resetMetrics`, `resetCosts`, `getCrewMetrics`, `resetCrewMetrics`, and metrics registry helpers).
|
|
15
|
+
|
|
16
|
+
### Docs
|
|
17
|
+
- README section documenting `providerArgs` usage, supported providers, and Azure configuration examples.
|
|
18
|
+
|
|
19
|
+
### Notes
|
|
20
|
+
- Existing configs continue to work. For Azure, you can still set `apiURL`; when `resourceName` is omitted, the code uses `apiURL` as the `resourceName` (full endpoint) for convenience.
|
|
21
|
+
|
|
22
|
+
# Changelog
|
|
23
|
+
## [4.1.2] - 2025-10-14
|
|
24
|
+
|
|
25
|
+
### Added
|
|
26
|
+
- Azure OpenAI provider key support via `AZURE_OPENAI_API_KEY`.
|
|
27
|
+
- Documentation: README Environment now lists Azure and includes a minimal Azure config example (`provider: "azure-openai"` + `apiURL`).
|
|
28
|
+
|
|
29
|
+
### Changed
|
|
30
|
+
- TypeScript config now includes Node types (`types: ["node"]`) to resolve `process` usage in `src/config/index.ts`.
|
|
31
|
+
|
|
2
32
|
## [4.1.0] - 2025-08-25
|
|
3
33
|
|
|
4
34
|
### Added
|
package/README.md
CHANGED
|
@@ -31,9 +31,37 @@ Set provider keys in your environment. Example `.env`:
|
|
|
31
31
|
GEMINI_API_KEY=...
|
|
32
32
|
ANTHROPIC_API_KEY=...
|
|
33
33
|
OPENAI_API_KEY=...
|
|
34
|
+
AZURE_OPENAI_API_KEY=...
|
|
34
35
|
```
|
|
35
36
|
In each agent config, set `providerKeyName` to the env var name.
|
|
36
37
|
|
|
38
|
+
#### Provider-specific arguments (`providerArgs`)
|
|
39
|
+
Some providers require extra top-level configuration beyond `ai.model` (e.g., Azure deployments or Gemini project/region). You can pass these via `providerArgs`; AxCrew forwards them to the underlying Ax factory with type alignment for popular providers.
|
|
40
|
+
|
|
41
|
+
Supported mappings:
|
|
42
|
+
- `azure-openai`: `resourceName`, `deploymentName`, `version` (if `resourceName` omitted and `apiURL` set, `apiURL` is used as full endpoint)
|
|
43
|
+
- `anthropic`: `projectId`, `region`
|
|
44
|
+
- `google-gemini`: `projectId`, `region`, `endpointId`
|
|
45
|
+
- `openrouter`: `referer`, `title`
|
|
46
|
+
- `ollama`: `url`
|
|
47
|
+
|
|
48
|
+
Azure example with `providerArgs`:
|
|
49
|
+
```json
|
|
50
|
+
{
|
|
51
|
+
"name": "Writer",
|
|
52
|
+
"description": "Writes concise summaries",
|
|
53
|
+
"signature": "topic:string -> summary:string",
|
|
54
|
+
"provider": "azure-openai",
|
|
55
|
+
"providerKeyName": "AZURE_OPENAI_API_KEY",
|
|
56
|
+
"ai": { "model": "gpt-4o-mini", "temperature": 0 },
|
|
57
|
+
"apiURL": "https://your-resource.openai.azure.com", "providerArgs": {
|
|
58
|
+
"resourceName": "your-resource",
|
|
59
|
+
"deploymentName": "gpt-4o-mini",
|
|
60
|
+
"version": "api-version=2024-02-15-preview"
|
|
61
|
+
}
|
|
62
|
+
}
|
|
63
|
+
```
|
|
64
|
+
|
|
37
65
|
### Quickstart
|
|
38
66
|
This package includes TypeScript declarations and provides type safety.
|
|
39
67
|
|
|
@@ -214,6 +214,36 @@ const parseAgentConfig = async (agentName, crewConfig, functions, state) => {
|
|
|
214
214
|
throw new Error(`Invalid apiURL provided: ${agentConfigData.apiURL}`);
|
|
215
215
|
}
|
|
216
216
|
}
|
|
217
|
+
// Forward provider-specific arguments with type-safety for Azure OpenAI
|
|
218
|
+
const providerArgs = agentConfigData.providerArgs;
|
|
219
|
+
if (provider === 'azure-openai') {
|
|
220
|
+
const az = providerArgs ?? {};
|
|
221
|
+
// If users supplied apiURL instead of resourceName, accept it (Ax supports full URL as resourceName)
|
|
222
|
+
if (!az.resourceName && agentConfigData.apiURL) {
|
|
223
|
+
az.resourceName = agentConfigData.apiURL;
|
|
224
|
+
}
|
|
225
|
+
Object.assign(aiArgs, az);
|
|
226
|
+
}
|
|
227
|
+
else if (provider === 'anthropic') {
|
|
228
|
+
const an = providerArgs ?? {};
|
|
229
|
+
Object.assign(aiArgs, an);
|
|
230
|
+
}
|
|
231
|
+
else if (provider === 'google-gemini') {
|
|
232
|
+
const g = providerArgs ?? {};
|
|
233
|
+
Object.assign(aiArgs, g);
|
|
234
|
+
}
|
|
235
|
+
else if (provider === 'openrouter') {
|
|
236
|
+
const o = providerArgs ?? {};
|
|
237
|
+
Object.assign(aiArgs, o);
|
|
238
|
+
}
|
|
239
|
+
else if (provider === 'ollama') {
|
|
240
|
+
const ol = providerArgs ?? {};
|
|
241
|
+
Object.assign(aiArgs, ol);
|
|
242
|
+
}
|
|
243
|
+
else if (providerArgs && typeof providerArgs === 'object') {
|
|
244
|
+
// Generic pass-through for other providers if needed in the future
|
|
245
|
+
Object.assign(aiArgs, providerArgs);
|
|
246
|
+
}
|
|
217
247
|
const aiInstance = ai(aiArgs);
|
|
218
248
|
// If an mcpServers config is provided in the agent config, convert to functions
|
|
219
249
|
const mcpFunctions = await initializeMCPServers(agentConfigData);
|
|
@@ -5,8 +5,22 @@ import type { StateInstance, ModelUsage, ModelInfo, UsageCost, AggregatedCosts }
|
|
|
5
5
|
*/
|
|
6
6
|
export declare class StateFulAxAgentUsage {
|
|
7
7
|
static STATE_KEY_PREFIX: string;
|
|
8
|
+
/**
|
|
9
|
+
* Compute usage costs given a model usage record and model pricing info.
|
|
10
|
+
* Returns null if inputs are invalid. Token-based costs are computed with high precision.
|
|
11
|
+
*/
|
|
8
12
|
static calculateCost(modelUsage: ModelUsage, modelInfo: ModelInfo): UsageCost | null;
|
|
13
|
+
/**
|
|
14
|
+
* Persist or aggregate the cost for an agent in the shared crew state.
|
|
15
|
+
* No-op if cost is null.
|
|
16
|
+
*/
|
|
9
17
|
static trackCostInState(agentName: string, cost: UsageCost | null, state: StateInstance): void;
|
|
18
|
+
/**
|
|
19
|
+
* Aggregate and return total costs across all agents from the shared crew state.
|
|
20
|
+
*/
|
|
10
21
|
static getAggregatedCosts(state: StateInstance): AggregatedCosts;
|
|
22
|
+
/**
|
|
23
|
+
* Remove all stored per-agent costs from the shared crew state.
|
|
24
|
+
*/
|
|
11
25
|
static resetCosts(state: StateInstance): void;
|
|
12
26
|
}
|
|
@@ -5,6 +5,10 @@ import { Decimal } from 'decimal.js';
|
|
|
5
5
|
*/
|
|
6
6
|
export class StateFulAxAgentUsage {
|
|
7
7
|
static STATE_KEY_PREFIX = 'agent_usage_';
|
|
8
|
+
/**
|
|
9
|
+
* Compute usage costs given a model usage record and model pricing info.
|
|
10
|
+
* Returns null if inputs are invalid. Token-based costs are computed with high precision.
|
|
11
|
+
*/
|
|
8
12
|
static calculateCost(modelUsage, modelInfo) {
|
|
9
13
|
// Handle both direct properties and nested tokens structure
|
|
10
14
|
const promptTokens = modelUsage.tokens?.promptTokens ?? modelUsage.promptTokens;
|
|
@@ -44,6 +48,10 @@ export class StateFulAxAgentUsage {
|
|
|
44
48
|
return null;
|
|
45
49
|
}
|
|
46
50
|
}
|
|
51
|
+
/**
|
|
52
|
+
* Persist or aggregate the cost for an agent in the shared crew state.
|
|
53
|
+
* No-op if cost is null.
|
|
54
|
+
*/
|
|
47
55
|
static trackCostInState(agentName, cost, state) {
|
|
48
56
|
// If cost is null, skip tracking
|
|
49
57
|
if (!cost)
|
|
@@ -75,6 +83,9 @@ export class StateFulAxAgentUsage {
|
|
|
75
83
|
state.set(stateKey, cost);
|
|
76
84
|
}
|
|
77
85
|
}
|
|
86
|
+
/**
|
|
87
|
+
* Aggregate and return total costs across all agents from the shared crew state.
|
|
88
|
+
*/
|
|
78
89
|
static getAggregatedCosts(state) {
|
|
79
90
|
const allState = state.getAll();
|
|
80
91
|
const agentCosts = {};
|
|
@@ -108,6 +119,9 @@ export class StateFulAxAgentUsage {
|
|
|
108
119
|
}
|
|
109
120
|
};
|
|
110
121
|
}
|
|
122
|
+
/**
|
|
123
|
+
* Remove all stored per-agent costs from the shared crew state.
|
|
124
|
+
*/
|
|
111
125
|
static resetCosts(state) {
|
|
112
126
|
const allState = state.getAll();
|
|
113
127
|
Object.keys(allState).forEach(key => {
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -25,7 +25,17 @@ declare class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
25
25
|
streamingForward(ai: AxAI, values: Record<string, any>, options?: Readonly<AxProgramStreamingForwardOptions<any>>): AxGenStreamingOut<any>;
|
|
26
26
|
getLastUsageCost(): UsageCost | null;
|
|
27
27
|
getAccumulatedCosts(): UsageCost | null;
|
|
28
|
+
/**
|
|
29
|
+
* Get the current metrics snapshot for this agent.
|
|
30
|
+
* Includes request counts, error rates, token usage, estimated USD cost, and function call stats.
|
|
31
|
+
*
|
|
32
|
+
* @returns A metrics snapshot scoped to this agent within its crew.
|
|
33
|
+
*/
|
|
28
34
|
getMetrics(): import("../metrics/types.js").MetricsSnapshot;
|
|
35
|
+
/**
|
|
36
|
+
* Reset all tracked metrics for this agent (does not affect other agents).
|
|
37
|
+
* Call this to start fresh measurement windows for the agent.
|
|
38
|
+
*/
|
|
29
39
|
resetMetrics(): void;
|
|
30
40
|
}
|
|
31
41
|
/**
|
|
@@ -70,10 +80,21 @@ declare class AxCrew {
|
|
|
70
80
|
*/
|
|
71
81
|
destroy(): void;
|
|
72
82
|
/**
|
|
73
|
-
* Resets all cost tracking for the crew
|
|
83
|
+
* Resets all cost and usage tracking for the entire crew.
|
|
84
|
+
* Also calls each agent's `resetUsage` (if available) and clears crew-level metrics.
|
|
74
85
|
*/
|
|
75
86
|
resetCosts(): void;
|
|
87
|
+
/**
|
|
88
|
+
* Get an aggregate metrics snapshot for the entire crew.
|
|
89
|
+
* Sums requests, errors, tokens, and estimated cost across all agents in the crew.
|
|
90
|
+
*
|
|
91
|
+
* @returns Crew-level metrics snapshot.
|
|
92
|
+
*/
|
|
76
93
|
getCrewMetrics(): import("../metrics/types.js").MetricsSnapshot;
|
|
94
|
+
/**
|
|
95
|
+
* Reset all tracked metrics for the entire crew.
|
|
96
|
+
* Use to clear totals before a new measurement period.
|
|
97
|
+
*/
|
|
77
98
|
resetCrewMetrics(): void;
|
|
78
99
|
}
|
|
79
100
|
export { AxCrew };
|
package/dist/agents/index.js
CHANGED
|
@@ -156,10 +156,20 @@ class StatefulAxAgent extends AxAgent {
|
|
|
156
156
|
// Get the accumulated costs for all runs of this agent
|
|
157
157
|
getAccumulatedCosts() { return null; }
|
|
158
158
|
// Metrics API for this agent
|
|
159
|
+
/**
|
|
160
|
+
* Get the current metrics snapshot for this agent.
|
|
161
|
+
* Includes request counts, error rates, token usage, estimated USD cost, and function call stats.
|
|
162
|
+
*
|
|
163
|
+
* @returns A metrics snapshot scoped to this agent within its crew.
|
|
164
|
+
*/
|
|
159
165
|
getMetrics() {
|
|
160
166
|
const crewId = this.state?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
161
167
|
return MetricsRegistry.snapshot({ crewId, agent: this.agentName });
|
|
162
168
|
}
|
|
169
|
+
/**
|
|
170
|
+
* Reset all tracked metrics for this agent (does not affect other agents).
|
|
171
|
+
* Call this to start fresh measurement windows for the agent.
|
|
172
|
+
*/
|
|
163
173
|
resetMetrics() {
|
|
164
174
|
const crewId = this.state?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
165
175
|
MetricsRegistry.reset({ crewId, agent: this.agentName });
|
|
@@ -376,7 +386,8 @@ class AxCrew {
|
|
|
376
386
|
this.state.reset();
|
|
377
387
|
}
|
|
378
388
|
/**
|
|
379
|
-
* Resets all cost tracking for the crew
|
|
389
|
+
* Resets all cost and usage tracking for the entire crew.
|
|
390
|
+
* Also calls each agent's `resetUsage` (if available) and clears crew-level metrics.
|
|
380
391
|
*/
|
|
381
392
|
resetCosts() {
|
|
382
393
|
// Reset AxAgent built-in usage and our metrics registry
|
|
@@ -395,9 +406,19 @@ class AxCrew {
|
|
|
395
406
|
MetricsRegistry.reset({ crewId: this.crewId });
|
|
396
407
|
}
|
|
397
408
|
// Metrics API
|
|
409
|
+
/**
|
|
410
|
+
* Get an aggregate metrics snapshot for the entire crew.
|
|
411
|
+
* Sums requests, errors, tokens, and estimated cost across all agents in the crew.
|
|
412
|
+
*
|
|
413
|
+
* @returns Crew-level metrics snapshot.
|
|
414
|
+
*/
|
|
398
415
|
getCrewMetrics() {
|
|
399
416
|
return MetricsRegistry.snapshotCrew(this.crewId);
|
|
400
417
|
}
|
|
418
|
+
/**
|
|
419
|
+
* Reset all tracked metrics for the entire crew.
|
|
420
|
+
* Use to clear totals before a new measurement period.
|
|
421
|
+
*/
|
|
401
422
|
resetCrewMetrics() {
|
|
402
423
|
MetricsRegistry.reset({ crewId: this.crewId });
|
|
403
424
|
}
|
package/dist/config/index.js
CHANGED
|
@@ -3,6 +3,7 @@ dotenv.config();
|
|
|
3
3
|
// AI API keys
|
|
4
4
|
const ANTHROPIC_API_KEY = process.env.ANTHROPIC_API_KEY;
|
|
5
5
|
const OPENAI_API_KEY = process.env.OPENAI_API_KEY;
|
|
6
|
+
const AZURE_OPENAI_API_KEY = process.env.AZURE_OPENAI_API_KEY;
|
|
6
7
|
const COHERE_API_KEY = process.env.COHERE_API_KEY;
|
|
7
8
|
const DEEPSEEK_API_KEY = process.env.DEEPSEEK_API_KEY;
|
|
8
9
|
const GEMINI_API_KEY = process.env.GEMINI_API_KEY;
|
|
@@ -16,6 +17,7 @@ const PROVIDER_API_KEYS = {
|
|
|
16
17
|
COHERE_API_KEY,
|
|
17
18
|
GEMINI_API_KEY,
|
|
18
19
|
OPENAI_API_KEY,
|
|
20
|
+
AZURE_OPENAI_API_KEY,
|
|
19
21
|
ANTHROPIC_API_KEY,
|
|
20
22
|
DEEPSEEK_API_KEY,
|
|
21
23
|
GROQ_API_KEY,
|
|
@@ -1,9 +1,36 @@
|
|
|
1
1
|
import type { LabelKeys, MetricsSnapshot, TokenUsage } from './types.js';
|
|
2
|
+
/**
|
|
3
|
+
* Record a completed request.
|
|
4
|
+
* @param labels Crew/agent/provider/model identifiers
|
|
5
|
+
* @param streaming Whether this was a streaming request
|
|
6
|
+
* @param durationMs Duration in milliseconds
|
|
7
|
+
*/
|
|
2
8
|
export declare function recordRequest(labels: LabelKeys, streaming: boolean, durationMs: number): void;
|
|
9
|
+
/**
|
|
10
|
+
* Record an error occurrence for the given labels.
|
|
11
|
+
*/
|
|
3
12
|
export declare function recordError(labels: LabelKeys): void;
|
|
13
|
+
/**
|
|
14
|
+
* Record token usage for a request (prompt and completion).
|
|
15
|
+
*/
|
|
4
16
|
export declare function recordTokens(labels: LabelKeys, usage: TokenUsage): void;
|
|
17
|
+
/**
|
|
18
|
+
* Add estimated cost (USD) to the cumulative total for the labels.
|
|
19
|
+
*/
|
|
5
20
|
export declare function recordEstimatedCost(labels: LabelKeys, usd: number): void;
|
|
21
|
+
/**
|
|
22
|
+
* Record a function call invocation and add its latency to totals.
|
|
23
|
+
*/
|
|
6
24
|
export declare function recordFunctionCall(labels: LabelKeys, latencyMs: number): void;
|
|
25
|
+
/**
|
|
26
|
+
* Get a metrics snapshot for specific labels (crew + agent + optional provider/model).
|
|
27
|
+
*/
|
|
7
28
|
export declare function snapshot(labels: LabelKeys): MetricsSnapshot;
|
|
29
|
+
/**
|
|
30
|
+
* Reset metrics for specific labels, or clear all if no labels provided.
|
|
31
|
+
*/
|
|
8
32
|
export declare function reset(labels?: LabelKeys): void;
|
|
33
|
+
/**
|
|
34
|
+
* Aggregate a crew-wide metrics snapshot across all agents in the crew.
|
|
35
|
+
*/
|
|
9
36
|
export declare function snapshotCrew(crewId: string): MetricsSnapshot;
|
package/dist/metrics/registry.js
CHANGED
|
@@ -24,6 +24,12 @@ function getOrInit(labels) {
|
|
|
24
24
|
}
|
|
25
25
|
return c;
|
|
26
26
|
}
|
|
27
|
+
/**
|
|
28
|
+
* Record a completed request.
|
|
29
|
+
* @param labels Crew/agent/provider/model identifiers
|
|
30
|
+
* @param streaming Whether this was a streaming request
|
|
31
|
+
* @param durationMs Duration in milliseconds
|
|
32
|
+
*/
|
|
27
33
|
export function recordRequest(labels, streaming, durationMs) {
|
|
28
34
|
const c = getOrInit(labels);
|
|
29
35
|
c.requests += 1;
|
|
@@ -32,26 +38,41 @@ export function recordRequest(labels, streaming, durationMs) {
|
|
|
32
38
|
c.durationMsSum += durationMs;
|
|
33
39
|
c.durationCount += 1;
|
|
34
40
|
}
|
|
41
|
+
/**
|
|
42
|
+
* Record an error occurrence for the given labels.
|
|
43
|
+
*/
|
|
35
44
|
export function recordError(labels) {
|
|
36
45
|
const c = getOrInit(labels);
|
|
37
46
|
c.errors += 1;
|
|
38
47
|
}
|
|
48
|
+
/**
|
|
49
|
+
* Record token usage for a request (prompt and completion).
|
|
50
|
+
*/
|
|
39
51
|
export function recordTokens(labels, usage) {
|
|
40
52
|
const c = getOrInit(labels);
|
|
41
53
|
c.inputTokens += usage.promptTokens || 0;
|
|
42
54
|
c.outputTokens += usage.completionTokens || 0;
|
|
43
55
|
}
|
|
56
|
+
/**
|
|
57
|
+
* Add estimated cost (USD) to the cumulative total for the labels.
|
|
58
|
+
*/
|
|
44
59
|
export function recordEstimatedCost(labels, usd) {
|
|
45
60
|
const c = getOrInit(labels);
|
|
46
61
|
const current = new Big(c.estimatedCostUSD || 0);
|
|
47
62
|
const addition = new Big(usd || 0);
|
|
48
63
|
c.estimatedCostUSD = Number(current.plus(addition));
|
|
49
64
|
}
|
|
65
|
+
/**
|
|
66
|
+
* Record a function call invocation and add its latency to totals.
|
|
67
|
+
*/
|
|
50
68
|
export function recordFunctionCall(labels, latencyMs) {
|
|
51
69
|
const c = getOrInit(labels);
|
|
52
70
|
c.functionCalls += 1;
|
|
53
71
|
c.functionLatencyMs += latencyMs || 0;
|
|
54
72
|
}
|
|
73
|
+
/**
|
|
74
|
+
* Get a metrics snapshot for specific labels (crew + agent + optional provider/model).
|
|
75
|
+
*/
|
|
55
76
|
export function snapshot(labels) {
|
|
56
77
|
const c = getOrInit(labels);
|
|
57
78
|
const totalTokens = c.inputTokens + c.outputTokens;
|
|
@@ -78,6 +99,9 @@ export function snapshot(labels) {
|
|
|
78
99
|
},
|
|
79
100
|
};
|
|
80
101
|
}
|
|
102
|
+
/**
|
|
103
|
+
* Reset metrics for specific labels, or clear all if no labels provided.
|
|
104
|
+
*/
|
|
81
105
|
export function reset(labels) {
|
|
82
106
|
if (!labels) {
|
|
83
107
|
store.clear();
|
|
@@ -86,6 +110,9 @@ export function reset(labels) {
|
|
|
86
110
|
const k = keyOf(labels);
|
|
87
111
|
store.delete(k);
|
|
88
112
|
}
|
|
113
|
+
/**
|
|
114
|
+
* Aggregate a crew-wide metrics snapshot across all agents in the crew.
|
|
115
|
+
*/
|
|
89
116
|
export function snapshotCrew(crewId) {
|
|
90
117
|
const empty = {
|
|
91
118
|
requests: 0,
|
package/dist/types.d.ts
CHANGED
|
@@ -165,6 +165,11 @@ interface AgentConfig {
|
|
|
165
165
|
};
|
|
166
166
|
debug?: boolean;
|
|
167
167
|
apiURL?: string;
|
|
168
|
+
/**
|
|
169
|
+
* Provider-specific arguments that are forwarded to the underlying Ax factory.
|
|
170
|
+
* Example (azure-openai): { resourceName, deploymentName, version }
|
|
171
|
+
*/
|
|
172
|
+
providerArgs?: Record<string, unknown>;
|
|
168
173
|
options?: Partial<AxProgramForwardOptions<any>> & Record<string, any>;
|
|
169
174
|
functions?: string[];
|
|
170
175
|
agents?: string[];
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
import { AxCrew, AxCrewConfig } from "../dist/index.js";
|
|
2
|
+
|
|
3
|
+
const crewConfig: AxCrewConfig = {
|
|
4
|
+
crew: [
|
|
5
|
+
{
|
|
6
|
+
name: "TestAgent",
|
|
7
|
+
description: "Test Agent for testing provider arguments",
|
|
8
|
+
provider: "azure-openai",
|
|
9
|
+
providerKeyName: "AZURE_OPENAI_API_KEY",
|
|
10
|
+
signature: "userQuery:string -> answer:string",
|
|
11
|
+
ai: {
|
|
12
|
+
model: "gpt-5-mini",
|
|
13
|
+
temperature: 0,
|
|
14
|
+
stream: false
|
|
15
|
+
},
|
|
16
|
+
providerArgs: {
|
|
17
|
+
resourceName: "your-resource-name",
|
|
18
|
+
deploymentName: "your-deployment-name",
|
|
19
|
+
version: "2025-01-01-preview"
|
|
20
|
+
},
|
|
21
|
+
functions: [],
|
|
22
|
+
options: {
|
|
23
|
+
debug: true,
|
|
24
|
+
stream: false
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
]
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
const crew = new AxCrew(crewConfig);
|
|
31
|
+
await crew.addAllAgents();
|
|
32
|
+
|
|
33
|
+
const testAgent = crew.agents?.get("TestAgent");
|
|
34
|
+
|
|
35
|
+
const response = await testAgent?.forward({
|
|
36
|
+
userQuery: "What is the capital of France?"
|
|
37
|
+
});
|
|
38
|
+
|
|
39
|
+
console.log(response?.answer);
|
|
40
|
+
|
|
41
|
+
console.log(testAgent?.getAccumulatedCosts());
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@amitdeshmukh/ax-crew",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "5.0.0",
|
|
5
5
|
"description": "Build and launch a crew of AI agents with shared state. Built with axllm.dev",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
@@ -30,7 +30,7 @@
|
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@testing-library/jest-dom": "^6.6.3",
|
|
32
32
|
"@types/big.js": "^6.2.2",
|
|
33
|
-
"@types/node": "^20.
|
|
33
|
+
"@types/node": "^20.19.21",
|
|
34
34
|
"@types/uuid": "^10.0.0",
|
|
35
35
|
"@vitest/coverage-v8": "^3.0.9",
|
|
36
36
|
"@vitest/ui": "^3.0.9",
|
|
@@ -1,6 +1,13 @@
|
|
|
1
1
|
import fs from 'fs';
|
|
2
2
|
// Import Ax factory and MCP transports (as exported by current package)
|
|
3
3
|
import { ai, AxMCPClient, AxMCPHTTPSSETransport, AxMCPStreambleHTTPTransport, AxDefaultCostTracker } from '@ax-llm/ax'
|
|
4
|
+
import type {
|
|
5
|
+
AxAIAzureOpenAIArgs,
|
|
6
|
+
AxAIAnthropicArgs,
|
|
7
|
+
AxAIGoogleGeminiArgs,
|
|
8
|
+
AxAIOpenRouterArgs,
|
|
9
|
+
AxAIOllamaArgs
|
|
10
|
+
} from '@ax-llm/ax';
|
|
4
11
|
import type { AxFunction } from '@ax-llm/ax';
|
|
5
12
|
// STDIO transport from tools package
|
|
6
13
|
import { AxMCPStdioTransport } from '@ax-llm/ax-tools'
|
|
@@ -246,6 +253,36 @@ const parseAgentConfig = async (
|
|
|
246
253
|
throw new Error(`Invalid apiURL provided: ${agentConfigData.apiURL}`);
|
|
247
254
|
}
|
|
248
255
|
}
|
|
256
|
+
// Forward provider-specific arguments with type-safety for Azure OpenAI
|
|
257
|
+
const providerArgs = (agentConfigData as any).providerArgs;
|
|
258
|
+
if (provider === 'azure-openai') {
|
|
259
|
+
type AzureArgs = Pick<AxAIAzureOpenAIArgs<string>, 'resourceName' | 'deploymentName' | 'version'>;
|
|
260
|
+
const az: Partial<AzureArgs> = providerArgs ?? {};
|
|
261
|
+
// If users supplied apiURL instead of resourceName, accept it (Ax supports full URL as resourceName)
|
|
262
|
+
if (!az.resourceName && agentConfigData.apiURL) {
|
|
263
|
+
az.resourceName = agentConfigData.apiURL as any;
|
|
264
|
+
}
|
|
265
|
+
Object.assign(aiArgs, az);
|
|
266
|
+
} else if (provider === 'anthropic') {
|
|
267
|
+
type AnthropicArgs = Pick<AxAIAnthropicArgs<string>, 'projectId' | 'region'>;
|
|
268
|
+
const an: Partial<AnthropicArgs> = providerArgs ?? {};
|
|
269
|
+
Object.assign(aiArgs, an);
|
|
270
|
+
} else if (provider === 'google-gemini') {
|
|
271
|
+
type GeminiArgs = Pick<AxAIGoogleGeminiArgs<string>, 'projectId' | 'region' | 'endpointId'>;
|
|
272
|
+
const g: Partial<GeminiArgs> = providerArgs ?? {};
|
|
273
|
+
Object.assign(aiArgs, g);
|
|
274
|
+
} else if (provider === 'openrouter') {
|
|
275
|
+
type OpenRouterArgs = Pick<AxAIOpenRouterArgs<string>, 'referer' | 'title'>;
|
|
276
|
+
const o: Partial<OpenRouterArgs> = providerArgs ?? {};
|
|
277
|
+
Object.assign(aiArgs, o);
|
|
278
|
+
} else if (provider === 'ollama') {
|
|
279
|
+
type OllamaArgs = Pick<AxAIOllamaArgs<string>, 'url'>;
|
|
280
|
+
const ol: Partial<OllamaArgs> = providerArgs ?? {};
|
|
281
|
+
Object.assign(aiArgs, ol);
|
|
282
|
+
} else if (providerArgs && typeof providerArgs === 'object') {
|
|
283
|
+
// Generic pass-through for other providers if needed in the future
|
|
284
|
+
Object.assign(aiArgs, providerArgs);
|
|
285
|
+
}
|
|
249
286
|
const aiInstance = ai(aiArgs);
|
|
250
287
|
|
|
251
288
|
// If an mcpServers config is provided in the agent config, convert to functions
|
|
@@ -15,6 +15,10 @@ import type {
|
|
|
15
15
|
export class StateFulAxAgentUsage {
|
|
16
16
|
static STATE_KEY_PREFIX = 'agent_usage_';
|
|
17
17
|
|
|
18
|
+
/**
|
|
19
|
+
* Compute usage costs given a model usage record and model pricing info.
|
|
20
|
+
* Returns null if inputs are invalid. Token-based costs are computed with high precision.
|
|
21
|
+
*/
|
|
18
22
|
static calculateCost(modelUsage: ModelUsage, modelInfo: ModelInfo): UsageCost | null {
|
|
19
23
|
// Handle both direct properties and nested tokens structure
|
|
20
24
|
const promptTokens = (modelUsage as any).tokens?.promptTokens ?? modelUsage.promptTokens;
|
|
@@ -59,6 +63,10 @@ export class StateFulAxAgentUsage {
|
|
|
59
63
|
}
|
|
60
64
|
}
|
|
61
65
|
|
|
66
|
+
/**
|
|
67
|
+
* Persist or aggregate the cost for an agent in the shared crew state.
|
|
68
|
+
* No-op if cost is null.
|
|
69
|
+
*/
|
|
62
70
|
static trackCostInState(agentName: string, cost: UsageCost | null, state: StateInstance) {
|
|
63
71
|
// If cost is null, skip tracking
|
|
64
72
|
if (!cost) return;
|
|
@@ -90,6 +98,9 @@ export class StateFulAxAgentUsage {
|
|
|
90
98
|
}
|
|
91
99
|
}
|
|
92
100
|
|
|
101
|
+
/**
|
|
102
|
+
* Aggregate and return total costs across all agents from the shared crew state.
|
|
103
|
+
*/
|
|
93
104
|
static getAggregatedCosts(state: StateInstance): AggregatedCosts {
|
|
94
105
|
const allState = state.getAll();
|
|
95
106
|
const agentCosts: Record<string, UsageCost> = {};
|
|
@@ -128,6 +139,9 @@ export class StateFulAxAgentUsage {
|
|
|
128
139
|
};
|
|
129
140
|
}
|
|
130
141
|
|
|
142
|
+
/**
|
|
143
|
+
* Remove all stored per-agent costs from the shared crew state.
|
|
144
|
+
*/
|
|
131
145
|
static resetCosts(state: StateInstance) {
|
|
132
146
|
const allState = state.getAll();
|
|
133
147
|
Object.keys(allState).forEach(key => {
|
package/src/agents/index.ts
CHANGED
|
@@ -238,10 +238,20 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
238
238
|
getAccumulatedCosts(): UsageCost | null { return null; }
|
|
239
239
|
|
|
240
240
|
// Metrics API for this agent
|
|
241
|
+
/**
|
|
242
|
+
* Get the current metrics snapshot for this agent.
|
|
243
|
+
* Includes request counts, error rates, token usage, estimated USD cost, and function call stats.
|
|
244
|
+
*
|
|
245
|
+
* @returns A metrics snapshot scoped to this agent within its crew.
|
|
246
|
+
*/
|
|
241
247
|
getMetrics() {
|
|
242
248
|
const crewId = (this.state as any)?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
243
249
|
return MetricsRegistry.snapshot({ crewId, agent: this.agentName } as any);
|
|
244
250
|
}
|
|
251
|
+
/**
|
|
252
|
+
* Reset all tracked metrics for this agent (does not affect other agents).
|
|
253
|
+
* Call this to start fresh measurement windows for the agent.
|
|
254
|
+
*/
|
|
245
255
|
resetMetrics(): void {
|
|
246
256
|
const crewId = (this.state as any)?.crewId || (this.state.get?.('crewId')) || 'default';
|
|
247
257
|
MetricsRegistry.reset({ crewId, agent: this.agentName } as any);
|
|
@@ -499,7 +509,8 @@ class AxCrew {
|
|
|
499
509
|
|
|
500
510
|
|
|
501
511
|
/**
|
|
502
|
-
* Resets all cost tracking for the crew
|
|
512
|
+
* Resets all cost and usage tracking for the entire crew.
|
|
513
|
+
* Also calls each agent's `resetUsage` (if available) and clears crew-level metrics.
|
|
503
514
|
*/
|
|
504
515
|
resetCosts(): void {
|
|
505
516
|
// Reset AxAgent built-in usage and our metrics registry
|
|
@@ -513,9 +524,19 @@ class AxCrew {
|
|
|
513
524
|
}
|
|
514
525
|
|
|
515
526
|
// Metrics API
|
|
527
|
+
/**
|
|
528
|
+
* Get an aggregate metrics snapshot for the entire crew.
|
|
529
|
+
* Sums requests, errors, tokens, and estimated cost across all agents in the crew.
|
|
530
|
+
*
|
|
531
|
+
* @returns Crew-level metrics snapshot.
|
|
532
|
+
*/
|
|
516
533
|
getCrewMetrics() {
|
|
517
534
|
return MetricsRegistry.snapshotCrew(this.crewId);
|
|
518
535
|
}
|
|
536
|
+
/**
|
|
537
|
+
* Reset all tracked metrics for the entire crew.
|
|
538
|
+
* Use to clear totals before a new measurement period.
|
|
539
|
+
*/
|
|
519
540
|
resetCrewMetrics(): void {
|
|
520
541
|
MetricsRegistry.reset({ crewId: this.crewId });
|
|
521
542
|
}
|
package/src/config/index.ts
CHANGED
|
@@ -4,6 +4,7 @@ dotenv.config();
|
|
|
4
4
|
// AI API keys
|
|
5
5
|
const ANTHROPIC_API_KEY: string | undefined = process.env.ANTHROPIC_API_KEY;
|
|
6
6
|
const OPENAI_API_KEY: string | undefined = process.env.OPENAI_API_KEY;
|
|
7
|
+
const AZURE_OPENAI_API_KEY: string | undefined = process.env.AZURE_OPENAI_API_KEY;
|
|
7
8
|
const COHERE_API_KEY: string | undefined = process.env.COHERE_API_KEY;
|
|
8
9
|
const DEEPSEEK_API_KEY: string | undefined = process.env.DEEPSEEK_API_KEY;
|
|
9
10
|
const GEMINI_API_KEY: string | undefined = process.env.GEMINI_API_KEY;
|
|
@@ -23,6 +24,7 @@ const PROVIDER_API_KEYS: ProviderApiKeys = {
|
|
|
23
24
|
COHERE_API_KEY,
|
|
24
25
|
GEMINI_API_KEY,
|
|
25
26
|
OPENAI_API_KEY,
|
|
27
|
+
AZURE_OPENAI_API_KEY,
|
|
26
28
|
ANTHROPIC_API_KEY,
|
|
27
29
|
DEEPSEEK_API_KEY,
|
|
28
30
|
GROQ_API_KEY,
|
package/src/metrics/registry.ts
CHANGED
|
@@ -44,6 +44,12 @@ function getOrInit(labels: LabelKeys): Counters {
|
|
|
44
44
|
return c;
|
|
45
45
|
}
|
|
46
46
|
|
|
47
|
+
/**
|
|
48
|
+
* Record a completed request.
|
|
49
|
+
* @param labels Crew/agent/provider/model identifiers
|
|
50
|
+
* @param streaming Whether this was a streaming request
|
|
51
|
+
* @param durationMs Duration in milliseconds
|
|
52
|
+
*/
|
|
47
53
|
export function recordRequest(labels: LabelKeys, streaming: boolean, durationMs: number) {
|
|
48
54
|
const c = getOrInit(labels);
|
|
49
55
|
c.requests += 1;
|
|
@@ -52,17 +58,26 @@ export function recordRequest(labels: LabelKeys, streaming: boolean, durationMs:
|
|
|
52
58
|
c.durationCount += 1;
|
|
53
59
|
}
|
|
54
60
|
|
|
61
|
+
/**
|
|
62
|
+
* Record an error occurrence for the given labels.
|
|
63
|
+
*/
|
|
55
64
|
export function recordError(labels: LabelKeys) {
|
|
56
65
|
const c = getOrInit(labels);
|
|
57
66
|
c.errors += 1;
|
|
58
67
|
}
|
|
59
68
|
|
|
69
|
+
/**
|
|
70
|
+
* Record token usage for a request (prompt and completion).
|
|
71
|
+
*/
|
|
60
72
|
export function recordTokens(labels: LabelKeys, usage: TokenUsage) {
|
|
61
73
|
const c = getOrInit(labels);
|
|
62
74
|
c.inputTokens += usage.promptTokens || 0;
|
|
63
75
|
c.outputTokens += usage.completionTokens || 0;
|
|
64
76
|
}
|
|
65
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Add estimated cost (USD) to the cumulative total for the labels.
|
|
80
|
+
*/
|
|
66
81
|
export function recordEstimatedCost(labels: LabelKeys, usd: number) {
|
|
67
82
|
const c = getOrInit(labels);
|
|
68
83
|
const current = new Big(c.estimatedCostUSD || 0);
|
|
@@ -70,12 +85,18 @@ export function recordEstimatedCost(labels: LabelKeys, usd: number) {
|
|
|
70
85
|
c.estimatedCostUSD = Number(current.plus(addition));
|
|
71
86
|
}
|
|
72
87
|
|
|
88
|
+
/**
|
|
89
|
+
* Record a function call invocation and add its latency to totals.
|
|
90
|
+
*/
|
|
73
91
|
export function recordFunctionCall(labels: LabelKeys, latencyMs: number) {
|
|
74
92
|
const c = getOrInit(labels);
|
|
75
93
|
c.functionCalls += 1;
|
|
76
94
|
c.functionLatencyMs += latencyMs || 0;
|
|
77
95
|
}
|
|
78
96
|
|
|
97
|
+
/**
|
|
98
|
+
* Get a metrics snapshot for specific labels (crew + agent + optional provider/model).
|
|
99
|
+
*/
|
|
79
100
|
export function snapshot(labels: LabelKeys): MetricsSnapshot {
|
|
80
101
|
const c = getOrInit(labels);
|
|
81
102
|
const totalTokens = c.inputTokens + c.outputTokens;
|
|
@@ -103,6 +124,9 @@ export function snapshot(labels: LabelKeys): MetricsSnapshot {
|
|
|
103
124
|
};
|
|
104
125
|
}
|
|
105
126
|
|
|
127
|
+
/**
|
|
128
|
+
* Reset metrics for specific labels, or clear all if no labels provided.
|
|
129
|
+
*/
|
|
106
130
|
export function reset(labels?: LabelKeys) {
|
|
107
131
|
if (!labels) {
|
|
108
132
|
store.clear();
|
|
@@ -112,6 +136,9 @@ export function reset(labels?: LabelKeys) {
|
|
|
112
136
|
store.delete(k);
|
|
113
137
|
}
|
|
114
138
|
|
|
139
|
+
/**
|
|
140
|
+
* Aggregate a crew-wide metrics snapshot across all agents in the crew.
|
|
141
|
+
*/
|
|
115
142
|
export function snapshotCrew(crewId: string): MetricsSnapshot {
|
|
116
143
|
const empty: Counters = {
|
|
117
144
|
requests: 0,
|
package/src/types.ts
CHANGED
|
@@ -196,6 +196,11 @@ interface AgentConfig {
|
|
|
196
196
|
ai: AxModelConfig & { model: string };
|
|
197
197
|
debug?: boolean;
|
|
198
198
|
apiURL?: string;
|
|
199
|
+
/**
|
|
200
|
+
* Provider-specific arguments that are forwarded to the underlying Ax factory.
|
|
201
|
+
* Example (azure-openai): { resourceName, deploymentName, version }
|
|
202
|
+
*/
|
|
203
|
+
providerArgs?: Record<string, unknown>;
|
|
199
204
|
options?: Partial<AxProgramForwardOptions<any>> & Record<string, any>;
|
|
200
205
|
functions?: string[];
|
|
201
206
|
agents?: string[];
|
package/tsconfig.json
CHANGED
|
@@ -13,7 +13,8 @@
|
|
|
13
13
|
"rootDir": "./src",
|
|
14
14
|
"allowJs": true,
|
|
15
15
|
"resolveJsonModule": true,
|
|
16
|
-
"typeRoots": ["./node_modules/@types", "./types"]
|
|
16
|
+
"typeRoots": ["./node_modules/@types", "./types"],
|
|
17
|
+
"types": ["node"]
|
|
17
18
|
},
|
|
18
19
|
"include": ["src/**/*"],
|
|
19
20
|
"exclude": ["node_modules", "**/*.spec.ts"]
|