@amitdeshmukh/ax-crew 5.0.0 → 6.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 +22 -0
- package/README.md +7 -59
- package/dist/agents/agentConfig.d.ts +6 -6
- package/dist/agents/agentConfig.js +41 -122
- package/dist/agents/compose.d.ts +15 -0
- package/dist/agents/compose.js +58 -0
- package/dist/agents/index.d.ts +20 -4
- package/dist/agents/index.js +19 -3
- package/dist/functions/index.d.ts +11 -0
- package/dist/functions/index.js +11 -1
- package/dist/index.d.ts +25 -8
- package/dist/index.js +26 -1
- package/dist/types.d.ts +40 -6
- package/examples/basic-researcher-writer.ts +5 -3
- package/examples/mcp-agent.ts +20 -43
- package/examples/perplexityDeepSearch.ts +6 -5
- package/examples/providerArgs.ts +2 -1
- package/examples/search-tweets.ts +5 -4
- package/examples/solve-math-problem.ts +7 -5
- package/examples/streaming.ts +2 -2
- package/package.json +4 -4
- package/src/agents/agentConfig.ts +46 -140
- package/src/agents/compose.ts +80 -0
- package/src/agents/index.ts +22 -6
- package/src/functions/index.ts +11 -1
- package/src/index.ts +25 -11
- package/src/types.ts +44 -21
- package/dist/config/index.d.ts +0 -5
- package/dist/config/index.js +0 -30
- package/src/config/index.ts +0 -40
package/dist/index.js
CHANGED
|
@@ -1,5 +1,30 @@
|
|
|
1
1
|
import { AxCrew } from './agents/index.js';
|
|
2
2
|
import { AxCrewFunctions } from './functions/index.js';
|
|
3
|
+
/**
|
|
4
|
+
* Metrics types and helpers for request counts, token usage, and estimated cost.
|
|
5
|
+
*
|
|
6
|
+
* Re-exports the metrics module for convenience:
|
|
7
|
+
* - Types: TokenUsage, MetricsSnapshot, etc.
|
|
8
|
+
* - Namespace: MetricsRegistry (record/snapshot/reset helpers)
|
|
9
|
+
*/
|
|
3
10
|
export * from './metrics/index.js';
|
|
11
|
+
/**
|
|
12
|
+
* MetricsRegistry provides functions to record requests, tokens, and cost,
|
|
13
|
+
* and to snapshot/reset metrics at agent or crew granularity.
|
|
14
|
+
*/
|
|
4
15
|
export { MetricsRegistry } from './metrics/index.js';
|
|
5
|
-
|
|
16
|
+
/**
|
|
17
|
+
* Create and manage a crew of Ax agents that share state and metrics.
|
|
18
|
+
* See the `AxCrew` class for full documentation.
|
|
19
|
+
*/
|
|
20
|
+
const _AxCrew = AxCrew;
|
|
21
|
+
/**
|
|
22
|
+
* Built-in function registry with common tools that can be referenced by name
|
|
23
|
+
* from agent configs, or extended with your own functions.
|
|
24
|
+
*/
|
|
25
|
+
const _AxCrewFunctions = AxCrewFunctions;
|
|
26
|
+
export {
|
|
27
|
+
/** See class JSDoc on the `AxCrew` implementation. */
|
|
28
|
+
_AxCrew as AxCrew,
|
|
29
|
+
/** Built-in function registry; see file docs in `src/functions/index.ts`. */
|
|
30
|
+
_AxCrewFunctions as AxCrewFunctions, };
|
package/dist/types.d.ts
CHANGED
|
@@ -1,5 +1,5 @@
|
|
|
1
|
-
import type { AxFunction, AxSignature, AxModelConfig, AxMCPStreamableHTTPTransportOptions, AxProgramForwardOptions } from '@ax-llm/ax';
|
|
2
|
-
export type Provider = '
|
|
1
|
+
import type { AxFunction, AxSignature, AxModelConfig, AxMCPStreamableHTTPTransportOptions, AxProgramForwardOptions, AxAIArgs } from '@ax-llm/ax';
|
|
2
|
+
export type Provider = AxAIArgs<any>['name'];
|
|
3
3
|
/**
|
|
4
4
|
* A state instance that is shared between agents.
|
|
5
5
|
* This can be used to store data that becomes available to all agents and functions in an out-of-band manner.
|
|
@@ -177,9 +177,43 @@ interface AgentConfig {
|
|
|
177
177
|
mcpServers?: Record<string, MCPTransportConfig>;
|
|
178
178
|
}
|
|
179
179
|
/**
|
|
180
|
-
* The
|
|
180
|
+
* The configuration object for an AxCrew instance.
|
|
181
|
+
*
|
|
182
|
+
* @property {AgentConfig[]} crew - The agents that make up the crew.
|
|
183
|
+
* @example
|
|
184
|
+
* const config: AxCrewConfig = {
|
|
185
|
+
* crew: [
|
|
186
|
+
* {
|
|
187
|
+
* name: "Agent1",
|
|
188
|
+
* description: "Agent 1 description",
|
|
189
|
+
* signature: "signature",
|
|
190
|
+
* provider: "provider",
|
|
191
|
+
* providerKeyName: "providerKeyName",
|
|
192
|
+
* ai: {
|
|
193
|
+
* model: "model",
|
|
194
|
+
* temperature: 0,
|
|
195
|
+
* },
|
|
196
|
+
* options: {
|
|
197
|
+
* debug: true,
|
|
198
|
+
* },
|
|
199
|
+
* functions: ["function1", "function2"],
|
|
200
|
+
* agents: ["agent2"],
|
|
201
|
+
* },
|
|
202
|
+
* {
|
|
203
|
+
* name: "Agent2",
|
|
204
|
+
* description: "Agent 2 description",
|
|
205
|
+
* signature: "signature",
|
|
206
|
+
* provider: "provider",
|
|
207
|
+
* providerKeyName: "providerKeyName",
|
|
208
|
+
* ai: {
|
|
209
|
+
* model: "model",
|
|
210
|
+
* temperature: 0,
|
|
211
|
+
* }
|
|
212
|
+
* ]
|
|
213
|
+
* }
|
|
214
|
+
* const crew = new AxCrew(config);
|
|
181
215
|
*/
|
|
182
|
-
|
|
216
|
+
interface AxCrewConfig {
|
|
183
217
|
crew: AgentConfig[];
|
|
184
|
-
}
|
|
185
|
-
export { type AgentConfig, type
|
|
218
|
+
}
|
|
219
|
+
export { type AgentConfig, type AxCrewConfig, type AggregatedMetrics, type StateInstance, type FunctionRegistryType, type MCPStdioTransportConfig, type MCPHTTPSSETransportConfig, type MCPStreamableHTTPTransportConfig, type MCPTransportConfig, type ModelUsage, type ModelInfo, type UsageCost, type AggregatedCosts };
|
|
@@ -2,6 +2,8 @@ import { AxCrew } from "../dist/index.js";
|
|
|
2
2
|
import { AxCrewFunctions } from "../dist/functions/index.js";
|
|
3
3
|
import type { AxCrewConfig } from "../dist/index.js";
|
|
4
4
|
import type { Provider } from "../dist/types.js";
|
|
5
|
+
import dotenv from "dotenv";
|
|
6
|
+
dotenv.config();
|
|
5
7
|
|
|
6
8
|
// Example agent configuration
|
|
7
9
|
const agentConfig: AxCrewConfig = {
|
|
@@ -26,10 +28,10 @@ const agentConfig: AxCrewConfig = {
|
|
|
26
28
|
name: "writer",
|
|
27
29
|
description: "A writing agent that creates content",
|
|
28
30
|
signature: "topic:string -> article:string",
|
|
29
|
-
provider: "
|
|
30
|
-
providerKeyName: "
|
|
31
|
+
provider: "google-gemini" as Provider,
|
|
32
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
31
33
|
ai: {
|
|
32
|
-
model: "
|
|
34
|
+
model: "gemini-2.5-flash-lite",
|
|
33
35
|
maxTokens: 4000,
|
|
34
36
|
stream: true
|
|
35
37
|
},
|
package/examples/mcp-agent.ts
CHANGED
|
@@ -1,4 +1,5 @@
|
|
|
1
|
-
import { AxCrew
|
|
1
|
+
import { AxCrew } from "../dist/index.js";
|
|
2
|
+
import type { AxCrewConfig } from "../dist/types.js";
|
|
2
3
|
|
|
3
4
|
import dotenv from "dotenv";
|
|
4
5
|
dotenv.config();
|
|
@@ -7,31 +8,23 @@ dotenv.config();
|
|
|
7
8
|
const config = {
|
|
8
9
|
crew: [
|
|
9
10
|
{
|
|
10
|
-
name: "
|
|
11
|
-
description: "A specialized agent with access to
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
providerKeyName: "ANTHROPIC_API_KEY",
|
|
11
|
+
name: "Context7DocsAgent",
|
|
12
|
+
description: "A specialized agent with access to Context7 Docs APIs that can: search for and get details about API docs",
|
|
13
|
+
signature: 'apiDocQuery:string "a question to be answered" -> apiDocAnswer:string "the answer to the question"',
|
|
14
|
+
provider: "google-gemini",
|
|
15
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
16
16
|
ai: {
|
|
17
|
-
model: "
|
|
17
|
+
model: "gemini-2.5-pro",
|
|
18
18
|
temperature: 0,
|
|
19
|
-
|
|
20
|
-
stream: true
|
|
19
|
+
stream: false
|
|
21
20
|
},
|
|
22
21
|
options: {
|
|
23
22
|
debug: true
|
|
24
23
|
},
|
|
25
24
|
"mcpServers": {
|
|
26
|
-
"
|
|
25
|
+
"context7": {
|
|
27
26
|
"command": "npx",
|
|
28
|
-
"args": [
|
|
29
|
-
"-y",
|
|
30
|
-
"@modelcontextprotocol/server-google-maps"
|
|
31
|
-
],
|
|
32
|
-
"env": {
|
|
33
|
-
"GOOGLE_MAPS_API_KEY": process.env.GOOGLE_MAPS_API_KEY
|
|
34
|
-
}
|
|
27
|
+
"args": ["-y", "@upstash/context7-mcp", "--api-key", process.env.CONTEXT7_API_KEY]
|
|
35
28
|
}
|
|
36
29
|
},
|
|
37
30
|
},
|
|
@@ -41,35 +34,19 @@ const config = {
|
|
|
41
34
|
prompt: "You are a manager agent that orchestrates tools and sub-agents. Read the user's objective, optionally produce a short plan, then call the MapsAgent when geospatial knowledge is needed. Keep answers direct and avoid extraneous commentary.",
|
|
42
35
|
signature:
|
|
43
36
|
'question:string "a question to be answered" -> answer:string "the answer to the question"',
|
|
44
|
-
provider: "
|
|
45
|
-
providerKeyName: "
|
|
37
|
+
provider: "google-gemini",
|
|
38
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
46
39
|
ai: {
|
|
47
|
-
model: "
|
|
40
|
+
model: "gemini-2.5-pro",
|
|
48
41
|
maxTokens: 1000,
|
|
49
42
|
temperature: 0,
|
|
50
|
-
stream:
|
|
43
|
+
stream: false
|
|
51
44
|
},
|
|
52
45
|
options: {
|
|
53
46
|
debug: true,
|
|
54
47
|
},
|
|
55
|
-
agents: ["
|
|
56
|
-
}
|
|
57
|
-
{
|
|
58
|
-
name: "MathAgent",
|
|
59
|
-
description: "Solves math problems",
|
|
60
|
-
signature:
|
|
61
|
-
'mathProblem:string "a sentence describing a math problem to be solved using Python code" -> solution:string "the answer to the math problem"',
|
|
62
|
-
provider: "google-gemini",
|
|
63
|
-
providerKeyName: "GEMINI_API_KEY",
|
|
64
|
-
ai: {
|
|
65
|
-
model: "gemini-1.5-pro",
|
|
66
|
-
temperature: 0,
|
|
67
|
-
stream: true
|
|
68
|
-
},
|
|
69
|
-
options: {
|
|
70
|
-
debug: false,
|
|
71
|
-
},
|
|
72
|
-
},
|
|
48
|
+
agents: ["Context7DocsAgent"]
|
|
49
|
+
}
|
|
73
50
|
],
|
|
74
51
|
};
|
|
75
52
|
|
|
@@ -81,9 +58,9 @@ await crew.addAllAgents();
|
|
|
81
58
|
|
|
82
59
|
// Get agent instances
|
|
83
60
|
const managerAgent = crew.agents?.get("ManagerAgent");
|
|
84
|
-
const
|
|
61
|
+
const context7DocsAgent = crew.agents?.get("Context7DocsAgent");
|
|
85
62
|
|
|
86
|
-
const userQuery: string = "
|
|
63
|
+
const userQuery: string = "How do i create an agent in the @amitdeshmukh/ax-crew framework and configure it to use an MCP server? Give me a concrete example.";
|
|
87
64
|
|
|
88
65
|
console.log(`\n\nQuestion: ${userQuery}`);
|
|
89
66
|
|
|
@@ -97,7 +74,7 @@ const main = async (): Promise<void> => {
|
|
|
97
74
|
// Print metrics
|
|
98
75
|
console.log("\nMetrics:\n+++++++++++++++++++++++++++++++++");
|
|
99
76
|
console.log("Manager Agent Metrics:", JSON.stringify((managerAgent as any)?.getMetrics?.(), null, 2));
|
|
100
|
-
console.log("
|
|
77
|
+
console.log("Context7 Docs Agent Metrics:", JSON.stringify((context7DocsAgent as any)?.getMetrics?.(), null, 2));
|
|
101
78
|
console.log("Crew Metrics:", JSON.stringify((crew as any)?.getCrewMetrics?.(), null, 2));
|
|
102
79
|
};
|
|
103
80
|
|
|
@@ -1,19 +1,20 @@
|
|
|
1
1
|
import { AxCrew } from "../dist/index.js";
|
|
2
|
+
import type { AxCrewConfig } from "../dist/types.js";
|
|
2
3
|
|
|
3
4
|
import dotenv from "dotenv";
|
|
4
5
|
dotenv.config();
|
|
5
6
|
|
|
6
7
|
// Define the crew configuration
|
|
7
|
-
const config = {
|
|
8
|
+
const config: AxCrewConfig = {
|
|
8
9
|
crew: [
|
|
9
10
|
{
|
|
10
11
|
name: "DeepResearchAgent",
|
|
11
12
|
description: "A specialized agent that performs deep research using perplexity",
|
|
12
|
-
signature: 'researchTopic:string "a topic of interest" ->
|
|
13
|
-
provider: "
|
|
14
|
-
providerKeyName: "
|
|
13
|
+
signature: 'researchTopic:string "a topic of interest" -> researchResult:string "The result of the research"',
|
|
14
|
+
provider: "google-gemini",
|
|
15
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
15
16
|
ai: {
|
|
16
|
-
model: "
|
|
17
|
+
model: "gemini-2.5-flash-lite",
|
|
17
18
|
temperature: 0.1,
|
|
18
19
|
},
|
|
19
20
|
options: {
|
package/examples/providerArgs.ts
CHANGED
|
@@ -1,15 +1,16 @@
|
|
|
1
1
|
import { AxCrew } from "../dist/index.js";
|
|
2
|
+
import type { AxCrewConfig } from "../dist/types.js";
|
|
2
3
|
|
|
3
4
|
import dotenv from "dotenv";
|
|
4
5
|
dotenv.config();
|
|
5
6
|
|
|
6
7
|
// Define the crew configuration
|
|
7
|
-
const config = {
|
|
8
|
+
const config: AxCrewConfig = {
|
|
8
9
|
crew: [
|
|
9
10
|
{
|
|
10
11
|
name: "XSearchAgent",
|
|
11
12
|
description: "A specialized agent that can search X (Twitter) posts for the latest news and updates about specific topics, people, or events. It can find trending posts, recent tweets, and real-time information from X platform.",
|
|
12
|
-
signature: 'searchQuery:string "a search query" ->
|
|
13
|
+
signature: 'searchQuery:string "a search query" -> searchResults:string "the response to the user query citing relevant sources including X posts and other web sources"',
|
|
13
14
|
provider: "grok",
|
|
14
15
|
providerKeyName: "GROK_API_KEY",
|
|
15
16
|
ai: {
|
|
@@ -55,8 +56,8 @@ const main = async (): Promise<void> => {
|
|
|
55
56
|
if (response) {
|
|
56
57
|
try {
|
|
57
58
|
for await (const chunk of response) {
|
|
58
|
-
if (chunk.delta && typeof chunk.delta === 'object' && '
|
|
59
|
-
process.stdout.write(chunk.delta.
|
|
59
|
+
if (chunk.delta && typeof chunk.delta === 'object' && 'searchResults' in chunk.delta) {
|
|
60
|
+
process.stdout.write(chunk.delta.searchResults);
|
|
60
61
|
}
|
|
61
62
|
}
|
|
62
63
|
console.log('\n');
|
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|
import { AxCrew } from "../dist/index.js";
|
|
2
2
|
import type { AxCrewConfig } from "../src/index.js";
|
|
3
|
+
import dotenv from "dotenv";
|
|
4
|
+
dotenv.config();
|
|
3
5
|
|
|
4
6
|
// Define the crew configuration
|
|
5
7
|
const config: AxCrewConfig = {
|
|
@@ -12,11 +14,11 @@ const config: AxCrewConfig = {
|
|
|
12
14
|
provider: "google-gemini",
|
|
13
15
|
providerKeyName: "GEMINI_API_KEY",
|
|
14
16
|
ai: {
|
|
15
|
-
model: "gemini-
|
|
17
|
+
model: "gemini-2.5-flash-lite",
|
|
16
18
|
temperature: 0,
|
|
17
19
|
},
|
|
18
20
|
options: {
|
|
19
|
-
debug:
|
|
21
|
+
debug: true,
|
|
20
22
|
codeExecution: true,
|
|
21
23
|
},
|
|
22
24
|
},
|
|
@@ -25,10 +27,10 @@ const config: AxCrewConfig = {
|
|
|
25
27
|
description: "Completes a user specified task",
|
|
26
28
|
signature:
|
|
27
29
|
'question:string "a question to be answered" -> answer:string "the answer to the question"',
|
|
28
|
-
provider: "
|
|
29
|
-
providerKeyName: "
|
|
30
|
+
provider: "google-gemini",
|
|
31
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
30
32
|
ai: {
|
|
31
|
-
model: "
|
|
33
|
+
model: "gemini-2.5-flash-lite",
|
|
32
34
|
maxTokens: 1000,
|
|
33
35
|
temperature: 0,
|
|
34
36
|
},
|
package/examples/streaming.ts
CHANGED
|
@@ -15,7 +15,7 @@ const config: AxCrewConfig = {
|
|
|
15
15
|
provider: "google-gemini",
|
|
16
16
|
providerKeyName: "GEMINI_API_KEY",
|
|
17
17
|
ai: {
|
|
18
|
-
model: "gemini-2.
|
|
18
|
+
model: "gemini-2.5-flash",
|
|
19
19
|
maxTokens: 1000,
|
|
20
20
|
temperature: 0,
|
|
21
21
|
},
|
|
@@ -33,7 +33,7 @@ const config: AxCrewConfig = {
|
|
|
33
33
|
provider: "google-gemini",
|
|
34
34
|
providerKeyName: "GEMINI_API_KEY",
|
|
35
35
|
ai: {
|
|
36
|
-
model: "gemini-
|
|
36
|
+
model: "gemini-2.5-pro",
|
|
37
37
|
temperature: 0,
|
|
38
38
|
},
|
|
39
39
|
options: {
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@amitdeshmukh/ax-crew",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "6.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",
|
|
@@ -11,7 +11,7 @@
|
|
|
11
11
|
"scripts": {
|
|
12
12
|
"build": "rm -rf dist && tsc --outDir dist",
|
|
13
13
|
"release": "npm run build && npm publish --access public",
|
|
14
|
-
"test": "vitest",
|
|
14
|
+
"test": "vitest run",
|
|
15
15
|
"test:watch": "vitest watch",
|
|
16
16
|
"test:coverage": "vitest run --coverage",
|
|
17
17
|
"test:ui": "vitest --ui"
|
|
@@ -24,8 +24,8 @@
|
|
|
24
24
|
"uuid": "^10.0.0"
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
|
-
"@ax-llm/ax": "14.0.
|
|
28
|
-
"@ax-llm/ax-tools": "14.0.
|
|
27
|
+
"@ax-llm/ax": "^14.0.36",
|
|
28
|
+
"@ax-llm/ax-tools": "^14.0.36"
|
|
29
29
|
},
|
|
30
30
|
"devDependencies": {
|
|
31
31
|
"@testing-library/jest-dom": "^6.6.3",
|
|
@@ -1,20 +1,12 @@
|
|
|
1
|
-
|
|
2
|
-
// Import Ax factory and MCP transports (as exported by current package)
|
|
1
|
+
// Import Ax factory and MCP transports
|
|
3
2
|
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';
|
|
11
3
|
import type { AxFunction } from '@ax-llm/ax';
|
|
12
4
|
// STDIO transport from tools package
|
|
13
5
|
import { AxMCPStdioTransport } from '@ax-llm/ax-tools'
|
|
14
|
-
|
|
6
|
+
// Resolve env by provided key name
|
|
15
7
|
import type {
|
|
16
8
|
AgentConfig,
|
|
17
|
-
|
|
9
|
+
AxCrewConfig,
|
|
18
10
|
FunctionRegistryType,
|
|
19
11
|
MCPTransportConfig,
|
|
20
12
|
MCPStdioTransportConfig,
|
|
@@ -23,26 +15,6 @@ import type {
|
|
|
23
15
|
} from '../types.js';
|
|
24
16
|
import type { Provider } from '../types.js';
|
|
25
17
|
|
|
26
|
-
// Canonical provider slugs supported by ai() factory
|
|
27
|
-
const PROVIDER_CANONICAL = new Set([
|
|
28
|
-
'openai',
|
|
29
|
-
'anthropic',
|
|
30
|
-
'google-gemini',
|
|
31
|
-
'mistral',
|
|
32
|
-
'groq',
|
|
33
|
-
'cohere',
|
|
34
|
-
'together',
|
|
35
|
-
'deepseek',
|
|
36
|
-
'ollama',
|
|
37
|
-
'huggingface',
|
|
38
|
-
'openrouter',
|
|
39
|
-
'azure-openai',
|
|
40
|
-
'reka',
|
|
41
|
-
'x-grok'
|
|
42
|
-
]);
|
|
43
|
-
|
|
44
|
-
// Provider type lives in src/types.ts
|
|
45
|
-
|
|
46
18
|
// Type guard to check if config is stdio transport
|
|
47
19
|
export function isStdioTransport(config: MCPTransportConfig): config is MCPStdioTransportConfig {
|
|
48
20
|
return 'command' in config;
|
|
@@ -69,53 +41,7 @@ function isConstructor<T>(func: any): func is { new (...args: any[]): T } {
|
|
|
69
41
|
return typeof func === 'function' && 'prototype' in func && 'toFunction' in func.prototype;
|
|
70
42
|
}
|
|
71
43
|
|
|
72
|
-
|
|
73
|
-
* Provides a user-friendly error message for JSON parsing errors
|
|
74
|
-
*/
|
|
75
|
-
const getFormattedJSONError = (error: Error, fileContents: string): string => {
|
|
76
|
-
if (error instanceof SyntaxError) {
|
|
77
|
-
const match = error.message.match(/position (\d+)/);
|
|
78
|
-
const position = match ? parseInt(match[1]) : -1;
|
|
79
|
-
|
|
80
|
-
if (position !== -1) {
|
|
81
|
-
const lines = fileContents.split('\n');
|
|
82
|
-
let currentPos = 0;
|
|
83
|
-
let errorLine = 0;
|
|
84
|
-
let errorColumn = 0;
|
|
85
|
-
|
|
86
|
-
// Find the line and column of the error
|
|
87
|
-
for (let i = 0; i < lines.length; i++) {
|
|
88
|
-
if (currentPos + lines[i].length >= position) {
|
|
89
|
-
errorLine = i + 1;
|
|
90
|
-
errorColumn = position - currentPos + 1;
|
|
91
|
-
break;
|
|
92
|
-
}
|
|
93
|
-
currentPos += lines[i].length + 1; // +1 for the newline character
|
|
94
|
-
}
|
|
95
|
-
|
|
96
|
-
const contextLines = lines.slice(Math.max(0, errorLine - 3), errorLine + 2)
|
|
97
|
-
.map((line, idx) => `${errorLine - 2 + idx}: ${line}`).join('\n');
|
|
98
|
-
|
|
99
|
-
return `JSON Parse Error in your agent configuration:
|
|
100
|
-
|
|
101
|
-
Error near line ${errorLine}, column ${errorColumn}
|
|
102
|
-
|
|
103
|
-
Context:
|
|
104
|
-
${contextLines}
|
|
105
|
-
|
|
106
|
-
Common issues to check:
|
|
107
|
-
- Missing or extra commas between properties
|
|
108
|
-
- Missing quotes around property names
|
|
109
|
-
- Unmatched brackets or braces
|
|
110
|
-
- Invalid JSON values
|
|
111
|
-
- Trailing commas (not allowed in JSON)
|
|
112
|
-
|
|
113
|
-
Original error: ${error.message}`;
|
|
114
|
-
}
|
|
115
|
-
}
|
|
116
|
-
|
|
117
|
-
return `Error parsing agent configuration: ${error.message}`;
|
|
118
|
-
};
|
|
44
|
+
// Removed file/JSON parse helpers to keep browser-safe
|
|
119
45
|
|
|
120
46
|
const initializeMCPServers = async (agentConfigData: AgentConfig): Promise<AxFunction[]> => {
|
|
121
47
|
const mcpServers = agentConfigData.mcpServers;
|
|
@@ -158,32 +84,16 @@ const initializeMCPServers = async (agentConfigData: AgentConfig): Promise<AxFun
|
|
|
158
84
|
};
|
|
159
85
|
|
|
160
86
|
/**
|
|
161
|
-
*
|
|
162
|
-
* @param {
|
|
87
|
+
* Returns the AxCrew config from a direct JSON object. Browser-safe.
|
|
88
|
+
* @param {CrewConfig} input - A JSON object with crew configuration.
|
|
163
89
|
* @returns {Object} The parsed crew config.
|
|
164
90
|
* @throws Will throw an error if reading/parsing fails.
|
|
165
91
|
*/
|
|
166
|
-
const parseCrewConfig = (input:
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
// Handle file path input
|
|
170
|
-
const fileContents = fs.readFileSync(input, 'utf8');
|
|
171
|
-
const parsedConfig = JSON.parse(fileContents) as { crew: AgentConfig[] };
|
|
172
|
-
return parsedConfig;
|
|
173
|
-
} else {
|
|
174
|
-
// Handle direct JSON object input
|
|
175
|
-
return input;
|
|
176
|
-
}
|
|
177
|
-
} catch (e) {
|
|
178
|
-
if (e instanceof Error) {
|
|
179
|
-
if (typeof input === 'string') {
|
|
180
|
-
const formattedError = getFormattedJSONError(e, fs.readFileSync(input, 'utf8'));
|
|
181
|
-
throw new Error(formattedError);
|
|
182
|
-
}
|
|
183
|
-
throw new Error(`Error parsing agent configuration: ${e.message}`);
|
|
184
|
-
}
|
|
185
|
-
throw e;
|
|
92
|
+
const parseCrewConfig = (input: AxCrewConfig): { crew: AgentConfig[] } => {
|
|
93
|
+
if (!input || typeof input !== 'object' || !Array.isArray((input as any).crew)) {
|
|
94
|
+
throw new Error('Invalid crew configuration: expected an object with a crew array');
|
|
186
95
|
}
|
|
96
|
+
return input as { crew: AgentConfig[] };
|
|
187
97
|
};
|
|
188
98
|
|
|
189
99
|
/**
|
|
@@ -191,7 +101,7 @@ const parseCrewConfig = (input: CrewConfigInput): { crew: AgentConfig[] } => {
|
|
|
191
101
|
* and creates an instance of the Agent with the appropriate settings.
|
|
192
102
|
*
|
|
193
103
|
* @param {string} agentName - The identifier for the AI agent to be initialized.
|
|
194
|
-
* @param {
|
|
104
|
+
* @param {AxCrewConfig} crewConfig - A JSON object with crew configuration.
|
|
195
105
|
* @param {FunctionRegistryType} functions - The functions available to the agent.
|
|
196
106
|
* @param {Object} state - The state object for the agent.
|
|
197
107
|
* @returns {Object} An object containing the Agents AI instance, its name, description, signature, functions and subAgentList.
|
|
@@ -200,7 +110,7 @@ const parseCrewConfig = (input: CrewConfigInput): { crew: AgentConfig[] } => {
|
|
|
200
110
|
*/
|
|
201
111
|
const parseAgentConfig = async (
|
|
202
112
|
agentName: string,
|
|
203
|
-
crewConfig:
|
|
113
|
+
crewConfig: AxCrewConfig,
|
|
204
114
|
functions: FunctionRegistryType,
|
|
205
115
|
state: Record<string, any>
|
|
206
116
|
) => {
|
|
@@ -211,20 +121,17 @@ const parseAgentConfig = async (
|
|
|
211
121
|
throw new Error(`AI agent with name ${agentName} is not configured`);
|
|
212
122
|
}
|
|
213
123
|
|
|
214
|
-
//
|
|
215
|
-
const lower = agentConfigData.provider.toLowerCase();
|
|
216
|
-
if (!PROVIDER_CANONICAL.has(lower)) {
|
|
217
|
-
throw new Error(`AI provider ${agentConfigData.provider} is not supported. Use one of: ${Array.from(PROVIDER_CANONICAL).join(', ')}`);
|
|
218
|
-
}
|
|
124
|
+
// Normalize provider slug to lowercase and validate via Ax factory
|
|
125
|
+
const lower = String(agentConfigData.provider).toLowerCase() as Provider;
|
|
219
126
|
const provider = lower as Provider;
|
|
220
127
|
|
|
221
|
-
//
|
|
128
|
+
// Resolve API key from user-supplied environment variable name
|
|
222
129
|
let apiKey = '';
|
|
223
130
|
if (agentConfigData.providerKeyName) {
|
|
224
|
-
|
|
225
|
-
|
|
131
|
+
const keyName = agentConfigData.providerKeyName;
|
|
132
|
+
apiKey = resolveApiKey(keyName) || '';
|
|
226
133
|
if (!apiKey) {
|
|
227
|
-
throw new Error(`API key for provider ${agentConfigData.provider} is not set in environment
|
|
134
|
+
throw new Error(`API key '${keyName}' for provider ${agentConfigData.provider} is not set in environment`);
|
|
228
135
|
}
|
|
229
136
|
} else {
|
|
230
137
|
throw new Error(`Provider key name is missing in the agent configuration`);
|
|
@@ -253,37 +160,19 @@ const parseAgentConfig = async (
|
|
|
253
160
|
throw new Error(`Invalid apiURL provided: ${agentConfigData.apiURL}`);
|
|
254
161
|
}
|
|
255
162
|
}
|
|
256
|
-
// Forward provider-specific arguments
|
|
163
|
+
// Forward provider-specific arguments as-is; let Ax validate/ignore as needed
|
|
257
164
|
const providerArgs = (agentConfigData as any).providerArgs;
|
|
258
|
-
if (
|
|
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
|
|
165
|
+
if (providerArgs && typeof providerArgs === 'object') {
|
|
284
166
|
Object.assign(aiArgs, providerArgs);
|
|
285
167
|
}
|
|
286
|
-
|
|
168
|
+
// Validate provider by attempting instantiation; Ax will throw on unknown providers
|
|
169
|
+
let aiInstance;
|
|
170
|
+
try {
|
|
171
|
+
aiInstance = ai(aiArgs);
|
|
172
|
+
} catch (e) {
|
|
173
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
174
|
+
throw new Error(`Unsupported provider '${provider}': ${msg}`);
|
|
175
|
+
}
|
|
287
176
|
|
|
288
177
|
// If an mcpServers config is provided in the agent config, convert to functions
|
|
289
178
|
const mcpFunctions = await initializeMCPServers(agentConfigData);
|
|
@@ -328,4 +217,21 @@ const parseAgentConfig = async (
|
|
|
328
217
|
export {
|
|
329
218
|
parseAgentConfig,
|
|
330
219
|
parseCrewConfig
|
|
331
|
-
};
|
|
220
|
+
};
|
|
221
|
+
|
|
222
|
+
function resolveApiKey(varName: string): string | undefined {
|
|
223
|
+
try {
|
|
224
|
+
// Prefer Node env when available
|
|
225
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
226
|
+
// @ts-ignore
|
|
227
|
+
if (typeof process !== 'undefined' && process?.env) {
|
|
228
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
229
|
+
// @ts-ignore
|
|
230
|
+
return process.env[varName];
|
|
231
|
+
}
|
|
232
|
+
// Fallback: allow global exposure in browser builds (e.g., injected at runtime)
|
|
233
|
+
return (globalThis as any)?.[varName];
|
|
234
|
+
} catch {
|
|
235
|
+
return undefined;
|
|
236
|
+
}
|
|
237
|
+
}
|