@amitdeshmukh/ax-crew 8.5.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/.claude/skills/ax-crew/SKILL.md +466 -0
- package/CHANGELOG.md +13 -0
- package/LICENSE +21 -0
- package/README.md +7 -7
- package/examples/run-crew-workflow.ts +2 -2
- 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
- package/examples/run-manager.ts +0 -98
|
@@ -0,0 +1,204 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ax-crew-providers
|
|
3
|
+
version: __VERSION__
|
|
4
|
+
description: "AxCrew provider configuration: openai, anthropic, google-gemini, azure-openai, groq, ollama, mistral, cohere, grok/xAI, perplexity, and model setup."
|
|
5
|
+
tags: [provider, openai, anthropic, google-gemini, azure, groq, ollama, mistral, cohere, model, grok, perplexity]
|
|
6
|
+
---
|
|
7
|
+
|
|
8
|
+
# Providers
|
|
9
|
+
|
|
10
|
+
AxCrew delegates provider instantiation to the Ax `ai()` factory. The `Provider` type is derived from Ax's `AxAIArgs['name']`, so any provider Ax supports is available.
|
|
11
|
+
|
|
12
|
+
## Provider Type
|
|
13
|
+
|
|
14
|
+
```ts
|
|
15
|
+
type Provider = AxAIArgs<any>['name'];
|
|
16
|
+
// Known values: "openai", "anthropic", "google-gemini", "azure-openai",
|
|
17
|
+
// "groq", "ollama", "mistral", "cohere", "grok", "deepseek", "huggingface", etc.
|
|
18
|
+
```
|
|
19
|
+
|
|
20
|
+
## AgentConfig Provider Fields
|
|
21
|
+
|
|
22
|
+
```ts
|
|
23
|
+
interface AgentConfig {
|
|
24
|
+
provider: Provider; // e.g. "openai"
|
|
25
|
+
providerKeyName?: string; // env var name, e.g. "OPENAI_API_KEY"
|
|
26
|
+
ai: AxModelConfig & { model: string }; // model name + temperature, maxTokens, etc.
|
|
27
|
+
apiURL?: string; // custom endpoint (ollama, proxies)
|
|
28
|
+
providerArgs?: Record<string, unknown>; // provider-specific args (azure, etc.)
|
|
29
|
+
options?: Partial<AxProgramForwardOptions<any>> & Record<string, any>; // searchParameters, codeExecution, etc.
|
|
30
|
+
}
|
|
31
|
+
```
|
|
32
|
+
|
|
33
|
+
## Provider-Specific Configuration
|
|
34
|
+
|
|
35
|
+
### Azure OpenAI
|
|
36
|
+
|
|
37
|
+
Use `providerArgs` for Azure-specific fields:
|
|
38
|
+
|
|
39
|
+
```ts
|
|
40
|
+
{
|
|
41
|
+
name: "AzureAgent",
|
|
42
|
+
provider: "azure-openai",
|
|
43
|
+
providerKeyName: "AZURE_OPENAI_API_KEY",
|
|
44
|
+
ai: { model: "gpt-5-mini", temperature: 0 },
|
|
45
|
+
providerArgs: {
|
|
46
|
+
resourceName: "your-resource-name",
|
|
47
|
+
deploymentName: "your-deployment-name",
|
|
48
|
+
version: "2025-01-01-preview",
|
|
49
|
+
},
|
|
50
|
+
}
|
|
51
|
+
```
|
|
52
|
+
|
|
53
|
+
### Ollama (Local)
|
|
54
|
+
|
|
55
|
+
Set `apiURL` to point at the local Ollama endpoint:
|
|
56
|
+
|
|
57
|
+
```ts
|
|
58
|
+
{
|
|
59
|
+
name: "LocalAgent",
|
|
60
|
+
provider: "ollama",
|
|
61
|
+
providerKeyName: "OLLAMA_API_KEY", // can be any non-empty value
|
|
62
|
+
apiURL: "http://localhost:11434",
|
|
63
|
+
ai: { model: "llama3", temperature: 0.7 },
|
|
64
|
+
}
|
|
65
|
+
```
|
|
66
|
+
|
|
67
|
+
### Grok / xAI
|
|
68
|
+
|
|
69
|
+
Use provider `"grok"` with a Grok API key:
|
|
70
|
+
|
|
71
|
+
```ts
|
|
72
|
+
{
|
|
73
|
+
name: "XSearchAgent",
|
|
74
|
+
provider: "grok",
|
|
75
|
+
providerKeyName: "GROK_API_KEY",
|
|
76
|
+
ai: { model: "grok-3-latest", temperature: 0.1 },
|
|
77
|
+
options: {
|
|
78
|
+
stream: true,
|
|
79
|
+
searchParameters: {
|
|
80
|
+
mode: "on",
|
|
81
|
+
returnCitations: true,
|
|
82
|
+
maxSearchResults: 10,
|
|
83
|
+
sources: [
|
|
84
|
+
{ type: "x" },
|
|
85
|
+
{ type: "web" },
|
|
86
|
+
{ type: "news" },
|
|
87
|
+
],
|
|
88
|
+
},
|
|
89
|
+
},
|
|
90
|
+
}
|
|
91
|
+
```
|
|
92
|
+
|
|
93
|
+
### Perplexity (via MCP)
|
|
94
|
+
|
|
95
|
+
Use any provider as the agent's LLM and attach Perplexity as an MCP server:
|
|
96
|
+
|
|
97
|
+
```ts
|
|
98
|
+
{
|
|
99
|
+
name: "DeepResearchAgent",
|
|
100
|
+
provider: "google-gemini",
|
|
101
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
102
|
+
ai: { model: "gemini-2.5-flash-lite", temperature: 0.1 },
|
|
103
|
+
mcpServers: {
|
|
104
|
+
"perplexity-mcp": {
|
|
105
|
+
command: "uvx",
|
|
106
|
+
args: ["perplexity-mcp"],
|
|
107
|
+
env: {
|
|
108
|
+
PERPLEXITY_API_KEY: process.env.PERPLEXITY_API_KEY,
|
|
109
|
+
PERPLEXITY_MODEL: "sonar-deep-research",
|
|
110
|
+
},
|
|
111
|
+
},
|
|
112
|
+
},
|
|
113
|
+
}
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Mixed Providers in One Crew
|
|
117
|
+
|
|
118
|
+
Each agent can use a different provider. The `providerKeyName` maps to an environment variable read at runtime.
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
const config: AxCrewConfig = {
|
|
122
|
+
crew: [
|
|
123
|
+
{
|
|
124
|
+
name: "Planner",
|
|
125
|
+
provider: "anthropic",
|
|
126
|
+
providerKeyName: "ANTHROPIC_API_KEY",
|
|
127
|
+
ai: { model: "claude-3-5-sonnet-20240620", temperature: 1 },
|
|
128
|
+
signature: "topic:string -> queries:string[]",
|
|
129
|
+
description: "Generates search queries",
|
|
130
|
+
},
|
|
131
|
+
{
|
|
132
|
+
name: "Searcher",
|
|
133
|
+
provider: "google-gemini",
|
|
134
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
135
|
+
ai: { model: "gemini-1.5-pro", temperature: 0.5 },
|
|
136
|
+
signature: "query:string -> results:string",
|
|
137
|
+
description: "Searches the web",
|
|
138
|
+
},
|
|
139
|
+
{
|
|
140
|
+
name: "Writer",
|
|
141
|
+
provider: "openai",
|
|
142
|
+
providerKeyName: "OPENAI_API_KEY",
|
|
143
|
+
ai: { model: "gpt-4o", temperature: 0.8 },
|
|
144
|
+
signature: "topic:string, results:string[] -> post:string",
|
|
145
|
+
description: "Writes content from research",
|
|
146
|
+
},
|
|
147
|
+
],
|
|
148
|
+
};
|
|
149
|
+
```
|
|
150
|
+
|
|
151
|
+
## Canonical Pattern
|
|
152
|
+
|
|
153
|
+
```ts
|
|
154
|
+
import { AxCrew } from "ax-crew";
|
|
155
|
+
import type { AxCrewConfig } from "ax-crew";
|
|
156
|
+
|
|
157
|
+
const config: AxCrewConfig = {
|
|
158
|
+
crew: [
|
|
159
|
+
{
|
|
160
|
+
name: "TestAgent",
|
|
161
|
+
description: "Test Agent for Azure OpenAI",
|
|
162
|
+
provider: "azure-openai",
|
|
163
|
+
providerKeyName: "AZURE_OPENAI_API_KEY",
|
|
164
|
+
signature: "userQuery:string -> answer:string",
|
|
165
|
+
ai: { model: "gpt-5-mini", temperature: 0 },
|
|
166
|
+
providerArgs: {
|
|
167
|
+
resourceName: "your-resource-name",
|
|
168
|
+
deploymentName: "your-deployment-name",
|
|
169
|
+
version: "2025-01-01-preview",
|
|
170
|
+
},
|
|
171
|
+
options: { debug: true, stream: false },
|
|
172
|
+
},
|
|
173
|
+
],
|
|
174
|
+
};
|
|
175
|
+
|
|
176
|
+
async function main() {
|
|
177
|
+
const crew = new AxCrew(config);
|
|
178
|
+
await crew.addAllAgents();
|
|
179
|
+
|
|
180
|
+
const agent = crew.agents?.get("TestAgent");
|
|
181
|
+
const response = await agent?.forward({ userQuery: "What is the capital of France?" });
|
|
182
|
+
console.log(response?.answer);
|
|
183
|
+
|
|
184
|
+
crew.destroy();
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
main().catch(console.error);
|
|
188
|
+
```
|
|
189
|
+
|
|
190
|
+
## Do Not Generate
|
|
191
|
+
|
|
192
|
+
- Do NOT hardcode API keys in config -- always use `providerKeyName` which reads from `process.env`.
|
|
193
|
+
- Do NOT use `providerArgs` for non-Azure providers unless the Ax factory documents it -- standard providers only need `provider`, `providerKeyName`, `ai`, and optionally `apiURL`.
|
|
194
|
+
- Do NOT confuse `options.searchParameters` (Grok-specific forward option) with `mcpServers` (external tool servers).
|
|
195
|
+
- Do NOT set `provider: "perplexity"` -- Perplexity is accessed via an MCP server, not as a native Ax provider.
|
|
196
|
+
- Do NOT omit `providerKeyName` -- the crew will throw at initialization if the env var is missing.
|
|
197
|
+
|
|
198
|
+
## References
|
|
199
|
+
|
|
200
|
+
- [providerArgs.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/providerArgs.ts) (Azure OpenAI)
|
|
201
|
+
- [search-tweets.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/search-tweets.ts) (Grok/xAI)
|
|
202
|
+
- [perplexityDeepSearch.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/perplexityDeepSearch.ts) (Perplexity MCP)
|
|
203
|
+
- [write-post-and-publish-to-wordpress.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/write-post-and-publish-to-wordpress.ts) (mixed providers)
|
|
204
|
+
- [src/agents/compose.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/agents/compose.ts)
|
|
@@ -0,0 +1,169 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ax-crew-signatures
|
|
3
|
+
description: AxCrew DSPy-style signature format for defining agent inputs/outputs. Covers signature syntax, types (string, number, boolean, class, string[], json, image, audio, date), optional fields (?), field descriptions, and AxSignature builder alternative.
|
|
4
|
+
version: "__VERSION__"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# AxCrew Signatures
|
|
8
|
+
|
|
9
|
+
DSPy-style string format: `"input1:type, input2:type -> output1:type, output2:type"`
|
|
10
|
+
|
|
11
|
+
## Supported Types
|
|
12
|
+
|
|
13
|
+
| Type | Description |
|
|
14
|
+
|------|-------------|
|
|
15
|
+
| `string` | Text (default if omitted) |
|
|
16
|
+
| `number` | Numeric |
|
|
17
|
+
| `boolean` | True/false |
|
|
18
|
+
| `string[]` | Array of strings |
|
|
19
|
+
| `json` | Arbitrary JSON object |
|
|
20
|
+
| `class` | Classification label |
|
|
21
|
+
| `image` | Image input |
|
|
22
|
+
| `audio` | Audio input |
|
|
23
|
+
| `date` | Date value |
|
|
24
|
+
|
|
25
|
+
## Signature Syntax
|
|
26
|
+
|
|
27
|
+
```typescript
|
|
28
|
+
// Minimal — types default to string
|
|
29
|
+
"question -> answer"
|
|
30
|
+
|
|
31
|
+
// Explicit types
|
|
32
|
+
"question:string -> answer:string"
|
|
33
|
+
|
|
34
|
+
// Multiple inputs and outputs
|
|
35
|
+
"question:string, context:string -> answer:string, confidence:number"
|
|
36
|
+
|
|
37
|
+
// Optional fields use ? suffix
|
|
38
|
+
"query:string, context?:string -> answer:string"
|
|
39
|
+
|
|
40
|
+
// Field descriptions in quotes
|
|
41
|
+
'question:string "The user question", context:string "Reference text" -> answer:string "Final answer", confidence:number "0-1 score"'
|
|
42
|
+
|
|
43
|
+
// Array output
|
|
44
|
+
'context:string, query:string -> answer:string, keyFindings:string[] "Analyzes context and returns findings"'
|
|
45
|
+
|
|
46
|
+
// Classification
|
|
47
|
+
"text:string -> sentiment:class"
|
|
48
|
+
|
|
49
|
+
// Multi-modal
|
|
50
|
+
"image:image, question:string -> caption:string"
|
|
51
|
+
|
|
52
|
+
// JSON output
|
|
53
|
+
"query:string -> result:json"
|
|
54
|
+
```
|
|
55
|
+
|
|
56
|
+
## Full Examples in AgentConfig
|
|
57
|
+
|
|
58
|
+
```typescript
|
|
59
|
+
import { AxCrew } from 'ax-crew';
|
|
60
|
+
import type { AxCrewConfig } from 'ax-crew';
|
|
61
|
+
|
|
62
|
+
const config: AxCrewConfig = {
|
|
63
|
+
crew: [
|
|
64
|
+
// Simple Q&A
|
|
65
|
+
{
|
|
66
|
+
name: "QA",
|
|
67
|
+
description: "Answers questions",
|
|
68
|
+
signature: "question:string -> answer:string",
|
|
69
|
+
provider: "openai",
|
|
70
|
+
ai: { model: "gpt-4o", temperature: 0 },
|
|
71
|
+
},
|
|
72
|
+
// Multi-field with descriptions
|
|
73
|
+
{
|
|
74
|
+
name: "Analyst",
|
|
75
|
+
description: "Analyzes data and returns structured findings",
|
|
76
|
+
signature:
|
|
77
|
+
'context:string, query:string -> answer:string, keyFindings:string[] "Analyzes context and returns findings"',
|
|
78
|
+
provider: "google-gemini",
|
|
79
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
80
|
+
ai: { model: "gemini-2.5-flash", temperature: 0 },
|
|
81
|
+
},
|
|
82
|
+
// Optional input field
|
|
83
|
+
{
|
|
84
|
+
name: "Summarizer",
|
|
85
|
+
description: "Summarizes text with optional style",
|
|
86
|
+
signature: "text:string, style?:string -> summary:string",
|
|
87
|
+
provider: "openai",
|
|
88
|
+
ai: { model: "gpt-4o-mini" },
|
|
89
|
+
},
|
|
90
|
+
// Classification
|
|
91
|
+
{
|
|
92
|
+
name: "Classifier",
|
|
93
|
+
description: "Classifies sentiment",
|
|
94
|
+
signature: 'question:string -> sentiment:string "positive, negative, or neutral"',
|
|
95
|
+
provider: "google-gemini",
|
|
96
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
97
|
+
ai: { model: "gemini-2.5-flash", temperature: 0 },
|
|
98
|
+
},
|
|
99
|
+
// Multi-output with confidence
|
|
100
|
+
{
|
|
101
|
+
name: "Extractor",
|
|
102
|
+
description: "Extracts entities from text",
|
|
103
|
+
signature: "text:string -> entities:string[], confidence:number",
|
|
104
|
+
provider: "openai",
|
|
105
|
+
ai: { model: "gpt-4o" },
|
|
106
|
+
},
|
|
107
|
+
],
|
|
108
|
+
};
|
|
109
|
+
|
|
110
|
+
async function main() {
|
|
111
|
+
const crew = new AxCrew(config);
|
|
112
|
+
await crew.addAllAgents();
|
|
113
|
+
|
|
114
|
+
const qa = crew.agents?.get("QA");
|
|
115
|
+
const result = await qa?.forward({ question: "What is TypeScript?" });
|
|
116
|
+
console.log(result?.answer);
|
|
117
|
+
|
|
118
|
+
const analyst = crew.agents?.get("Analyst");
|
|
119
|
+
const analysis = await analyst?.forward({
|
|
120
|
+
context: "Q1 revenue: $10M, Q2: $15M, Q3: $12M",
|
|
121
|
+
query: "What is the revenue trend?",
|
|
122
|
+
});
|
|
123
|
+
console.log(analysis?.answer, analysis?.keyFindings);
|
|
124
|
+
|
|
125
|
+
crew.destroy();
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
main().catch(console.error);
|
|
129
|
+
```
|
|
130
|
+
|
|
131
|
+
## AxSignature Builder Alternative
|
|
132
|
+
|
|
133
|
+
The `signature` field also accepts an `AxSignature` object from `@ax-llm/ax`:
|
|
134
|
+
|
|
135
|
+
```typescript
|
|
136
|
+
import { AxSignature } from '@ax-llm/ax';
|
|
137
|
+
import type { AxCrewConfig } from 'ax-crew';
|
|
138
|
+
|
|
139
|
+
const sig = new AxSignature(
|
|
140
|
+
"question:string, context:string -> answer:string, confidence:number"
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
const config: AxCrewConfig = {
|
|
144
|
+
crew: [
|
|
145
|
+
{
|
|
146
|
+
name: "Agent",
|
|
147
|
+
description: "Uses AxSignature object",
|
|
148
|
+
signature: sig, // AxSignature instance accepted
|
|
149
|
+
provider: "openai",
|
|
150
|
+
ai: { model: "gpt-4o" },
|
|
151
|
+
},
|
|
152
|
+
],
|
|
153
|
+
};
|
|
154
|
+
```
|
|
155
|
+
|
|
156
|
+
## Do Not Generate
|
|
157
|
+
|
|
158
|
+
- Do NOT omit the `->` separator between inputs and outputs
|
|
159
|
+
- Do NOT use unsupported types (e.g., `int`, `float`, `array`, `object` -- use `number`, `string[]`, `json`)
|
|
160
|
+
- Do NOT put field descriptions outside quotes: use `'field:type "desc"'` not `field:type desc`
|
|
161
|
+
- Do NOT use `|` or union types in signatures -- use `class` type for enums
|
|
162
|
+
- Do NOT confuse `string[]` (array of strings) with `json` (arbitrary object)
|
|
163
|
+
- Do NOT wrap the signature string in `AxSignature()` when passing to config -- raw strings work directly
|
|
164
|
+
|
|
165
|
+
## References
|
|
166
|
+
|
|
167
|
+
- [rlm-long-task.ts](../examples/rlm-long-task.ts) -- array output in signature
|
|
168
|
+
- [rlm-shared-fields.ts](../examples/rlm-shared-fields.ts) -- field descriptions in quotes
|
|
169
|
+
- [ace-customer-support.ts](../examples/ace-customer-support.ts) -- multi-output signature
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ax-crew-state
|
|
3
|
+
version: __VERSION__
|
|
4
|
+
description: "State management: shared state, StateInstance, set, get, getAll, reset, accessing state from class-based functions"
|
|
5
|
+
---
|
|
6
|
+
|
|
7
|
+
# State
|
|
8
|
+
|
|
9
|
+
Every `AxCrew` instance has a shared `StateInstance` at `crew.state`. All agents and class-based functions in the crew can access the same state.
|
|
10
|
+
|
|
11
|
+
## StateInstance API
|
|
12
|
+
|
|
13
|
+
```ts
|
|
14
|
+
interface StateInstance {
|
|
15
|
+
set(key: string, value: any): void; // Set a value
|
|
16
|
+
get(key: string): any; // Get a value
|
|
17
|
+
getAll(): Record<string, any>; // Get all key-value pairs (shallow copy)
|
|
18
|
+
reset(): void; // Clear all state
|
|
19
|
+
}
|
|
20
|
+
```
|
|
21
|
+
|
|
22
|
+
## Core Behavior
|
|
23
|
+
|
|
24
|
+
- `crew.state` is created automatically when `new AxCrew(config)` is called.
|
|
25
|
+
- State is keyed by `crewId` (auto-generated UUID). Each crew instance has its own isolated state.
|
|
26
|
+
- State persists for the lifetime of the crew. Calling `crew.destroy()` calls `state.reset()`.
|
|
27
|
+
- Values can be any type: strings, objects, arrays, etc.
|
|
28
|
+
|
|
29
|
+
## Canonical Pattern
|
|
30
|
+
|
|
31
|
+
```ts
|
|
32
|
+
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
33
|
+
import type { AxCrewConfig, FunctionRegistryType } from '@amitdeshmukh/ax-crew';
|
|
34
|
+
import type { AxFunction } from '@ax-llm/ax';
|
|
35
|
+
import dotenv from 'dotenv';
|
|
36
|
+
dotenv.config();
|
|
37
|
+
|
|
38
|
+
// Class-based function that reads from shared state
|
|
39
|
+
class SendEmail {
|
|
40
|
+
private state: Record<string, any>;
|
|
41
|
+
constructor(state: Record<string, any>) { this.state = state; }
|
|
42
|
+
toFunction(): AxFunction {
|
|
43
|
+
return {
|
|
44
|
+
name: 'SendEmail',
|
|
45
|
+
description: 'Send an email using configured SMTP credentials',
|
|
46
|
+
parameters: {
|
|
47
|
+
type: 'object',
|
|
48
|
+
properties: {
|
|
49
|
+
to: { type: 'string', description: 'Recipient email' },
|
|
50
|
+
subject: { type: 'string', description: 'Email subject' },
|
|
51
|
+
body: { type: 'string', description: 'Email body' },
|
|
52
|
+
},
|
|
53
|
+
required: ['to', 'subject', 'body']
|
|
54
|
+
},
|
|
55
|
+
func: async ({ to, subject, body }: { to: string; subject: string; body: string }) => {
|
|
56
|
+
// Access env credentials set via crew.state.set("env", {...})
|
|
57
|
+
const env = this.state.env || {};
|
|
58
|
+
const smtpHost = env.SMTP_HOST;
|
|
59
|
+
const smtpUser = env.SMTP_USER;
|
|
60
|
+
const smtpPass = env.SMTP_PASS;
|
|
61
|
+
// ... send email using credentials
|
|
62
|
+
return { sent: true, to };
|
|
63
|
+
}
|
|
64
|
+
};
|
|
65
|
+
}
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
const config: AxCrewConfig = {
|
|
69
|
+
crew: [
|
|
70
|
+
{
|
|
71
|
+
name: "notifier",
|
|
72
|
+
description: "An agent that sends email notifications",
|
|
73
|
+
signature: "task:string -> result:string",
|
|
74
|
+
provider: "openai",
|
|
75
|
+
providerKeyName: "OPENAI_API_KEY",
|
|
76
|
+
ai: { model: "gpt-4o-mini", temperature: 0 },
|
|
77
|
+
functions: ["SendEmail"],
|
|
78
|
+
}
|
|
79
|
+
]
|
|
80
|
+
};
|
|
81
|
+
|
|
82
|
+
async function main() {
|
|
83
|
+
const functions: FunctionRegistryType = { SendEmail };
|
|
84
|
+
const crew = new AxCrew(config, functions);
|
|
85
|
+
|
|
86
|
+
// Set environment variables in shared state BEFORE adding agents
|
|
87
|
+
crew.state.set("env", {
|
|
88
|
+
SMTP_HOST: "smtp.example.com",
|
|
89
|
+
SMTP_USER: "user@example.com",
|
|
90
|
+
SMTP_PASS: "secret",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
// You can also set arbitrary data
|
|
94
|
+
crew.state.set("company", "Acme Corp");
|
|
95
|
+
crew.state.set("maxRetries", 3);
|
|
96
|
+
|
|
97
|
+
await crew.addAllAgents();
|
|
98
|
+
const notifier = crew.agents?.get("notifier");
|
|
99
|
+
|
|
100
|
+
const result = await notifier?.forward({ task: "Notify user about order shipped" });
|
|
101
|
+
console.log(result?.result);
|
|
102
|
+
|
|
103
|
+
// Read state back
|
|
104
|
+
console.log("All state:", crew.state.getAll());
|
|
105
|
+
console.log("Company:", crew.state.get("company"));
|
|
106
|
+
|
|
107
|
+
// Reset state (clear all)
|
|
108
|
+
crew.state.reset();
|
|
109
|
+
|
|
110
|
+
crew.destroy();
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
main().catch(console.error);
|
|
114
|
+
```
|
|
115
|
+
|
|
116
|
+
## Setting Environment Credentials
|
|
117
|
+
|
|
118
|
+
The common pattern for passing credentials to class-based functions:
|
|
119
|
+
|
|
120
|
+
```ts
|
|
121
|
+
crew.state.set("env", {
|
|
122
|
+
WORDPRESS_URL: "http://my-wordpress-site.com",
|
|
123
|
+
WORDPRESS_USERNAME: "my-username",
|
|
124
|
+
WORDPRESS_PASSWORD: "my-password",
|
|
125
|
+
});
|
|
126
|
+
```
|
|
127
|
+
|
|
128
|
+
Inside the class-based function, access via `this.state.env`:
|
|
129
|
+
|
|
130
|
+
```ts
|
|
131
|
+
class MyFunction {
|
|
132
|
+
private state: Record<string, any>;
|
|
133
|
+
constructor(state: Record<string, any>) { this.state = state; }
|
|
134
|
+
toFunction(): AxFunction {
|
|
135
|
+
return {
|
|
136
|
+
name: 'MyFunction',
|
|
137
|
+
description: 'Does something',
|
|
138
|
+
parameters: { type: 'object', properties: {} },
|
|
139
|
+
func: () => {
|
|
140
|
+
const url = this.state.env?.WORDPRESS_URL; // reads from shared state
|
|
141
|
+
return url;
|
|
142
|
+
}
|
|
143
|
+
};
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
```
|
|
147
|
+
|
|
148
|
+
## Accessing State from Agents
|
|
149
|
+
|
|
150
|
+
Each `StatefulAxAgent` has a `state` property that references the crew's shared state:
|
|
151
|
+
|
|
152
|
+
```ts
|
|
153
|
+
const agent = crew.agents?.get("myAgent");
|
|
154
|
+
// agent.state is the same StateInstance as crew.state
|
|
155
|
+
```
|
|
156
|
+
|
|
157
|
+
## Do Not Generate
|
|
158
|
+
|
|
159
|
+
- Do NOT assume state values exist without checking; always use optional chaining (e.g. `this.state.env?.KEY`).
|
|
160
|
+
- Do NOT call `crew.state.set("env", ...)` after `addAllAgents()` if class-based functions read state during construction -- set state first.
|
|
161
|
+
- Do NOT confuse `crew.state` (StateInstance with `set`/`get`/`getAll`/`reset`) with plain objects. The state object passed to class-based function constructors is a plain record, not the `StateInstance` interface.
|
|
162
|
+
- Do NOT store sensitive credentials in state if the state object might be logged or serialized. The `getAll()` method returns all stored values.
|
|
163
|
+
|
|
164
|
+
## References
|
|
165
|
+
|
|
166
|
+
- [write-post-and-publish-to-wordpress.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/write-post-and-publish-to-wordpress.ts)
|
|
167
|
+
- [src/state/index.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/state/index.ts)
|
|
168
|
+
- [src/types.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/types.ts)
|
|
@@ -0,0 +1,143 @@
|
|
|
1
|
+
---
|
|
2
|
+
name: ax-crew-streaming
|
|
3
|
+
version: "__VERSION__"
|
|
4
|
+
description: "ax-crew streaming patterns: streaming, streamingForward, stream, delta, real-time, chunks, async generator consumption"
|
|
5
|
+
argument-hint: [topic]
|
|
6
|
+
allowed-tools: Read, Grep, Glob
|
|
7
|
+
---
|
|
8
|
+
|
|
9
|
+
# ax-crew Streaming
|
|
10
|
+
|
|
11
|
+
## streamingForward() vs forward()
|
|
12
|
+
|
|
13
|
+
`forward()` returns `Promise<Record<string, any>>` -- waits for full response.
|
|
14
|
+
`streamingForward()` returns `AxGenStreamingOut<any>` -- an async generator yielding chunks as they arrive.
|
|
15
|
+
|
|
16
|
+
Both accept the same input values object. Both work with `axgen` and `axagent` execution modes.
|
|
17
|
+
|
|
18
|
+
```typescript
|
|
19
|
+
// Blocking
|
|
20
|
+
const result = await agent.forward({ question: "What is AI?" });
|
|
21
|
+
console.log(result.answer);
|
|
22
|
+
|
|
23
|
+
// Streaming
|
|
24
|
+
const stream = agent.streamingForward({ question: "What is AI?" });
|
|
25
|
+
for await (const chunk of stream) {
|
|
26
|
+
if (chunk.delta && 'answer' in chunk.delta) {
|
|
27
|
+
process.stdout.write(chunk.delta.answer);
|
|
28
|
+
}
|
|
29
|
+
}
|
|
30
|
+
```
|
|
31
|
+
|
|
32
|
+
## chunk.delta Structure
|
|
33
|
+
|
|
34
|
+
Each yielded chunk has a `delta` property containing partial values keyed by output field names from the agent's signature.
|
|
35
|
+
|
|
36
|
+
```typescript
|
|
37
|
+
// For signature: 'question:string -> answer:string'
|
|
38
|
+
// chunk.delta = { answer: "partial text..." }
|
|
39
|
+
|
|
40
|
+
// For signature: 'query:string -> title:string, summary:string'
|
|
41
|
+
// chunk.delta may contain { title: "..." } or { summary: "..." }
|
|
42
|
+
```
|
|
43
|
+
|
|
44
|
+
Always check for the field's existence before accessing:
|
|
45
|
+
```typescript
|
|
46
|
+
if (chunk.delta && typeof chunk.delta === 'object' && 'answer' in chunk.delta) {
|
|
47
|
+
process.stdout.write(chunk.delta.answer);
|
|
48
|
+
}
|
|
49
|
+
```
|
|
50
|
+
|
|
51
|
+
## Stream Config in Agent Config
|
|
52
|
+
|
|
53
|
+
Enable streaming at the provider level via `ai.stream` or `options.stream`:
|
|
54
|
+
|
|
55
|
+
```typescript
|
|
56
|
+
{
|
|
57
|
+
name: "StreamAgent",
|
|
58
|
+
ai: { model: "gemini-2.5-flash", stream: true }, // provider-level
|
|
59
|
+
options: { stream: true }, // forward options level
|
|
60
|
+
}
|
|
61
|
+
```
|
|
62
|
+
|
|
63
|
+
## Overload Signatures
|
|
64
|
+
|
|
65
|
+
```typescript
|
|
66
|
+
// Without explicit AI instance (uses agent's configured AI)
|
|
67
|
+
agent.streamingForward(values: Record<string, any>, options?: AxProgramStreamingForwardOptions): AxGenStreamingOut<any>;
|
|
68
|
+
|
|
69
|
+
// With explicit AI instance
|
|
70
|
+
agent.streamingForward(ai: AxAI, values: Record<string, any>, options?: AxProgramStreamingForwardOptions): AxGenStreamingOut<any>;
|
|
71
|
+
```
|
|
72
|
+
|
|
73
|
+
## Canonical Pattern
|
|
74
|
+
|
|
75
|
+
```typescript
|
|
76
|
+
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
77
|
+
import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
|
|
78
|
+
|
|
79
|
+
const config: AxCrewConfig = {
|
|
80
|
+
crew: [
|
|
81
|
+
{
|
|
82
|
+
name: "ManagerAgent",
|
|
83
|
+
description: "Completes a user specified task",
|
|
84
|
+
signature:
|
|
85
|
+
'question:string "a question to be answered" -> answer:string "the answer to the question"',
|
|
86
|
+
provider: "google-gemini",
|
|
87
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
88
|
+
ai: { model: "gemini-2.5-flash", maxTokens: 1000, temperature: 0 },
|
|
89
|
+
options: { debug: true, stream: true },
|
|
90
|
+
agents: ["MathAgent"],
|
|
91
|
+
},
|
|
92
|
+
{
|
|
93
|
+
name: "MathAgent",
|
|
94
|
+
description: "Solves math problems",
|
|
95
|
+
signature:
|
|
96
|
+
'mathProblem:string "a math problem" -> solution:string "the answer"',
|
|
97
|
+
provider: "google-gemini",
|
|
98
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
99
|
+
ai: { model: "gemini-2.5-pro", temperature: 0 },
|
|
100
|
+
},
|
|
101
|
+
],
|
|
102
|
+
};
|
|
103
|
+
|
|
104
|
+
async function main() {
|
|
105
|
+
const crew = new AxCrew(config);
|
|
106
|
+
await crew.addAllAgents();
|
|
107
|
+
|
|
108
|
+
const agent = crew.agents?.get("ManagerAgent");
|
|
109
|
+
if (!agent) throw new Error("Agent not found");
|
|
110
|
+
|
|
111
|
+
const stream = agent.streamingForward({
|
|
112
|
+
question: "Get me the first 5 fibonacci numbers divided by 2",
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
for await (const chunk of stream) {
|
|
116
|
+
if (chunk.delta && typeof chunk.delta === 'object' && 'answer' in chunk.delta) {
|
|
117
|
+
process.stdout.write(chunk.delta.answer);
|
|
118
|
+
}
|
|
119
|
+
}
|
|
120
|
+
console.log('\n');
|
|
121
|
+
|
|
122
|
+
// Metrics still available after streaming
|
|
123
|
+
console.log("Agent Metrics:", JSON.stringify(agent.getMetrics?.(), null, 2));
|
|
124
|
+
console.log("Crew Metrics:", JSON.stringify(crew.getCrewMetrics(), null, 2));
|
|
125
|
+
|
|
126
|
+
crew.destroy();
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
main().catch(console.error);
|
|
130
|
+
```
|
|
131
|
+
|
|
132
|
+
## Do Not Generate
|
|
133
|
+
|
|
134
|
+
- Do NOT `await` the return of `streamingForward()` -- it returns the async generator directly, not a promise.
|
|
135
|
+
- Do NOT assume `chunk.delta` is always a string -- it is an object keyed by output field names.
|
|
136
|
+
- Do NOT forget to check field existence in `chunk.delta` before accessing (`'fieldName' in chunk.delta`).
|
|
137
|
+
- Do NOT use `forward()` with the expectation of receiving streaming chunks -- use `streamingForward()`.
|
|
138
|
+
- Do NOT skip `crew.destroy()` -- it cleans up MCP servers and resources.
|
|
139
|
+
|
|
140
|
+
## References
|
|
141
|
+
|
|
142
|
+
- [streaming.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/streaming.ts)
|
|
143
|
+
- [Source: StatefulAxAgent.streamingForward](https://github.com/amitdeshmukh/ax-crew/blob/main/src/agents/index.ts)
|