@amitdeshmukh/ax-crew 8.6.0 → 8.7.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 +3 -1
- package/CHANGELOG.md +8 -0
- package/LICENSE +21 -0
- package/README.md +7 -7
- package/package.json +3 -1
- package/scripts/install-skills.mjs +59 -0
- package/scripts/uninstall-skills.mjs +25 -0
- package/src/skills/ax-crew-ace.md +165 -0
- package/src/skills/ax-crew-agent-config.md +181 -0
- package/src/skills/ax-crew-code-execution.md +166 -0
- package/src/skills/ax-crew-execution-modes.md +287 -0
- package/src/skills/ax-crew-few-shot.md +165 -0
- package/src/skills/ax-crew-functions.md +218 -0
- package/src/skills/ax-crew-mcp.md +221 -0
- package/src/skills/ax-crew-metrics.md +170 -0
- package/src/skills/ax-crew-patterns.md +286 -0
- package/src/skills/ax-crew-providers.md +204 -0
- package/src/skills/ax-crew-signatures.md +169 -0
- package/src/skills/ax-crew-state.md +168 -0
- package/src/skills/ax-crew-streaming.md +143 -0
- package/src/skills/ax-crew-sub-agents.md +203 -0
- package/src/skills/ax-crew-telemetry.md +161 -0
- package/src/skills/ax-crew.md +124 -0
|
@@ -0,0 +1,218 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ax-crew-functions
|
|
3
|
+
version: __VERSION__
|
|
4
|
+
description: "Functions and tools: FunctionRegistryType, AxFunction, toFunction, custom functions, AxCrewFunctions, class-based functions with state"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# Functions
|
|
8
|
+
|
|
9
|
+
Agents call tools via a `FunctionRegistryType` -- a map of function names to either plain `AxFunction` objects or class-based constructors that receive shared state.
|
|
10
|
+
|
|
11
|
+
## Two Patterns
|
|
12
|
+
|
|
13
|
+
### Object-based (plain AxFunction)
|
|
14
|
+
|
|
15
|
+
```ts
|
|
16
|
+
import type { AxFunction } from '@ax-llm/ax';
|
|
17
|
+
|
|
18
|
+
const MyTool: AxFunction = {
|
|
19
|
+
name: 'MyTool',
|
|
20
|
+
description: 'Does something useful',
|
|
21
|
+
parameters: {
|
|
22
|
+
type: 'object',
|
|
23
|
+
properties: {
|
|
24
|
+
input: { type: 'string', description: 'The input value' }
|
|
25
|
+
},
|
|
26
|
+
required: ['input']
|
|
27
|
+
},
|
|
28
|
+
func: ({ input }: { input: string }) => {
|
|
29
|
+
return `Processed: ${input}`;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
```
|
|
33
|
+
|
|
34
|
+
### Class-based (with state access)
|
|
35
|
+
|
|
36
|
+
Constructor receives shared state. Must implement `toFunction()` returning an `AxFunction`.
|
|
37
|
+
|
|
38
|
+
```ts
|
|
39
|
+
import type { AxFunction } from '@ax-llm/ax';
|
|
40
|
+
|
|
41
|
+
class WordPressPost {
|
|
42
|
+
private state: Record<string, any>;
|
|
43
|
+
|
|
44
|
+
constructor(state: Record<string, any>) {
|
|
45
|
+
this.state = state;
|
|
46
|
+
}
|
|
47
|
+
|
|
48
|
+
toFunction(): AxFunction {
|
|
49
|
+
return {
|
|
50
|
+
name: 'WordPressPost',
|
|
51
|
+
description: 'Creates a post on WordPress',
|
|
52
|
+
parameters: {
|
|
53
|
+
type: 'object',
|
|
54
|
+
properties: {
|
|
55
|
+
title: { type: 'string', description: 'Post title' },
|
|
56
|
+
content: { type: 'string', description: 'Post content' },
|
|
57
|
+
status: { type: 'string', description: 'Post status (draft, publish, private)' }
|
|
58
|
+
},
|
|
59
|
+
required: ['title', 'content', 'status']
|
|
60
|
+
},
|
|
61
|
+
func: async ({ title, content, status }: { title: string; content: string; status: string }) => {
|
|
62
|
+
const env = this.state.env || {};
|
|
63
|
+
const url = env.WORDPRESS_URL;
|
|
64
|
+
const username = env.WORDPRESS_USERNAME;
|
|
65
|
+
const password = env.WORDPRESS_PASSWORD;
|
|
66
|
+
// ... make API call using credentials from state
|
|
67
|
+
return { id: 123, link: `${url}/?p=123` };
|
|
68
|
+
}
|
|
69
|
+
};
|
|
70
|
+
}
|
|
71
|
+
}
|
|
72
|
+
```
|
|
73
|
+
|
|
74
|
+
## FunctionRegistryType
|
|
75
|
+
|
|
76
|
+
```ts
|
|
77
|
+
type FunctionRegistryType = {
|
|
78
|
+
[key: string]: AxFunction | { new(state: Record<string, any>): { toFunction: () => AxFunction } };
|
|
79
|
+
};
|
|
80
|
+
```
|
|
81
|
+
|
|
82
|
+
The registry key must match the name used in `AgentConfig.functions[]`.
|
|
83
|
+
|
|
84
|
+
## Built-in AxCrewFunctions
|
|
85
|
+
|
|
86
|
+
```ts
|
|
87
|
+
import { AxCrewFunctions } from '@amitdeshmukh/ax-crew';
|
|
88
|
+
// Contains: { CurrentDateTime, DaysBetweenDates }
|
|
89
|
+
```
|
|
90
|
+
|
|
91
|
+
**CurrentDateTime** -- returns current date/time in `iso`, `datetime`, or `date` format.
|
|
92
|
+
|
|
93
|
+
**DaysBetweenDates** -- calculates days between two ISO date strings. Parameters: `startDate`, `endDate`.
|
|
94
|
+
|
|
95
|
+
## Merging Registries
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
import { AxCrew, AxCrewFunctions } from '@amitdeshmukh/ax-crew';
|
|
99
|
+
import type { FunctionRegistryType } from '@amitdeshmukh/ax-crew';
|
|
100
|
+
|
|
101
|
+
const myFunctions: FunctionRegistryType = {
|
|
102
|
+
MyTool: MyTool,
|
|
103
|
+
WordPressPost: WordPressPost, // class-based
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
// Merge built-in + custom
|
|
107
|
+
const crew = new AxCrew(config, { ...AxCrewFunctions, ...myFunctions });
|
|
108
|
+
```
|
|
109
|
+
|
|
110
|
+
Then reference by name in agent config:
|
|
111
|
+
|
|
112
|
+
```ts
|
|
113
|
+
{
|
|
114
|
+
name: "poster",
|
|
115
|
+
functions: ["CurrentDateTime", "WordPressPost"],
|
|
116
|
+
// ...
|
|
117
|
+
}
|
|
118
|
+
```
|
|
119
|
+
|
|
120
|
+
## Canonical Pattern
|
|
121
|
+
|
|
122
|
+
Full runnable example adapted from the WordPress example:
|
|
123
|
+
|
|
124
|
+
```ts
|
|
125
|
+
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
126
|
+
import type { AxCrewConfig, FunctionRegistryType } from '@amitdeshmukh/ax-crew';
|
|
127
|
+
import type { AxFunction } from '@ax-llm/ax';
|
|
128
|
+
import dotenv from 'dotenv';
|
|
129
|
+
dotenv.config();
|
|
130
|
+
|
|
131
|
+
// Plain AxFunction
|
|
132
|
+
const Summarize: AxFunction = {
|
|
133
|
+
name: 'Summarize',
|
|
134
|
+
description: 'Summarize text to a given length',
|
|
135
|
+
parameters: {
|
|
136
|
+
type: 'object',
|
|
137
|
+
properties: {
|
|
138
|
+
text: { type: 'string', description: 'Text to summarize' },
|
|
139
|
+
maxWords: { type: 'number', description: 'Maximum words' }
|
|
140
|
+
},
|
|
141
|
+
required: ['text']
|
|
142
|
+
},
|
|
143
|
+
func: ({ text, maxWords }: { text: string; maxWords?: number }) => {
|
|
144
|
+
const limit = maxWords ?? 50;
|
|
145
|
+
return text.split(' ').slice(0, limit).join(' ') + '...';
|
|
146
|
+
}
|
|
147
|
+
};
|
|
148
|
+
|
|
149
|
+
// Class-based function with state access
|
|
150
|
+
class FetchFromAPI {
|
|
151
|
+
private state: Record<string, any>;
|
|
152
|
+
constructor(state: Record<string, any>) { this.state = state; }
|
|
153
|
+
toFunction(): AxFunction {
|
|
154
|
+
return {
|
|
155
|
+
name: 'FetchFromAPI',
|
|
156
|
+
description: 'Fetch data from a configured API endpoint',
|
|
157
|
+
parameters: {
|
|
158
|
+
type: 'object',
|
|
159
|
+
properties: {
|
|
160
|
+
endpoint: { type: 'string', description: 'API endpoint path' }
|
|
161
|
+
},
|
|
162
|
+
required: ['endpoint']
|
|
163
|
+
},
|
|
164
|
+
func: async ({ endpoint }: { endpoint: string }) => {
|
|
165
|
+
const baseUrl = this.state.env?.API_BASE_URL || 'https://api.example.com';
|
|
166
|
+
const resp = await fetch(`${baseUrl}${endpoint}`);
|
|
167
|
+
return await resp.json();
|
|
168
|
+
}
|
|
169
|
+
};
|
|
170
|
+
}
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
const config: AxCrewConfig = {
|
|
174
|
+
crew: [
|
|
175
|
+
{
|
|
176
|
+
name: "assistant",
|
|
177
|
+
description: "An assistant that can summarize text and fetch data",
|
|
178
|
+
signature: "request:string -> response:string",
|
|
179
|
+
provider: "openai",
|
|
180
|
+
providerKeyName: "OPENAI_API_KEY",
|
|
181
|
+
ai: { model: "gpt-4o-mini", temperature: 0.5 },
|
|
182
|
+
functions: ["Summarize", "FetchFromAPI"],
|
|
183
|
+
}
|
|
184
|
+
]
|
|
185
|
+
};
|
|
186
|
+
|
|
187
|
+
async function main() {
|
|
188
|
+
const customFunctions: FunctionRegistryType = {
|
|
189
|
+
Summarize,
|
|
190
|
+
FetchFromAPI,
|
|
191
|
+
};
|
|
192
|
+
const crew = new AxCrew(config, customFunctions);
|
|
193
|
+
|
|
194
|
+
// Set state for class-based functions
|
|
195
|
+
crew.state.set("env", { API_BASE_URL: "https://api.example.com" });
|
|
196
|
+
|
|
197
|
+
await crew.addAllAgents();
|
|
198
|
+
const assistant = crew.agents?.get("assistant");
|
|
199
|
+
const result = await assistant?.forward({ request: "Summarize the latest news" });
|
|
200
|
+
console.log(result?.response);
|
|
201
|
+
crew.destroy();
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
main().catch(console.error);
|
|
205
|
+
```
|
|
206
|
+
|
|
207
|
+
## Do Not Generate
|
|
208
|
+
|
|
209
|
+
- Do NOT define functions inline in AgentConfig; always use a `FunctionRegistryType` registry passed to the `AxCrew` constructor.
|
|
210
|
+
- Do NOT forget that class-based function constructors receive `state: Record<string, any>`, not `StateInstance`. Access values directly (e.g. `this.state.env`), since the state object is a plain record populated via `crew.state.set()`.
|
|
211
|
+
- Do NOT use a registry key that differs from the function name used in `AgentConfig.functions[]` -- they must match.
|
|
212
|
+
- Do NOT import `AxCrewFunctions` from `@ax-llm/ax`; import from `@amitdeshmukh/ax-crew`.
|
|
213
|
+
|
|
214
|
+
## References
|
|
215
|
+
|
|
216
|
+
- [write-post-and-publish-to-wordpress.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/write-post-and-publish-to-wordpress.ts)
|
|
217
|
+
- [src/functions/dateTime.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/functions/dateTime.ts)
|
|
218
|
+
- [src/functions/index.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/functions/index.ts)
|
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ax-crew-mcp
|
|
3
|
+
version: "__VERSION__"
|
|
4
|
+
description: "ax-crew MCP integration: MCP, Model Context Protocol, STDIO, HTTP SSE, Streamable HTTP, mcpServers, tools, tool filtering, multiple servers"
|
|
5
|
+
argument-hint: [topic]
|
|
6
|
+
allowed-tools: Read, Grep, Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ax-crew MCP (Model Context Protocol)
|
|
10
|
+
|
|
11
|
+
MCP servers expose external tools to agents. Configured per-agent via `mcpServers` in the agent config. Transport type is auto-detected by config shape.
|
|
12
|
+
|
|
13
|
+
## Three Transport Types
|
|
14
|
+
|
|
15
|
+
### STDIO (local process)
|
|
16
|
+
|
|
17
|
+
```typescript
|
|
18
|
+
mcpServers: {
|
|
19
|
+
"context7": {
|
|
20
|
+
command: "npx", // required
|
|
21
|
+
args: ["-y", "@upstash/context7-mcp"], // optional
|
|
22
|
+
env: { API_KEY: "..." }, // optional
|
|
23
|
+
tools: ["resolve-library-id", "query-docs"], // optional allowlist
|
|
24
|
+
}
|
|
25
|
+
}
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
Type: `MCPStdioTransportConfig = { command: string; args?: string[]; env?: NodeJS.ProcessEnv; tools?: string[] }`
|
|
29
|
+
|
|
30
|
+
### HTTP SSE (remote, server-sent events)
|
|
31
|
+
|
|
32
|
+
```typescript
|
|
33
|
+
mcpServers: {
|
|
34
|
+
"api-server": {
|
|
35
|
+
sseUrl: "https://api.example.com/mcp/sse", // required
|
|
36
|
+
tools: ["search", "fetch"], // optional allowlist
|
|
37
|
+
}
|
|
38
|
+
}
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
Type: `MCPHTTPSSETransportConfig = { sseUrl: string; tools?: string[] }`
|
|
42
|
+
|
|
43
|
+
### Streamable HTTP (bidirectional)
|
|
44
|
+
|
|
45
|
+
```typescript
|
|
46
|
+
mcpServers: {
|
|
47
|
+
"graphjin": {
|
|
48
|
+
mcpEndpoint: "http://localhost:8080/api/v1/mcp", // required
|
|
49
|
+
options: { timeout: 30000 }, // optional AxMCPStreamableHTTPTransportOptions
|
|
50
|
+
tools: ["list_workflows", "execute_workflow"], // optional allowlist
|
|
51
|
+
}
|
|
52
|
+
}
|
|
53
|
+
```
|
|
54
|
+
|
|
55
|
+
Type: `MCPStreamableHTTPTransportConfig = { mcpEndpoint: string; options?: AxMCPStreamableHTTPTransportOptions; tools?: string[] }`
|
|
56
|
+
|
|
57
|
+
## tools[] Allowlist
|
|
58
|
+
|
|
59
|
+
When `tools` is specified, only those MCP tool names are exposed to the agent. This reduces token usage and prevents the agent from calling unwanted tools.
|
|
60
|
+
|
|
61
|
+
```typescript
|
|
62
|
+
mcpServers: {
|
|
63
|
+
"big-server": {
|
|
64
|
+
command: "npx",
|
|
65
|
+
args: ["-y", "some-mcp-server"],
|
|
66
|
+
tools: ["only_this_tool", "and_this_one"], // filter from all available
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
```
|
|
70
|
+
|
|
71
|
+
If `tools` is omitted or empty, all tools from the MCP server are exposed.
|
|
72
|
+
|
|
73
|
+
## Multiple MCP Servers Per Agent
|
|
74
|
+
|
|
75
|
+
An agent can connect to multiple MCP servers simultaneously. All tools are merged:
|
|
76
|
+
|
|
77
|
+
```typescript
|
|
78
|
+
{
|
|
79
|
+
name: "MultiToolAgent",
|
|
80
|
+
description: "Agent with multiple MCP servers",
|
|
81
|
+
signature: 'query:string -> answer:string',
|
|
82
|
+
provider: "google-gemini",
|
|
83
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
84
|
+
ai: { model: "gemini-2.5-pro", temperature: 0 },
|
|
85
|
+
mcpServers: {
|
|
86
|
+
"filesystem": {
|
|
87
|
+
command: "npx",
|
|
88
|
+
args: ["-y", "@modelcontextprotocol/server-filesystem", "/tmp"],
|
|
89
|
+
},
|
|
90
|
+
"database": {
|
|
91
|
+
mcpEndpoint: "http://localhost:8080/api/v1/mcp",
|
|
92
|
+
tools: ["query", "describe_table"],
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
}
|
|
96
|
+
```
|
|
97
|
+
|
|
98
|
+
## Canonical Pattern
|
|
99
|
+
|
|
100
|
+
### STDIO transport (from mcp-agent.ts)
|
|
101
|
+
|
|
102
|
+
```typescript
|
|
103
|
+
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
104
|
+
import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
|
|
105
|
+
|
|
106
|
+
const config: AxCrewConfig = {
|
|
107
|
+
crew: [
|
|
108
|
+
{
|
|
109
|
+
name: "Context7DocsAgent",
|
|
110
|
+
description: "Agent with access to Context7 Docs APIs",
|
|
111
|
+
signature: 'apiDocQuery:string "a question" -> apiDocAnswer:string "the answer"',
|
|
112
|
+
provider: "google-gemini",
|
|
113
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
114
|
+
ai: { model: "gemini-2.5-pro", temperature: 0 },
|
|
115
|
+
options: { debug: true },
|
|
116
|
+
mcpServers: {
|
|
117
|
+
"context7": {
|
|
118
|
+
command: "npx",
|
|
119
|
+
args: ["-y", "@upstash/context7-mcp", "--api-key", process.env.CONTEXT7_API_KEY!],
|
|
120
|
+
},
|
|
121
|
+
},
|
|
122
|
+
},
|
|
123
|
+
{
|
|
124
|
+
name: "ManagerAgent",
|
|
125
|
+
description: "Orchestrates sub-agents",
|
|
126
|
+
signature: 'question:string -> answer:string',
|
|
127
|
+
provider: "google-gemini",
|
|
128
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
129
|
+
ai: { model: "gemini-2.5-pro", maxTokens: 1000, temperature: 0 },
|
|
130
|
+
agents: ["Context7DocsAgent"],
|
|
131
|
+
},
|
|
132
|
+
],
|
|
133
|
+
};
|
|
134
|
+
|
|
135
|
+
async function main() {
|
|
136
|
+
const crew = new AxCrew(config);
|
|
137
|
+
try {
|
|
138
|
+
await crew.addAllAgents();
|
|
139
|
+
|
|
140
|
+
const manager = crew.agents?.get("ManagerAgent");
|
|
141
|
+
if (!manager) throw new Error("Agent not found");
|
|
142
|
+
|
|
143
|
+
const result = await manager.forward({
|
|
144
|
+
question: "How do I configure MCP servers in ax-crew?",
|
|
145
|
+
});
|
|
146
|
+
console.log("Answer:", result.answer);
|
|
147
|
+
} finally {
|
|
148
|
+
crew.destroy();
|
|
149
|
+
}
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
main().catch(console.error);
|
|
153
|
+
```
|
|
154
|
+
|
|
155
|
+
### Streamable HTTP transport (from graphjin-database-agent.ts)
|
|
156
|
+
|
|
157
|
+
```typescript
|
|
158
|
+
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
159
|
+
import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
|
|
160
|
+
|
|
161
|
+
const config: AxCrewConfig = {
|
|
162
|
+
crew: [
|
|
163
|
+
{
|
|
164
|
+
name: "DatabaseAgent",
|
|
165
|
+
description: "Agent with direct database access via GraphJin",
|
|
166
|
+
signature: 'dbQuery:string "a database question" -> dbResult:string "the result"',
|
|
167
|
+
provider: "google-gemini",
|
|
168
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
169
|
+
ai: { model: "gemini-2.5-pro", temperature: 0 },
|
|
170
|
+
mcpServers: {
|
|
171
|
+
"graphjin": {
|
|
172
|
+
command: "graphjin",
|
|
173
|
+
args: ["mcp", "--server", "http://localhost:8080"],
|
|
174
|
+
},
|
|
175
|
+
},
|
|
176
|
+
},
|
|
177
|
+
{
|
|
178
|
+
name: "ManagerAgent",
|
|
179
|
+
description: "Orchestrates database queries",
|
|
180
|
+
signature: 'question:string -> answer:string',
|
|
181
|
+
provider: "google-gemini",
|
|
182
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
183
|
+
ai: { model: "gemini-2.5-pro", maxTokens: 2000, temperature: 0 },
|
|
184
|
+
agents: ["DatabaseAgent"],
|
|
185
|
+
},
|
|
186
|
+
],
|
|
187
|
+
};
|
|
188
|
+
|
|
189
|
+
async function main() {
|
|
190
|
+
const crew = new AxCrew(config);
|
|
191
|
+
try {
|
|
192
|
+
await crew.addAllAgents();
|
|
193
|
+
const manager = crew.agents?.get("ManagerAgent");
|
|
194
|
+
if (!manager) throw new Error("Agent not found");
|
|
195
|
+
|
|
196
|
+
const result = await manager.forward({
|
|
197
|
+
question: "What tables are available in the database?",
|
|
198
|
+
});
|
|
199
|
+
console.log("Answer:", result.answer);
|
|
200
|
+
} finally {
|
|
201
|
+
crew.destroy();
|
|
202
|
+
}
|
|
203
|
+
}
|
|
204
|
+
|
|
205
|
+
main().catch(console.error);
|
|
206
|
+
```
|
|
207
|
+
|
|
208
|
+
## Do Not Generate
|
|
209
|
+
|
|
210
|
+
- Do NOT mix transport config keys -- use exactly one of `command`, `sseUrl`, or `mcpEndpoint` per server entry.
|
|
211
|
+
- Do NOT forget `crew.destroy()` -- MCP STDIO processes must be cleaned up.
|
|
212
|
+
- Do NOT put `mcpServers` at the crew level -- it is per-agent only.
|
|
213
|
+
- Do NOT assume MCP tools have `parameters` -- some zero-arg tools omit it; ax-crew normalizes this automatically.
|
|
214
|
+
- Do NOT use `AxMCPStdioTransport` directly -- ax-crew handles transport creation internally from config.
|
|
215
|
+
|
|
216
|
+
## References
|
|
217
|
+
|
|
218
|
+
- [mcp-agent.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/mcp-agent.ts)
|
|
219
|
+
- [graphjin-database-agent.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/graphjin-database-agent.ts)
|
|
220
|
+
- [Source: agentConfig.ts (initializeMCPServers)](https://github.com/amitdeshmukh/ax-crew/blob/main/src/agents/agentConfig.ts)
|
|
221
|
+
- [Types: MCPStdioTransportConfig, MCPHTTPSSETransportConfig, MCPStreamableHTTPTransportConfig](https://github.com/amitdeshmukh/ax-crew/blob/main/src/types.ts)
|
|
@@ -0,0 +1,170 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ax-crew-metrics
|
|
3
|
+
version: "__VERSION__"
|
|
4
|
+
description: "ax-crew metrics and cost tracking: metrics, cost, tracking, getMetrics, getCrewMetrics, MetricsSnapshot, usage, tokens, estimatedCostUSD, resetCosts, resetCrewMetrics"
|
|
5
|
+
argument-hint: [topic]
|
|
6
|
+
allowed-tools: Read, Grep, Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ax-crew Metrics & Cost Tracking
|
|
10
|
+
|
|
11
|
+
## Per-Agent: agent.getMetrics()
|
|
12
|
+
|
|
13
|
+
Returns a `MetricsSnapshot` scoped to this agent within its crew.
|
|
14
|
+
|
|
15
|
+
```typescript
|
|
16
|
+
const agent = crew.agents?.get("MyAgent");
|
|
17
|
+
const metrics = agent.getMetrics();
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## Crew-Level: crew.getCrewMetrics()
|
|
21
|
+
|
|
22
|
+
Returns a `MetricsSnapshot` aggregated across all agents in the crew.
|
|
23
|
+
|
|
24
|
+
```typescript
|
|
25
|
+
const crewMetrics = crew.getCrewMetrics();
|
|
26
|
+
```
|
|
27
|
+
|
|
28
|
+
## MetricsSnapshot Shape
|
|
29
|
+
|
|
30
|
+
```typescript
|
|
31
|
+
interface MetricsSnapshot {
|
|
32
|
+
provider?: string;
|
|
33
|
+
model?: string;
|
|
34
|
+
requests: {
|
|
35
|
+
totalRequests: number;
|
|
36
|
+
totalErrors: number;
|
|
37
|
+
errorRate: number; // errors / requests
|
|
38
|
+
totalStreamingRequests: number;
|
|
39
|
+
durationMsSum: number;
|
|
40
|
+
durationCount: number;
|
|
41
|
+
};
|
|
42
|
+
tokens: {
|
|
43
|
+
promptTokens: number;
|
|
44
|
+
completionTokens: number;
|
|
45
|
+
totalTokens?: number;
|
|
46
|
+
};
|
|
47
|
+
estimatedCostUSD: number; // rounded to 5 decimal places
|
|
48
|
+
functions: {
|
|
49
|
+
totalFunctionCalls: number;
|
|
50
|
+
totalFunctionLatencyMs: number;
|
|
51
|
+
details?: Array<{ // per-function breakdown
|
|
52
|
+
name: string;
|
|
53
|
+
calls: number;
|
|
54
|
+
totalLatencyMs: number;
|
|
55
|
+
}>;
|
|
56
|
+
};
|
|
57
|
+
}
|
|
58
|
+
```
|
|
59
|
+
|
|
60
|
+
## Reset Methods
|
|
61
|
+
|
|
62
|
+
```typescript
|
|
63
|
+
// Reset all cost/usage tracking for the entire crew (resets agent usage + metrics)
|
|
64
|
+
crew.resetCosts();
|
|
65
|
+
|
|
66
|
+
// Reset only the metrics registry for the crew
|
|
67
|
+
crew.resetCrewMetrics();
|
|
68
|
+
```
|
|
69
|
+
|
|
70
|
+
`resetCosts()` calls `resetUsage()` and `resetMetrics()` on each agent, then clears crew-level metrics.
|
|
71
|
+
`resetCrewMetrics()` only clears the MetricsRegistry entries for this crew.
|
|
72
|
+
|
|
73
|
+
## Canonical Pattern
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { AxCrew, AxCrewFunctions } from '@amitdeshmukh/ax-crew';
|
|
77
|
+
import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
|
|
78
|
+
|
|
79
|
+
const config: AxCrewConfig = {
|
|
80
|
+
crew: [
|
|
81
|
+
{
|
|
82
|
+
name: "researcher",
|
|
83
|
+
description: "A research agent that finds information",
|
|
84
|
+
signature: "query:string -> research:string",
|
|
85
|
+
provider: "google-gemini",
|
|
86
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
87
|
+
ai: { model: "gemini-2.5-flash-lite", maxTokens: 4000, stream: true },
|
|
88
|
+
functions: ["CurrentDateTime"],
|
|
89
|
+
},
|
|
90
|
+
{
|
|
91
|
+
name: "writer",
|
|
92
|
+
description: "A writing agent that creates content",
|
|
93
|
+
signature: "topic:string -> article:string",
|
|
94
|
+
provider: "google-gemini",
|
|
95
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
96
|
+
ai: { model: "gemini-2.5-flash-lite", maxTokens: 4000, stream: true },
|
|
97
|
+
agents: ["researcher"],
|
|
98
|
+
},
|
|
99
|
+
],
|
|
100
|
+
};
|
|
101
|
+
|
|
102
|
+
async function main() {
|
|
103
|
+
const crew = new AxCrew(config, AxCrewFunctions);
|
|
104
|
+
await crew.addAgentsToCrew(["researcher"]);
|
|
105
|
+
await crew.addAgentsToCrew(["writer"]);
|
|
106
|
+
|
|
107
|
+
const writer = crew.agents?.get("writer");
|
|
108
|
+
const researcher = crew.agents?.get("researcher");
|
|
109
|
+
if (!writer || !researcher) throw new Error("Failed to initialize agents");
|
|
110
|
+
|
|
111
|
+
try {
|
|
112
|
+
const { article } = await writer.forward({
|
|
113
|
+
topic: "Quantum Computing Benefits",
|
|
114
|
+
});
|
|
115
|
+
console.log("Article:", article);
|
|
116
|
+
|
|
117
|
+
// Per-agent metrics
|
|
118
|
+
console.log("Writer Metrics:", JSON.stringify(writer.getMetrics?.(), null, 2));
|
|
119
|
+
console.log("Researcher Metrics:", JSON.stringify(researcher.getMetrics?.(), null, 2));
|
|
120
|
+
|
|
121
|
+
// Crew-wide aggregate
|
|
122
|
+
console.log("Crew Metrics:", JSON.stringify(crew.getCrewMetrics(), null, 2));
|
|
123
|
+
|
|
124
|
+
// Reset for next measurement period
|
|
125
|
+
crew.resetCosts();
|
|
126
|
+
} finally {
|
|
127
|
+
crew.destroy();
|
|
128
|
+
}
|
|
129
|
+
}
|
|
130
|
+
|
|
131
|
+
main().catch(console.error);
|
|
132
|
+
```
|
|
133
|
+
|
|
134
|
+
## Accessing Specific Fields
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
const m = agent.getMetrics();
|
|
138
|
+
|
|
139
|
+
// Cost
|
|
140
|
+
console.log(`Estimated USD: ${m?.estimatedCostUSD ?? 0}`);
|
|
141
|
+
|
|
142
|
+
// Tokens
|
|
143
|
+
console.log(`Prompt: ${m?.tokens?.promptTokens}, Completion: ${m?.tokens?.completionTokens}, Total: ${m?.tokens?.totalTokens}`);
|
|
144
|
+
|
|
145
|
+
// Request stats
|
|
146
|
+
console.log(`Requests: ${m?.requests?.totalRequests}, Errors: ${m?.requests?.totalErrors}`);
|
|
147
|
+
|
|
148
|
+
// Function calls
|
|
149
|
+
console.log(`Function calls: ${m?.functions?.totalFunctionCalls}`);
|
|
150
|
+
if (m?.functions?.details) {
|
|
151
|
+
for (const fn of m.functions.details) {
|
|
152
|
+
console.log(` ${fn.name}: ${fn.calls} calls, ${fn.totalLatencyMs}ms`);
|
|
153
|
+
}
|
|
154
|
+
}
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Do Not Generate
|
|
158
|
+
|
|
159
|
+
- Do NOT call `getMetrics()` on the crew object -- use `crew.getCrewMetrics()` for crew-level and `agent.getMetrics()` for per-agent.
|
|
160
|
+
- Do NOT confuse `resetCosts()` with `resetCrewMetrics()` -- `resetCosts()` is broader (resets agent usage too).
|
|
161
|
+
- Do NOT expect `getMetrics()` to return cost data from the legacy `getUsage()` / `getCosts()` API -- those are separate.
|
|
162
|
+
- Do NOT assume `functions.details` is always present -- it is `undefined` when no function calls have been made.
|
|
163
|
+
- Do NOT forget that `estimatedCostUSD` is an estimate based on tracked token counts and model pricing.
|
|
164
|
+
|
|
165
|
+
## References
|
|
166
|
+
|
|
167
|
+
- [basic-researcher-writer.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/basic-researcher-writer.ts)
|
|
168
|
+
- [rlm-long-task.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/rlm-long-task.ts)
|
|
169
|
+
- [Source: MetricsSnapshot type](https://github.com/amitdeshmukh/ax-crew/blob/main/src/metrics/types.ts)
|
|
170
|
+
- [Source: MetricsRegistry](https://github.com/amitdeshmukh/ax-crew/blob/main/src/metrics/registry.ts)
|