@amitdeshmukh/ax-crew 5.0.0 → 7.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/.claude/settings.local.json +7 -0
- package/CHANGELOG.md +40 -0
- package/README.md +143 -59
- package/dist/agents/agentConfig.d.ts +6 -6
- package/dist/agents/agentConfig.js +46 -124
- package/dist/agents/compose.d.ts +15 -0
- package/dist/agents/compose.js +58 -0
- package/dist/agents/index.d.ts +22 -4
- package/dist/agents/index.js +24 -5
- 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 +53 -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/examples/telemetry-demo.ts +166 -0
- package/package.json +5 -4
- package/src/agents/agentConfig.ts +53 -142
- package/src/agents/compose.ts +80 -0
- package/src/agents/index.ts +30 -8
- package/src/functions/index.ts +11 -1
- package/src/index.ts +25 -11
- package/src/types.ts +59 -21
- package/tests/telemetry.test.ts +81 -0
- package/dist/config/index.d.ts +0 -5
- package/dist/config/index.js +0 -30
- package/src/config/index.ts +0 -40
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,45 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## [7.0.0] - 2025-12-27
|
|
4
|
+
|
|
5
|
+
### Breaking
|
|
6
|
+
- Change to `AxCrewConfig` interface
|
|
7
|
+
|
|
8
|
+
### Added
|
|
9
|
+
- Optional OpenTelemetry telemetry support for distributed tracing and metrics collection
|
|
10
|
+
- New `AxCrewOptions` interface with `telemetry` field accepting `tracer` and `meter` instances
|
|
11
|
+
- Automatic instrumentation of agent execution, function calls, and token metrics when telemetry is enabled
|
|
12
|
+
- Support for multiple OpenTelemetry exporters (console, Jaeger, Prometheus, etc.)
|
|
13
|
+
- Complete telemetry example in `examples/telemetry-demo.ts` demonstrating setup with console and Jaeger exporters
|
|
14
|
+
- Comprehensive telemetry documentation in README with setup instructions, best practices, and examples
|
|
15
|
+
- Test coverage for telemetry functionality in `tests/telemetry.test.ts`
|
|
16
|
+
|
|
17
|
+
### Changed
|
|
18
|
+
- AxCrew constructor now accepts optional fourth parameter `options` of type `AxCrewOptions` for telemetry configuration
|
|
19
|
+
- Enhanced agent initialization to pass telemetry context to underlying agents
|
|
20
|
+
|
|
21
|
+
## [6.0.0] - 2025-10-22
|
|
22
|
+
|
|
23
|
+
### Breaking
|
|
24
|
+
- Config is now object-only to be browser-safe. File-path based configs are no longer supported. Load JSON yourself and pass `{ crew: [...] }`.
|
|
25
|
+
|
|
26
|
+
### Added
|
|
27
|
+
- Runtime API key resolution via `providerKeyName`. Keys are read from `process.env[providerKeyName]` in Node or `globalThis[providerKeyName]` in the browser.
|
|
28
|
+
- Updated examples to use `dotenv/config` to ensure keys are available at runtime.
|
|
29
|
+
|
|
30
|
+
### Changed
|
|
31
|
+
- Removed reliance on a hardcoded `PROVIDER_API_KEYS` map; providers read secrets using the user-supplied env var name.
|
|
32
|
+
- `providerArgs` are forwarded unchanged to Ax; validation and semantics are handled by Ax provider implementations.
|
|
33
|
+
- Docs updated: object-only config, env resolution behavior, providerArgs guidance.
|
|
34
|
+
|
|
35
|
+
### Removed
|
|
36
|
+
- Hardcoded provider lists/registries and the old `providers.ts` list file.
|
|
37
|
+
- Temporary removal of router/balancer helpers and related examples to simplify this refactor.
|
|
38
|
+
|
|
39
|
+
### Migration notes
|
|
40
|
+
- If you previously passed a config file path, read the file yourself and pass the parsed object to `new AxCrew(config)`.
|
|
41
|
+
- Set `providerKeyName` to the exact environment variable name holding the API key. Ensure env is loaded before constructing the crew (e.g., `import 'dotenv/config'`).
|
|
42
|
+
|
|
3
43
|
## [5.0.0] - 2025-10-16
|
|
4
44
|
|
|
5
45
|
### Added
|
package/README.md
CHANGED
|
@@ -1,5 +1,7 @@
|
|
|
1
1
|

|
|
2
2
|
|
|
3
|
+
[](https://www.npmjs.com/package/@amitdeshmukh/ax-crew) [](https://www.npmjs.com/package/@amitdeshmukh/ax-crew)
|
|
4
|
+
|
|
3
5
|
### AxCrew — build a crew of AI agents with shared state (powered by AxLLM)
|
|
4
6
|
|
|
5
7
|
AxCrew lets you define a team of AI agents in config and run them together with shared state, tools, streaming, MCP, and built‑in metrics/cost tracking. Bring your own functions or use the provided registry.
|
|
@@ -33,43 +35,17 @@ ANTHROPIC_API_KEY=...
|
|
|
33
35
|
OPENAI_API_KEY=...
|
|
34
36
|
AZURE_OPENAI_API_KEY=...
|
|
35
37
|
```
|
|
36
|
-
In each agent config, set `providerKeyName` to the env var name.
|
|
38
|
+
In each agent config, set `providerKeyName` to the env var name. AxCrew resolves the key at runtime by reading `process.env[providerKeyName]` in Node or `globalThis[providerKeyName]` in the browser. Ensure your env is loaded (for example, import `dotenv/config` in Node) before creating the crew.
|
|
37
39
|
|
|
38
40
|
#### Provider-specific arguments (`providerArgs`)
|
|
39
|
-
Some providers require extra top-level configuration beyond `ai.model` (
|
|
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
|
-
```
|
|
41
|
+
Some providers require extra top-level configuration beyond `ai.model` (for example, Azure deployment details or Vertex AI project/region). Pass these via `providerArgs`. AxCrew forwards `providerArgs` to Ax unchanged; validation and semantics are handled by Ax. Refer to Ax provider docs for supported fields.
|
|
64
42
|
|
|
65
43
|
### Quickstart
|
|
66
|
-
This package includes TypeScript declarations and provides type safety.
|
|
67
44
|
|
|
68
45
|
```typescript
|
|
69
46
|
import { AxCrew, AxCrewFunctions, FunctionRegistryType, StateInstance } from '@amitdeshmukh/ax-crew';
|
|
70
47
|
import type { AxFunction } from '@ax-llm/ax';
|
|
71
48
|
|
|
72
|
-
// Type-safe configuration
|
|
73
49
|
const config = {
|
|
74
50
|
crew: [{
|
|
75
51
|
name: "Planner",
|
|
@@ -156,25 +132,7 @@ Key TypeScript features:
|
|
|
156
132
|
## Usage
|
|
157
133
|
|
|
158
134
|
### Initializing a Crew
|
|
159
|
-
|
|
160
|
-
|
|
161
|
-
1. Using a JSON configuration file that defines the agents in the crew, along with their individual configurations.
|
|
162
|
-
2. Directly passing a JSON object with the crew configuration.
|
|
163
|
-
|
|
164
|
-
#### Using a Configuration File
|
|
165
|
-
See [agentConfig.json](agentConfig.json) for an example configuration file.
|
|
166
|
-
|
|
167
|
-
```javascript
|
|
168
|
-
// Import the AxCrew class
|
|
169
|
-
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
170
|
-
|
|
171
|
-
// Create a new instance of AxCrew using a config file
|
|
172
|
-
const configFilePath = './agentConfig.json';
|
|
173
|
-
const crew = new AxCrew(configFilePath);
|
|
174
|
-
```
|
|
175
|
-
|
|
176
|
-
#### Using a Direct Configuration Object
|
|
177
|
-
You can also pass the configuration directly as a JSON object:
|
|
135
|
+
Pass a JSON object to the `AxCrew` constructor:
|
|
178
136
|
|
|
179
137
|
```javascript
|
|
180
138
|
// Import the AxCrew class
|
|
@@ -205,15 +163,7 @@ const config = {
|
|
|
205
163
|
const crew = new AxCrew(config);
|
|
206
164
|
```
|
|
207
165
|
|
|
208
|
-
|
|
209
|
-
- Use a configuration file when you want to:
|
|
210
|
-
- Keep your configuration separate from your code
|
|
211
|
-
- Share configurations across different projects
|
|
212
|
-
- Version control your configurations
|
|
213
|
-
- Use a direct configuration object when you want to:
|
|
214
|
-
- Generate configurations dynamically
|
|
215
|
-
- Modify configurations at runtime
|
|
216
|
-
- Keep everything in one file for simpler projects
|
|
166
|
+
AxCrew no longer supports reading from a configuration file to remain browser-compatible.
|
|
217
167
|
|
|
218
168
|
### Agent Persona: definition and prompt
|
|
219
169
|
|
|
@@ -417,7 +367,7 @@ An example of how to complete a task using the agents is shown below. The `Plann
|
|
|
417
367
|
import { AxCrew, AxCrewFunctions } from '@amitdeshmukh/ax-crew';
|
|
418
368
|
|
|
419
369
|
// Create a new instance of AxCrew
|
|
420
|
-
const crew = new AxCrew(
|
|
370
|
+
const crew = new AxCrew(config, AxCrewFunctions);
|
|
421
371
|
crew.addAgentsToCrew(['Planner', 'Calculator', 'Manager']);
|
|
422
372
|
|
|
423
373
|
// Get agent instances
|
|
@@ -449,7 +399,7 @@ The package supports streaming responses from agents, allowing you to receive an
|
|
|
449
399
|
import { AxCrew, AxCrewFunctions } from '@amitdeshmukh/ax-crew';
|
|
450
400
|
|
|
451
401
|
// Create and initialize crew as shown above
|
|
452
|
-
const crew = new AxCrew(
|
|
402
|
+
const crew = new AxCrew(config, AxCrewFunctions);
|
|
453
403
|
await crew.addAgentsToCrew(['Planner']);
|
|
454
404
|
|
|
455
405
|
const planner = crew.agents.get("Planner");
|
|
@@ -666,7 +616,7 @@ MCP functions are automatically available to agents once the servers are configu
|
|
|
666
616
|
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
667
617
|
|
|
668
618
|
// Create crew with MCP-enabled agents
|
|
669
|
-
const crew = new AxCrew(
|
|
619
|
+
const crew = new AxCrew(config);
|
|
670
620
|
await crew.addAgent('DataAnalyst'); // Agent with MCP servers configured
|
|
671
621
|
|
|
672
622
|
const analyst = crew.agents.get('DataAnalyst');
|
|
@@ -767,6 +717,140 @@ Notes:
|
|
|
767
717
|
- Legacy cost APIs (`getLastUsageCost`, `getAccumulatedCosts`, `getAggregatedCosts`) are superseded by metrics methods.
|
|
768
718
|
- Estimated cost values are numbers rounded to 5 decimal places.
|
|
769
719
|
|
|
720
|
+
### Telemetry Support (OpenTelemetry)
|
|
721
|
+
|
|
722
|
+
AxCrew provides optional OpenTelemetry integration for comprehensive observability. You can pass custom tracer and meter instances to monitor agent operations, track performance, and analyze behavior across your crew.
|
|
723
|
+
|
|
724
|
+
#### Features
|
|
725
|
+
|
|
726
|
+
- **Distributed Tracing**: Track agent execution flows, function calls, and dependencies
|
|
727
|
+
- **Metrics Collection**: Monitor token usage, costs, latency, and error rates
|
|
728
|
+
- **Multiple Exporters**: Support for console, Jaeger, Prometheus, and other OpenTelemetry backends
|
|
729
|
+
|
|
730
|
+
#### Setup
|
|
731
|
+
|
|
732
|
+
Install OpenTelemetry dependencies:
|
|
733
|
+
|
|
734
|
+
```bash
|
|
735
|
+
npm install @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/sdk-metrics
|
|
736
|
+
```
|
|
737
|
+
|
|
738
|
+
Optional: Install exporters for enhanced visualization:
|
|
739
|
+
|
|
740
|
+
```bash
|
|
741
|
+
# For Jaeger tracing UI
|
|
742
|
+
npm install @opentelemetry/exporter-jaeger
|
|
743
|
+
|
|
744
|
+
# For Prometheus metrics
|
|
745
|
+
npm install @opentelemetry/exporter-prometheus
|
|
746
|
+
```
|
|
747
|
+
|
|
748
|
+
#### Basic Configuration
|
|
749
|
+
|
|
750
|
+
```typescript
|
|
751
|
+
import { AxCrew } from '@amitdeshmukh/ax-crew';
|
|
752
|
+
import { trace, metrics } from '@opentelemetry/api';
|
|
753
|
+
import { NodeTracerProvider } from '@opentelemetry/sdk-trace-node';
|
|
754
|
+
import { MeterProvider } from '@opentelemetry/sdk-metrics';
|
|
755
|
+
import { ConsoleSpanExporter, SimpleSpanProcessor } from '@opentelemetry/sdk-trace-base';
|
|
756
|
+
|
|
757
|
+
// Initialize OpenTelemetry
|
|
758
|
+
const tracerProvider = new NodeTracerProvider({
|
|
759
|
+
spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())]
|
|
760
|
+
});
|
|
761
|
+
tracerProvider.register();
|
|
762
|
+
|
|
763
|
+
const meterProvider = new MeterProvider();
|
|
764
|
+
metrics.setGlobalMeterProvider(meterProvider);
|
|
765
|
+
|
|
766
|
+
// Get tracer and meter instances
|
|
767
|
+
const tracer = trace.getTracer('my-app');
|
|
768
|
+
const meter = metrics.getMeter('my-app');
|
|
769
|
+
|
|
770
|
+
// Pass to AxCrew
|
|
771
|
+
const crew = new AxCrew(
|
|
772
|
+
config,
|
|
773
|
+
AxCrewFunctions,
|
|
774
|
+
undefined,
|
|
775
|
+
{
|
|
776
|
+
telemetry: {
|
|
777
|
+
tracer,
|
|
778
|
+
meter
|
|
779
|
+
}
|
|
780
|
+
}
|
|
781
|
+
);
|
|
782
|
+
```
|
|
783
|
+
|
|
784
|
+
#### What Gets Traced
|
|
785
|
+
|
|
786
|
+
When telemetry is enabled, AxCrew automatically instruments:
|
|
787
|
+
|
|
788
|
+
- **Agent Execution**: Each agent's `forward()` call creates a span with timing and metadata
|
|
789
|
+
- **Function Calls**: Tool/function invocations are traced with parameters and results
|
|
790
|
+
- **Provider Information**: Model name, provider, and configuration details
|
|
791
|
+
- **Token Metrics**: Input/output tokens and estimated costs
|
|
792
|
+
- **Errors**: Exceptions and failures are captured with full context
|
|
793
|
+
|
|
794
|
+
#### Advanced Configuration with Jaeger
|
|
795
|
+
|
|
796
|
+
For enhanced visualization, you can export traces to Jaeger:
|
|
797
|
+
|
|
798
|
+
```typescript
|
|
799
|
+
import { JaegerExporter } from '@opentelemetry/exporter-jaeger';
|
|
800
|
+
|
|
801
|
+
const tracerProvider = new NodeTracerProvider({
|
|
802
|
+
spanProcessors: [
|
|
803
|
+
new SimpleSpanProcessor(new ConsoleSpanExporter()),
|
|
804
|
+
new SimpleSpanProcessor(new JaegerExporter({
|
|
805
|
+
endpoint: 'http://localhost:14268/api/traces'
|
|
806
|
+
}))
|
|
807
|
+
]
|
|
808
|
+
});
|
|
809
|
+
tracerProvider.register();
|
|
810
|
+
|
|
811
|
+
// ... rest of setup
|
|
812
|
+
```
|
|
813
|
+
|
|
814
|
+
Start Jaeger with Docker:
|
|
815
|
+
|
|
816
|
+
```bash
|
|
817
|
+
docker run -d --name jaeger \
|
|
818
|
+
-p 16686:16686 \
|
|
819
|
+
-p 14268:14268 \
|
|
820
|
+
jaegertracing/all-in-one:latest
|
|
821
|
+
```
|
|
822
|
+
|
|
823
|
+
View traces at: http://localhost:16686
|
|
824
|
+
|
|
825
|
+
#### Complete Example
|
|
826
|
+
|
|
827
|
+
See [examples/telemetry-demo.ts](examples/telemetry-demo.ts) for a full working example that demonstrates:
|
|
828
|
+
|
|
829
|
+
- Setting up OpenTelemetry with console and Jaeger exporters
|
|
830
|
+
- Configuring multiple agents with different providers
|
|
831
|
+
- Running a multi-step workflow with telemetry tracking
|
|
832
|
+
- Viewing traces and metrics in the console and Jaeger UI
|
|
833
|
+
|
|
834
|
+
Run the example:
|
|
835
|
+
|
|
836
|
+
```bash
|
|
837
|
+
# With console output only
|
|
838
|
+
npm run dev examples/telemetry-demo.ts
|
|
839
|
+
|
|
840
|
+
# With Jaeger (start Jaeger first)
|
|
841
|
+
docker run -d --name jaeger -p 16686:16686 -p 14268:14268 jaegertracing/all-in-one:latest
|
|
842
|
+
npm run dev examples/telemetry-demo.ts
|
|
843
|
+
# Open http://localhost:16686 to view traces
|
|
844
|
+
```
|
|
845
|
+
|
|
846
|
+
#### Best Practices
|
|
847
|
+
|
|
848
|
+
1. **Production Setup**: Use appropriate exporters for your infrastructure (Jaeger, Zipkin, Cloud providers)
|
|
849
|
+
2. **Sampling**: Configure sampling strategies to control trace volume in production
|
|
850
|
+
3. **Context Propagation**: OpenTelemetry automatically propagates trace context across agent calls
|
|
851
|
+
4. **Custom Attributes**: Extend traces with custom attributes specific to your use case
|
|
852
|
+
5. **Performance**: Telemetry adds minimal overhead when properly configured
|
|
853
|
+
|
|
770
854
|
## Changelog
|
|
771
855
|
|
|
772
856
|
See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version updates.
|
|
@@ -1,16 +1,16 @@
|
|
|
1
1
|
import { AxDefaultCostTracker } from '@ax-llm/ax';
|
|
2
2
|
import type { AxFunction } from '@ax-llm/ax';
|
|
3
|
-
import type { AgentConfig,
|
|
3
|
+
import type { AgentConfig, AxCrewConfig, AxCrewOptions, FunctionRegistryType, MCPTransportConfig, MCPStdioTransportConfig, MCPHTTPSSETransportConfig, MCPStreamableHTTPTransportConfig } from '../types.js';
|
|
4
4
|
export declare function isStdioTransport(config: MCPTransportConfig): config is MCPStdioTransportConfig;
|
|
5
5
|
export declare function isHTTPSSETransport(config: MCPTransportConfig): config is MCPHTTPSSETransportConfig;
|
|
6
6
|
export declare function isStreambleHTTPTransport(config: MCPTransportConfig): config is MCPStreamableHTTPTransportConfig;
|
|
7
7
|
/**
|
|
8
|
-
*
|
|
9
|
-
* @param {
|
|
8
|
+
* Returns the AxCrew config from a direct JSON object. Browser-safe.
|
|
9
|
+
* @param {CrewConfig} input - A JSON object with crew configuration.
|
|
10
10
|
* @returns {Object} The parsed crew config.
|
|
11
11
|
* @throws Will throw an error if reading/parsing fails.
|
|
12
12
|
*/
|
|
13
|
-
declare const parseCrewConfig: (input:
|
|
13
|
+
declare const parseCrewConfig: (input: AxCrewConfig) => {
|
|
14
14
|
crew: AgentConfig[];
|
|
15
15
|
};
|
|
16
16
|
/**
|
|
@@ -18,14 +18,14 @@ declare const parseCrewConfig: (input: CrewConfigInput) => {
|
|
|
18
18
|
* and creates an instance of the Agent with the appropriate settings.
|
|
19
19
|
*
|
|
20
20
|
* @param {string} agentName - The identifier for the AI agent to be initialized.
|
|
21
|
-
* @param {
|
|
21
|
+
* @param {AxCrewConfig} crewConfig - A JSON object with crew configuration.
|
|
22
22
|
* @param {FunctionRegistryType} functions - The functions available to the agent.
|
|
23
23
|
* @param {Object} state - The state object for the agent.
|
|
24
24
|
* @returns {Object} An object containing the Agents AI instance, its name, description, signature, functions and subAgentList.
|
|
25
25
|
* @throws {Error} Throws an error if the agent configuration is missing, the provider is unsupported,
|
|
26
26
|
* the API key is not found, or the provider key name is not specified in the configuration.
|
|
27
27
|
*/
|
|
28
|
-
declare const parseAgentConfig: (agentName: string, crewConfig:
|
|
28
|
+
declare const parseAgentConfig: (agentName: string, crewConfig: AxCrewConfig, functions: FunctionRegistryType, state: Record<string, any>, options?: AxCrewOptions) => Promise<{
|
|
29
29
|
ai: import("@ax-llm/ax").AxAI<string>;
|
|
30
30
|
name: string;
|
|
31
31
|
description: string;
|
|
@@ -1,27 +1,7 @@
|
|
|
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
3
|
// STDIO transport from tools package
|
|
5
4
|
import { AxMCPStdioTransport } from '@ax-llm/ax-tools';
|
|
6
|
-
import { PROVIDER_API_KEYS } from '../config/index.js';
|
|
7
|
-
// Canonical provider slugs supported by ai() factory
|
|
8
|
-
const PROVIDER_CANONICAL = new Set([
|
|
9
|
-
'openai',
|
|
10
|
-
'anthropic',
|
|
11
|
-
'google-gemini',
|
|
12
|
-
'mistral',
|
|
13
|
-
'groq',
|
|
14
|
-
'cohere',
|
|
15
|
-
'together',
|
|
16
|
-
'deepseek',
|
|
17
|
-
'ollama',
|
|
18
|
-
'huggingface',
|
|
19
|
-
'openrouter',
|
|
20
|
-
'azure-openai',
|
|
21
|
-
'reka',
|
|
22
|
-
'x-grok'
|
|
23
|
-
]);
|
|
24
|
-
// Provider type lives in src/types.ts
|
|
25
5
|
// Type guard to check if config is stdio transport
|
|
26
6
|
export function isStdioTransport(config) {
|
|
27
7
|
return 'command' in config;
|
|
@@ -44,48 +24,7 @@ export function isStreambleHTTPTransport(config) {
|
|
|
44
24
|
function isConstructor(func) {
|
|
45
25
|
return typeof func === 'function' && 'prototype' in func && 'toFunction' in func.prototype;
|
|
46
26
|
}
|
|
47
|
-
|
|
48
|
-
* Provides a user-friendly error message for JSON parsing errors
|
|
49
|
-
*/
|
|
50
|
-
const getFormattedJSONError = (error, fileContents) => {
|
|
51
|
-
if (error instanceof SyntaxError) {
|
|
52
|
-
const match = error.message.match(/position (\d+)/);
|
|
53
|
-
const position = match ? parseInt(match[1]) : -1;
|
|
54
|
-
if (position !== -1) {
|
|
55
|
-
const lines = fileContents.split('\n');
|
|
56
|
-
let currentPos = 0;
|
|
57
|
-
let errorLine = 0;
|
|
58
|
-
let errorColumn = 0;
|
|
59
|
-
// Find the line and column of the error
|
|
60
|
-
for (let i = 0; i < lines.length; i++) {
|
|
61
|
-
if (currentPos + lines[i].length >= position) {
|
|
62
|
-
errorLine = i + 1;
|
|
63
|
-
errorColumn = position - currentPos + 1;
|
|
64
|
-
break;
|
|
65
|
-
}
|
|
66
|
-
currentPos += lines[i].length + 1; // +1 for the newline character
|
|
67
|
-
}
|
|
68
|
-
const contextLines = lines.slice(Math.max(0, errorLine - 3), errorLine + 2)
|
|
69
|
-
.map((line, idx) => `${errorLine - 2 + idx}: ${line}`).join('\n');
|
|
70
|
-
return `JSON Parse Error in your agent configuration:
|
|
71
|
-
|
|
72
|
-
Error near line ${errorLine}, column ${errorColumn}
|
|
73
|
-
|
|
74
|
-
Context:
|
|
75
|
-
${contextLines}
|
|
76
|
-
|
|
77
|
-
Common issues to check:
|
|
78
|
-
- Missing or extra commas between properties
|
|
79
|
-
- Missing quotes around property names
|
|
80
|
-
- Unmatched brackets or braces
|
|
81
|
-
- Invalid JSON values
|
|
82
|
-
- Trailing commas (not allowed in JSON)
|
|
83
|
-
|
|
84
|
-
Original error: ${error.message}`;
|
|
85
|
-
}
|
|
86
|
-
}
|
|
87
|
-
return `Error parsing agent configuration: ${error.message}`;
|
|
88
|
-
};
|
|
27
|
+
// Removed file/JSON parse helpers to keep browser-safe
|
|
89
28
|
const initializeMCPServers = async (agentConfigData) => {
|
|
90
29
|
const mcpServers = agentConfigData.mcpServers;
|
|
91
30
|
if (!mcpServers || Object.keys(mcpServers).length === 0) {
|
|
@@ -126,66 +65,46 @@ const initializeMCPServers = async (agentConfigData) => {
|
|
|
126
65
|
}
|
|
127
66
|
};
|
|
128
67
|
/**
|
|
129
|
-
*
|
|
130
|
-
* @param {
|
|
68
|
+
* Returns the AxCrew config from a direct JSON object. Browser-safe.
|
|
69
|
+
* @param {CrewConfig} input - A JSON object with crew configuration.
|
|
131
70
|
* @returns {Object} The parsed crew config.
|
|
132
71
|
* @throws Will throw an error if reading/parsing fails.
|
|
133
72
|
*/
|
|
134
73
|
const parseCrewConfig = (input) => {
|
|
135
|
-
|
|
136
|
-
|
|
137
|
-
// Handle file path input
|
|
138
|
-
const fileContents = fs.readFileSync(input, 'utf8');
|
|
139
|
-
const parsedConfig = JSON.parse(fileContents);
|
|
140
|
-
return parsedConfig;
|
|
141
|
-
}
|
|
142
|
-
else {
|
|
143
|
-
// Handle direct JSON object input
|
|
144
|
-
return input;
|
|
145
|
-
}
|
|
146
|
-
}
|
|
147
|
-
catch (e) {
|
|
148
|
-
if (e instanceof Error) {
|
|
149
|
-
if (typeof input === 'string') {
|
|
150
|
-
const formattedError = getFormattedJSONError(e, fs.readFileSync(input, 'utf8'));
|
|
151
|
-
throw new Error(formattedError);
|
|
152
|
-
}
|
|
153
|
-
throw new Error(`Error parsing agent configuration: ${e.message}`);
|
|
154
|
-
}
|
|
155
|
-
throw e;
|
|
74
|
+
if (!input || typeof input !== 'object' || !Array.isArray(input.crew)) {
|
|
75
|
+
throw new Error('Invalid crew configuration: expected an object with a crew array');
|
|
156
76
|
}
|
|
77
|
+
return input;
|
|
157
78
|
};
|
|
158
79
|
/**
|
|
159
80
|
* Parses the agent's configuration, validates the presence of the necessary API key,
|
|
160
81
|
* and creates an instance of the Agent with the appropriate settings.
|
|
161
82
|
*
|
|
162
83
|
* @param {string} agentName - The identifier for the AI agent to be initialized.
|
|
163
|
-
* @param {
|
|
84
|
+
* @param {AxCrewConfig} crewConfig - A JSON object with crew configuration.
|
|
164
85
|
* @param {FunctionRegistryType} functions - The functions available to the agent.
|
|
165
86
|
* @param {Object} state - The state object for the agent.
|
|
166
87
|
* @returns {Object} An object containing the Agents AI instance, its name, description, signature, functions and subAgentList.
|
|
167
88
|
* @throws {Error} Throws an error if the agent configuration is missing, the provider is unsupported,
|
|
168
89
|
* the API key is not found, or the provider key name is not specified in the configuration.
|
|
169
90
|
*/
|
|
170
|
-
const parseAgentConfig = async (agentName, crewConfig, functions, state) => {
|
|
91
|
+
const parseAgentConfig = async (agentName, crewConfig, functions, state, options) => {
|
|
171
92
|
try {
|
|
172
93
|
// Retrieve the parameters for the specified AI agent from config
|
|
173
94
|
const agentConfigData = parseCrewConfig(crewConfig).crew.find(agent => agent.name === agentName);
|
|
174
95
|
if (!agentConfigData) {
|
|
175
96
|
throw new Error(`AI agent with name ${agentName} is not configured`);
|
|
176
97
|
}
|
|
177
|
-
//
|
|
178
|
-
const lower = agentConfigData.provider.toLowerCase();
|
|
179
|
-
if (!PROVIDER_CANONICAL.has(lower)) {
|
|
180
|
-
throw new Error(`AI provider ${agentConfigData.provider} is not supported. Use one of: ${Array.from(PROVIDER_CANONICAL).join(', ')}`);
|
|
181
|
-
}
|
|
98
|
+
// Normalize provider slug to lowercase and validate via Ax factory
|
|
99
|
+
const lower = String(agentConfigData.provider).toLowerCase();
|
|
182
100
|
const provider = lower;
|
|
183
|
-
//
|
|
101
|
+
// Resolve API key from user-supplied environment variable name
|
|
184
102
|
let apiKey = '';
|
|
185
103
|
if (agentConfigData.providerKeyName) {
|
|
186
|
-
|
|
104
|
+
const keyName = agentConfigData.providerKeyName;
|
|
105
|
+
apiKey = resolveApiKey(keyName) || '';
|
|
187
106
|
if (!apiKey) {
|
|
188
|
-
throw new Error(`API key for provider ${agentConfigData.provider} is not set in environment
|
|
107
|
+
throw new Error(`API key '${keyName}' for provider ${agentConfigData.provider} is not set in environment`);
|
|
189
108
|
}
|
|
190
109
|
}
|
|
191
110
|
else {
|
|
@@ -202,7 +121,10 @@ const parseAgentConfig = async (agentName, crewConfig, functions, state) => {
|
|
|
202
121
|
debug: agentConfigData.debug || false,
|
|
203
122
|
...agentConfigData.options,
|
|
204
123
|
// Attach default cost tracker so usage/costs are recorded by provider layer
|
|
205
|
-
trackers: [costTracker]
|
|
124
|
+
trackers: [costTracker],
|
|
125
|
+
// Inject telemetry if provided
|
|
126
|
+
tracer: options?.telemetry?.tracer,
|
|
127
|
+
meter: options?.telemetry?.meter
|
|
206
128
|
}
|
|
207
129
|
};
|
|
208
130
|
if (agentConfigData.apiURL) {
|
|
@@ -214,37 +136,20 @@ const parseAgentConfig = async (agentName, crewConfig, functions, state) => {
|
|
|
214
136
|
throw new Error(`Invalid apiURL provided: ${agentConfigData.apiURL}`);
|
|
215
137
|
}
|
|
216
138
|
}
|
|
217
|
-
// Forward provider-specific arguments
|
|
139
|
+
// Forward provider-specific arguments as-is; let Ax validate/ignore as needed
|
|
218
140
|
const providerArgs = agentConfigData.providerArgs;
|
|
219
|
-
if (
|
|
220
|
-
|
|
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);
|
|
141
|
+
if (providerArgs && typeof providerArgs === 'object') {
|
|
142
|
+
Object.assign(aiArgs, providerArgs);
|
|
238
143
|
}
|
|
239
|
-
|
|
240
|
-
|
|
241
|
-
|
|
144
|
+
// Validate provider by attempting instantiation; Ax will throw on unknown providers
|
|
145
|
+
let aiInstance;
|
|
146
|
+
try {
|
|
147
|
+
aiInstance = ai(aiArgs);
|
|
242
148
|
}
|
|
243
|
-
|
|
244
|
-
|
|
245
|
-
|
|
149
|
+
catch (e) {
|
|
150
|
+
const msg = e instanceof Error ? e.message : String(e);
|
|
151
|
+
throw new Error(`Unsupported provider '${provider}': ${msg}`);
|
|
246
152
|
}
|
|
247
|
-
const aiInstance = ai(aiArgs);
|
|
248
153
|
// If an mcpServers config is provided in the agent config, convert to functions
|
|
249
154
|
const mcpFunctions = await initializeMCPServers(agentConfigData);
|
|
250
155
|
// Prepare functions for the AI agent
|
|
@@ -284,3 +189,20 @@ const parseAgentConfig = async (agentName, crewConfig, functions, state) => {
|
|
|
284
189
|
}
|
|
285
190
|
};
|
|
286
191
|
export { parseAgentConfig, parseCrewConfig };
|
|
192
|
+
function resolveApiKey(varName) {
|
|
193
|
+
try {
|
|
194
|
+
// Prefer Node env when available
|
|
195
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
196
|
+
// @ts-ignore
|
|
197
|
+
if (typeof process !== 'undefined' && process?.env) {
|
|
198
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
199
|
+
// @ts-ignore
|
|
200
|
+
return process.env[varName];
|
|
201
|
+
}
|
|
202
|
+
// Fallback: allow global exposure in browser builds (e.g., injected at runtime)
|
|
203
|
+
return globalThis?.[varName];
|
|
204
|
+
}
|
|
205
|
+
catch {
|
|
206
|
+
return undefined;
|
|
207
|
+
}
|
|
208
|
+
}
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import type { AxAI } from '@ax-llm/ax';
|
|
2
|
+
import type { AxCrewConfig } from '../types.js';
|
|
3
|
+
type BuildProviderArgs = {
|
|
4
|
+
provider: string;
|
|
5
|
+
apiKey: string;
|
|
6
|
+
config: any;
|
|
7
|
+
apiURL?: string;
|
|
8
|
+
providerArgs?: Record<string, unknown>;
|
|
9
|
+
options?: Record<string, unknown>;
|
|
10
|
+
};
|
|
11
|
+
export declare function instantiateProvider({ provider, apiKey, config, apiURL, providerArgs, options, }: BuildProviderArgs): AxAI<any>;
|
|
12
|
+
export declare function buildProvidersFromConfig(cfg: AxCrewConfig): AxAI<any>[];
|
|
13
|
+
export declare function discoverProvidersFromConfig(cfg: AxCrewConfig): string[];
|
|
14
|
+
export declare function listSelectableProviders(cfg: AxCrewConfig): string[];
|
|
15
|
+
export {};
|
|
@@ -0,0 +1,58 @@
|
|
|
1
|
+
import { ai } from '@ax-llm/ax';
|
|
2
|
+
export function instantiateProvider({ provider, apiKey, config, apiURL, providerArgs, options, }) {
|
|
3
|
+
const args = { name: provider, apiKey, config, options };
|
|
4
|
+
if (apiURL)
|
|
5
|
+
args.apiURL = apiURL;
|
|
6
|
+
if (providerArgs && typeof providerArgs === 'object')
|
|
7
|
+
Object.assign(args, providerArgs);
|
|
8
|
+
return ai(args);
|
|
9
|
+
}
|
|
10
|
+
export function buildProvidersFromConfig(cfg) {
|
|
11
|
+
const services = [];
|
|
12
|
+
for (const agent of cfg.crew) {
|
|
13
|
+
const apiKeyName = agent.providerKeyName;
|
|
14
|
+
if (!apiKeyName)
|
|
15
|
+
throw new Error(`Provider key name is missing for agent ${agent.name}`);
|
|
16
|
+
const apiKey = resolveApiKey(apiKeyName) || '';
|
|
17
|
+
if (!apiKey)
|
|
18
|
+
throw new Error(`API key '${apiKeyName}' not set for agent ${agent.name}`);
|
|
19
|
+
const service = instantiateProvider({
|
|
20
|
+
provider: String(agent.provider).toLowerCase(),
|
|
21
|
+
apiKey,
|
|
22
|
+
config: agent.ai,
|
|
23
|
+
apiURL: agent.apiURL,
|
|
24
|
+
providerArgs: agent.providerArgs,
|
|
25
|
+
options: agent.options,
|
|
26
|
+
});
|
|
27
|
+
services.push(service);
|
|
28
|
+
}
|
|
29
|
+
return services;
|
|
30
|
+
}
|
|
31
|
+
// Provider discovery helpers consolidated here (previously in src/providers.ts)
|
|
32
|
+
export function discoverProvidersFromConfig(cfg) {
|
|
33
|
+
const providers = new Set();
|
|
34
|
+
for (const agent of cfg.crew) {
|
|
35
|
+
providers.add(String(agent.provider).toLowerCase());
|
|
36
|
+
}
|
|
37
|
+
return Array.from(providers);
|
|
38
|
+
}
|
|
39
|
+
export function listSelectableProviders(cfg) {
|
|
40
|
+
return discoverProvidersFromConfig(cfg);
|
|
41
|
+
}
|
|
42
|
+
function resolveApiKey(varName) {
|
|
43
|
+
try {
|
|
44
|
+
// Prefer Node env when available
|
|
45
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
46
|
+
// @ts-ignore
|
|
47
|
+
if (typeof process !== 'undefined' && process?.env) {
|
|
48
|
+
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
|
|
49
|
+
// @ts-ignore
|
|
50
|
+
return process.env[varName];
|
|
51
|
+
}
|
|
52
|
+
// Fallback: allow global exposure in browser builds (e.g., injected at runtime)
|
|
53
|
+
return globalThis?.[varName];
|
|
54
|
+
}
|
|
55
|
+
catch {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
}
|