@amitdeshmukh/ax-crew 6.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 +18 -0
- package/README.md +136 -0
- package/dist/agents/agentConfig.d.ts +2 -2
- package/dist/agents/agentConfig.js +5 -2
- package/dist/agents/index.d.ts +4 -2
- package/dist/agents/index.js +5 -2
- package/dist/types.d.ts +14 -1
- package/examples/telemetry-demo.ts +166 -0
- package/package.json +3 -2
- package/src/agents/agentConfig.ts +7 -2
- package/src/agents/index.ts +8 -2
- package/src/types.ts +15 -0
- package/tests/telemetry.test.ts +81 -0
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,23 @@
|
|
|
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
|
+
|
|
3
21
|
## [6.0.0] - 2025-10-22
|
|
4
22
|
|
|
5
23
|
### Breaking
|
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.
|
|
@@ -715,6 +717,140 @@ Notes:
|
|
|
715
717
|
- Legacy cost APIs (`getLastUsageCost`, `getAccumulatedCosts`, `getAggregatedCosts`) are superseded by metrics methods.
|
|
716
718
|
- Estimated cost values are numbers rounded to 5 decimal places.
|
|
717
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
|
+
|
|
718
854
|
## Changelog
|
|
719
855
|
|
|
720
856
|
See [CHANGELOG.md](CHANGELOG.md) for a list of changes and version updates.
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AxDefaultCostTracker } from '@ax-llm/ax';
|
|
2
2
|
import type { AxFunction } from '@ax-llm/ax';
|
|
3
|
-
import type { AgentConfig, AxCrewConfig, FunctionRegistryType, MCPTransportConfig, MCPStdioTransportConfig, MCPHTTPSSETransportConfig, MCPStreamableHTTPTransportConfig } from '../types.js';
|
|
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;
|
|
@@ -25,7 +25,7 @@ declare const parseCrewConfig: (input: AxCrewConfig) => {
|
|
|
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: AxCrewConfig, functions: FunctionRegistryType, state: Record<string, any
|
|
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;
|
|
@@ -88,7 +88,7 @@ const parseCrewConfig = (input) => {
|
|
|
88
88
|
* @throws {Error} Throws an error if the agent configuration is missing, the provider is unsupported,
|
|
89
89
|
* the API key is not found, or the provider key name is not specified in the configuration.
|
|
90
90
|
*/
|
|
91
|
-
const parseAgentConfig = async (agentName, crewConfig, functions, state) => {
|
|
91
|
+
const parseAgentConfig = async (agentName, crewConfig, functions, state, options) => {
|
|
92
92
|
try {
|
|
93
93
|
// Retrieve the parameters for the specified AI agent from config
|
|
94
94
|
const agentConfigData = parseCrewConfig(crewConfig).crew.find(agent => agent.name === agentName);
|
|
@@ -121,7 +121,10 @@ const parseAgentConfig = async (agentName, crewConfig, functions, state) => {
|
|
|
121
121
|
debug: agentConfigData.debug || false,
|
|
122
122
|
...agentConfigData.options,
|
|
123
123
|
// Attach default cost tracker so usage/costs are recorded by provider layer
|
|
124
|
-
trackers: [costTracker]
|
|
124
|
+
trackers: [costTracker],
|
|
125
|
+
// Inject telemetry if provided
|
|
126
|
+
tracer: options?.telemetry?.tracer,
|
|
127
|
+
meter: options?.telemetry?.meter
|
|
125
128
|
}
|
|
126
129
|
};
|
|
127
130
|
if (agentConfigData.apiURL) {
|
package/dist/agents/index.d.ts
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import { AxAgent, AxAI } from "@ax-llm/ax";
|
|
2
2
|
import type { AxSignature, AxAgentic, AxFunction, AxProgramForwardOptions, AxProgramStreamingForwardOptions, AxGenStreamingOut } from "@ax-llm/ax";
|
|
3
|
-
import type { StateInstance, FunctionRegistryType, UsageCost, AxCrewConfig, MCPTransportConfig } from "../types.js";
|
|
3
|
+
import type { StateInstance, FunctionRegistryType, UsageCost, AxCrewConfig, AxCrewOptions, MCPTransportConfig } from "../types.js";
|
|
4
4
|
declare class StatefulAxAgent extends AxAgent<any, any> {
|
|
5
5
|
state: StateInstance;
|
|
6
6
|
axai: any;
|
|
@@ -59,6 +59,7 @@ declare class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
59
59
|
*/
|
|
60
60
|
declare class AxCrew {
|
|
61
61
|
private crewConfig;
|
|
62
|
+
private options?;
|
|
62
63
|
functionsRegistry: FunctionRegistryType;
|
|
63
64
|
crewId: string;
|
|
64
65
|
agents: Map<string, StatefulAxAgent> | null;
|
|
@@ -67,9 +68,10 @@ declare class AxCrew {
|
|
|
67
68
|
* Creates an instance of AxCrew.
|
|
68
69
|
* @param {AxCrewConfig} crewConfig - JSON object with crew configuration.
|
|
69
70
|
* @param {FunctionRegistryType} [functionsRegistry={}] - The registry of functions to use in the crew.
|
|
71
|
+
* @param {AxCrewOptions} [options] - Optional settings for the crew (e.g., telemetry).
|
|
70
72
|
* @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
|
|
71
73
|
*/
|
|
72
|
-
constructor(crewConfig: AxCrewConfig, functionsRegistry?: FunctionRegistryType, crewId?: string);
|
|
74
|
+
constructor(crewConfig: AxCrewConfig, functionsRegistry?: FunctionRegistryType, options?: AxCrewOptions, crewId?: string);
|
|
73
75
|
/**
|
|
74
76
|
* Factory function for creating an agent.
|
|
75
77
|
* @param {string} agentName - The name of the agent to create.
|
package/dist/agents/index.js
CHANGED
|
@@ -196,6 +196,7 @@ class StatefulAxAgent extends AxAgent {
|
|
|
196
196
|
*/
|
|
197
197
|
class AxCrew {
|
|
198
198
|
crewConfig;
|
|
199
|
+
options;
|
|
199
200
|
functionsRegistry = {};
|
|
200
201
|
crewId;
|
|
201
202
|
agents;
|
|
@@ -204,9 +205,10 @@ class AxCrew {
|
|
|
204
205
|
* Creates an instance of AxCrew.
|
|
205
206
|
* @param {AxCrewConfig} crewConfig - JSON object with crew configuration.
|
|
206
207
|
* @param {FunctionRegistryType} [functionsRegistry={}] - The registry of functions to use in the crew.
|
|
208
|
+
* @param {AxCrewOptions} [options] - Optional settings for the crew (e.g., telemetry).
|
|
207
209
|
* @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
|
|
208
210
|
*/
|
|
209
|
-
constructor(crewConfig, functionsRegistry = {}, crewId = uuidv4()) {
|
|
211
|
+
constructor(crewConfig, functionsRegistry = {}, options, crewId = uuidv4()) {
|
|
210
212
|
// Basic validation of crew configuration
|
|
211
213
|
if (!crewConfig || typeof crewConfig !== 'object' || !('crew' in crewConfig)) {
|
|
212
214
|
throw new Error('Invalid crew configuration');
|
|
@@ -220,6 +222,7 @@ class AxCrew {
|
|
|
220
222
|
this.crewConfig = crewConfig;
|
|
221
223
|
this.functionsRegistry = functionsRegistry;
|
|
222
224
|
this.crewId = crewId;
|
|
225
|
+
this.options = options;
|
|
223
226
|
this.agents = new Map();
|
|
224
227
|
this.state = createState(crewId);
|
|
225
228
|
// Make crewId discoverable to metrics
|
|
@@ -233,7 +236,7 @@ class AxCrew {
|
|
|
233
236
|
*/
|
|
234
237
|
createAgent = async (agentName) => {
|
|
235
238
|
try {
|
|
236
|
-
const agentConfig = await parseAgentConfig(agentName, this.crewConfig, this.functionsRegistry, this.state);
|
|
239
|
+
const agentConfig = await parseAgentConfig(agentName, this.crewConfig, this.functionsRegistry, this.state, this.options);
|
|
237
240
|
// Destructure with type assertion
|
|
238
241
|
const { ai, name, description, signature, functions, subAgentNames, examples, tracker } = agentConfig;
|
|
239
242
|
// Get subagents for the AI agent
|
package/dist/types.d.ts
CHANGED
|
@@ -216,4 +216,17 @@ interface AgentConfig {
|
|
|
216
216
|
interface AxCrewConfig {
|
|
217
217
|
crew: AgentConfig[];
|
|
218
218
|
}
|
|
219
|
-
|
|
219
|
+
/**
|
|
220
|
+
* Options for the AxCrew instance, specifically allowing optional OpenTelemetry injection.
|
|
221
|
+
*
|
|
222
|
+
* @property {Object} [telemetry] - Telemetry configuration.
|
|
223
|
+
* @property {any} [telemetry.tracer] - OpenTelemetry Tracer instance.
|
|
224
|
+
* @property {any} [telemetry.meter] - OpenTelemetry Meter instance.
|
|
225
|
+
*/
|
|
226
|
+
interface AxCrewOptions {
|
|
227
|
+
telemetry?: {
|
|
228
|
+
tracer?: any;
|
|
229
|
+
meter?: any;
|
|
230
|
+
};
|
|
231
|
+
}
|
|
232
|
+
export { type AgentConfig, type AxCrewConfig, type AxCrewOptions, type AggregatedMetrics, type StateInstance, type FunctionRegistryType, type MCPStdioTransportConfig, type MCPHTTPSSETransportConfig, type MCPStreamableHTTPTransportConfig, type MCPTransportConfig, type ModelUsage, type ModelInfo, type UsageCost, type AggregatedCosts };
|
|
@@ -0,0 +1,166 @@
|
|
|
1
|
+
|
|
2
|
+
import { AxCrew } from "../dist/index.js";
|
|
3
|
+
import { AxCrewFunctions } from "../dist/functions/index.js";
|
|
4
|
+
import type { AxCrewConfig } from "../dist/types.js";
|
|
5
|
+
import type { Provider } from "../dist/types.js";
|
|
6
|
+
import "dotenv/config";
|
|
7
|
+
|
|
8
|
+
// Import OpenTelemetry packages
|
|
9
|
+
// Note: In a real project, you would need to install these dependencies:
|
|
10
|
+
// npm install @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/sdk-metrics
|
|
11
|
+
// Optional: npm install @opentelemetry/exporter-jaeger (for Jaeger UI visualization)
|
|
12
|
+
import { metrics, trace } from "@opentelemetry/api";
|
|
13
|
+
import {
|
|
14
|
+
ConsoleSpanExporter,
|
|
15
|
+
SimpleSpanProcessor,
|
|
16
|
+
} from "@opentelemetry/sdk-trace-base";
|
|
17
|
+
import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
|
|
18
|
+
import {
|
|
19
|
+
ConsoleMetricExporter,
|
|
20
|
+
MeterProvider,
|
|
21
|
+
PeriodicExportingMetricReader,
|
|
22
|
+
} from "@opentelemetry/sdk-metrics";
|
|
23
|
+
|
|
24
|
+
// Optional Jaeger import - will be used if available
|
|
25
|
+
let JaegerExporter;
|
|
26
|
+
try {
|
|
27
|
+
JaegerExporter = (await import('@opentelemetry/exporter-jaeger')).JaegerExporter;
|
|
28
|
+
} catch (error) {
|
|
29
|
+
console.log('Jaeger exporter not available. Install with: npm install @opentelemetry/exporter-jaeger');
|
|
30
|
+
console.log('Traces will only be sent to console.');
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
// --- 1. Setup OpenTelemetry (Console + Optional Jaeger) ---
|
|
34
|
+
|
|
35
|
+
// Set up tracing to console and optionally Jaeger
|
|
36
|
+
const spanProcessors = [new SimpleSpanProcessor(new ConsoleSpanExporter())];
|
|
37
|
+
|
|
38
|
+
// Add Jaeger if available
|
|
39
|
+
if (JaegerExporter) {
|
|
40
|
+
try {
|
|
41
|
+
spanProcessors.push(new SimpleSpanProcessor(new JaegerExporter({
|
|
42
|
+
endpoint: 'http://localhost:14268/api/traces',
|
|
43
|
+
})));
|
|
44
|
+
console.log('Jaeger tracing enabled. View traces at: http://localhost:16686');
|
|
45
|
+
} catch (error) {
|
|
46
|
+
console.log('Failed to initialize Jaeger exporter:', error.message);
|
|
47
|
+
console.log('Continuing with console tracing only.');
|
|
48
|
+
}
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
const tracerProvider = new NodeTracerProvider({
|
|
52
|
+
spanProcessors
|
|
53
|
+
});
|
|
54
|
+
tracerProvider.register(); // This registers it as the global tracer provider
|
|
55
|
+
|
|
56
|
+
// Set up basic metrics to print to console
|
|
57
|
+
const meterProvider = new MeterProvider({
|
|
58
|
+
readers: [
|
|
59
|
+
new PeriodicExportingMetricReader({
|
|
60
|
+
exporter: new ConsoleMetricExporter(),
|
|
61
|
+
exportIntervalMillis: 5000, // Export every 5 seconds
|
|
62
|
+
}),
|
|
63
|
+
],
|
|
64
|
+
});
|
|
65
|
+
metrics.setGlobalMeterProvider(meterProvider);
|
|
66
|
+
|
|
67
|
+
// Get your tracer and meter instances
|
|
68
|
+
const tracer = trace.getTracer("ax-crew-example");
|
|
69
|
+
const meter = metrics.getMeter("ax-crew-example");
|
|
70
|
+
|
|
71
|
+
// --- 2. Define Crew Configuration ---
|
|
72
|
+
|
|
73
|
+
const crewConfig: AxCrewConfig = {
|
|
74
|
+
crew: [
|
|
75
|
+
{
|
|
76
|
+
name: "Researcher",
|
|
77
|
+
description: "Researches a topic using tools and provides a summary.",
|
|
78
|
+
signature: "topic:string -> facts:string[]",
|
|
79
|
+
// Agent 1 uses OpenAI
|
|
80
|
+
provider: "openai" as Provider,
|
|
81
|
+
providerKeyName: "OPENAI_API_KEY",
|
|
82
|
+
ai: {
|
|
83
|
+
model: "gpt-4o-mini",
|
|
84
|
+
temperature: 0.7,
|
|
85
|
+
},
|
|
86
|
+
// Give this agent access to a tool (function)
|
|
87
|
+
functions: ["CurrentDateTime"]
|
|
88
|
+
},
|
|
89
|
+
{
|
|
90
|
+
name: "Writer",
|
|
91
|
+
description: "Writes a blog post based on provided facts.",
|
|
92
|
+
signature: "facts:string[] -> blogPost:string",
|
|
93
|
+
// Agent 2 uses a different provider (e.g., Google Gemini)
|
|
94
|
+
provider: "google-gemini" as Provider,
|
|
95
|
+
providerKeyName: "GEMINI_API_KEY",
|
|
96
|
+
ai: {
|
|
97
|
+
model: "gemini-flash-latest",
|
|
98
|
+
temperature: 0.7,
|
|
99
|
+
},
|
|
100
|
+
// No tools for this agent, just pure generation
|
|
101
|
+
}
|
|
102
|
+
]
|
|
103
|
+
};
|
|
104
|
+
|
|
105
|
+
// --- 3. Run the Crew ---
|
|
106
|
+
|
|
107
|
+
async function main() {
|
|
108
|
+
console.log("Starting AxCrew with Telemetry...");
|
|
109
|
+
|
|
110
|
+
// Initialize AxCrew with the telemetry options
|
|
111
|
+
const crew = new AxCrew(
|
|
112
|
+
crewConfig,
|
|
113
|
+
AxCrewFunctions,
|
|
114
|
+
undefined, // Use default crewId
|
|
115
|
+
{
|
|
116
|
+
telemetry: {
|
|
117
|
+
tracer,
|
|
118
|
+
meter
|
|
119
|
+
}
|
|
120
|
+
}
|
|
121
|
+
);
|
|
122
|
+
|
|
123
|
+
try {
|
|
124
|
+
// Initialize agents
|
|
125
|
+
await crew.addAgent("Researcher");
|
|
126
|
+
await crew.addAgent("Writer");
|
|
127
|
+
|
|
128
|
+
const researcher = crew.agents!.get("Researcher")!;
|
|
129
|
+
const writer = crew.agents!.get("Writer")!;
|
|
130
|
+
|
|
131
|
+
// Step 1: Research
|
|
132
|
+
console.log("\n--- Step 1: Researching ---");
|
|
133
|
+
// This call will be traced, including the 'CurrentDateTime' tool usage
|
|
134
|
+
const researchResult = await researcher.forward({
|
|
135
|
+
topic: "The future of AI agents in 2025"
|
|
136
|
+
});
|
|
137
|
+
console.log("Research output:", researchResult.facts);
|
|
138
|
+
|
|
139
|
+
// Step 2: Writing
|
|
140
|
+
console.log("\n--- Step 2: Writing ---");
|
|
141
|
+
// This call will be traced under a different provider
|
|
142
|
+
const writerResult = await writer.forward({
|
|
143
|
+
facts: researchResult.facts
|
|
144
|
+
});
|
|
145
|
+
console.log("Blog Post:\n", writerResult.blogPost);
|
|
146
|
+
|
|
147
|
+
console.log("\n--- Done ---");
|
|
148
|
+
console.log("Check your console output above for OpenTelemetry traces and metrics.");
|
|
149
|
+
if (JaegerExporter) {
|
|
150
|
+
console.log("Check Jaeger UI at http://localhost:16686 for enhanced trace visualization.");
|
|
151
|
+
} else {
|
|
152
|
+
console.log("For enhanced visualization, install Jaeger: npm install @opentelemetry/exporter-jaeger");
|
|
153
|
+
console.log("Then run: docker run -d --name jaeger -p 16686:16686 -p 14268:14268 jaegertracing/all-in-one:latest");
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
// Wait a moment for metrics to export before exiting
|
|
157
|
+
await new Promise(resolve => setTimeout(resolve, 6000));
|
|
158
|
+
|
|
159
|
+
} catch (error) {
|
|
160
|
+
console.error("Error running crew:", error);
|
|
161
|
+
} finally {
|
|
162
|
+
crew.destroy();
|
|
163
|
+
}
|
|
164
|
+
}
|
|
165
|
+
|
|
166
|
+
main().catch(console.error);
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"type": "module",
|
|
3
3
|
"name": "@amitdeshmukh/ax-crew",
|
|
4
|
-
"version": "
|
|
4
|
+
"version": "7.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",
|
|
@@ -25,7 +25,8 @@
|
|
|
25
25
|
},
|
|
26
26
|
"peerDependencies": {
|
|
27
27
|
"@ax-llm/ax": "^14.0.36",
|
|
28
|
-
"@ax-llm/ax-tools": "^14.0.36"
|
|
28
|
+
"@ax-llm/ax-tools": "^14.0.36",
|
|
29
|
+
"@opentelemetry/api": "^1.9.0"
|
|
29
30
|
},
|
|
30
31
|
"devDependencies": {
|
|
31
32
|
"@testing-library/jest-dom": "^6.6.3",
|
|
@@ -7,6 +7,7 @@ import { AxMCPStdioTransport } from '@ax-llm/ax-tools'
|
|
|
7
7
|
import type {
|
|
8
8
|
AgentConfig,
|
|
9
9
|
AxCrewConfig,
|
|
10
|
+
AxCrewOptions,
|
|
10
11
|
FunctionRegistryType,
|
|
11
12
|
MCPTransportConfig,
|
|
12
13
|
MCPStdioTransportConfig,
|
|
@@ -112,7 +113,8 @@ const parseAgentConfig = async (
|
|
|
112
113
|
agentName: string,
|
|
113
114
|
crewConfig: AxCrewConfig,
|
|
114
115
|
functions: FunctionRegistryType,
|
|
115
|
-
state: Record<string, any
|
|
116
|
+
state: Record<string, any>,
|
|
117
|
+
options?: AxCrewOptions
|
|
116
118
|
) => {
|
|
117
119
|
try {
|
|
118
120
|
// Retrieve the parameters for the specified AI agent from config
|
|
@@ -149,7 +151,10 @@ const parseAgentConfig = async (
|
|
|
149
151
|
debug: agentConfigData.debug || false,
|
|
150
152
|
...agentConfigData.options,
|
|
151
153
|
// Attach default cost tracker so usage/costs are recorded by provider layer
|
|
152
|
-
trackers: [costTracker]
|
|
154
|
+
trackers: [costTracker],
|
|
155
|
+
// Inject telemetry if provided
|
|
156
|
+
tracer: options?.telemetry?.tracer,
|
|
157
|
+
meter: options?.telemetry?.meter
|
|
153
158
|
}
|
|
154
159
|
};
|
|
155
160
|
if (agentConfigData.apiURL) {
|
package/src/agents/index.ts
CHANGED
|
@@ -15,6 +15,7 @@ import type {
|
|
|
15
15
|
FunctionRegistryType,
|
|
16
16
|
UsageCost,
|
|
17
17
|
AxCrewConfig,
|
|
18
|
+
AxCrewOptions,
|
|
18
19
|
MCPTransportConfig,
|
|
19
20
|
} from "../types.js";
|
|
20
21
|
|
|
@@ -280,6 +281,7 @@ class StatefulAxAgent extends AxAgent<any, any> {
|
|
|
280
281
|
*/
|
|
281
282
|
class AxCrew {
|
|
282
283
|
private crewConfig: AxCrewConfig;
|
|
284
|
+
private options?: AxCrewOptions;
|
|
283
285
|
functionsRegistry: FunctionRegistryType = {};
|
|
284
286
|
crewId: string;
|
|
285
287
|
agents: Map<string, StatefulAxAgent> | null;
|
|
@@ -289,12 +291,14 @@ class AxCrew {
|
|
|
289
291
|
* Creates an instance of AxCrew.
|
|
290
292
|
* @param {AxCrewConfig} crewConfig - JSON object with crew configuration.
|
|
291
293
|
* @param {FunctionRegistryType} [functionsRegistry={}] - The registry of functions to use in the crew.
|
|
294
|
+
* @param {AxCrewOptions} [options] - Optional settings for the crew (e.g., telemetry).
|
|
292
295
|
* @param {string} [crewId=uuidv4()] - The unique identifier for the crew.
|
|
293
296
|
*/
|
|
294
297
|
constructor(
|
|
295
298
|
crewConfig: AxCrewConfig,
|
|
296
299
|
functionsRegistry: FunctionRegistryType = {},
|
|
297
|
-
|
|
300
|
+
options?: AxCrewOptions,
|
|
301
|
+
crewId: string = uuidv4(),
|
|
298
302
|
) {
|
|
299
303
|
// Basic validation of crew configuration
|
|
300
304
|
if (!crewConfig || typeof crewConfig !== 'object' || !('crew' in crewConfig)) {
|
|
@@ -311,6 +315,7 @@ class AxCrew {
|
|
|
311
315
|
this.crewConfig = crewConfig;
|
|
312
316
|
this.functionsRegistry = functionsRegistry;
|
|
313
317
|
this.crewId = crewId;
|
|
318
|
+
this.options = options;
|
|
314
319
|
this.agents = new Map<string, StatefulAxAgent>();
|
|
315
320
|
this.state = createState(crewId);
|
|
316
321
|
// Make crewId discoverable to metrics
|
|
@@ -329,7 +334,8 @@ class AxCrew {
|
|
|
329
334
|
agentName,
|
|
330
335
|
this.crewConfig,
|
|
331
336
|
this.functionsRegistry,
|
|
332
|
-
this.state
|
|
337
|
+
this.state,
|
|
338
|
+
this.options
|
|
333
339
|
);
|
|
334
340
|
|
|
335
341
|
// Destructure with type assertion
|
package/src/types.ts
CHANGED
|
@@ -236,9 +236,24 @@ interface AxCrewConfig {
|
|
|
236
236
|
crew: AgentConfig[]
|
|
237
237
|
}
|
|
238
238
|
|
|
239
|
+
/**
|
|
240
|
+
* Options for the AxCrew instance, specifically allowing optional OpenTelemetry injection.
|
|
241
|
+
*
|
|
242
|
+
* @property {Object} [telemetry] - Telemetry configuration.
|
|
243
|
+
* @property {any} [telemetry.tracer] - OpenTelemetry Tracer instance.
|
|
244
|
+
* @property {any} [telemetry.meter] - OpenTelemetry Meter instance.
|
|
245
|
+
*/
|
|
246
|
+
interface AxCrewOptions {
|
|
247
|
+
telemetry?: {
|
|
248
|
+
tracer?: any;
|
|
249
|
+
meter?: any;
|
|
250
|
+
}
|
|
251
|
+
}
|
|
252
|
+
|
|
239
253
|
export {
|
|
240
254
|
type AgentConfig,
|
|
241
255
|
type AxCrewConfig,
|
|
256
|
+
type AxCrewOptions,
|
|
242
257
|
type AggregatedMetrics,
|
|
243
258
|
type StateInstance,
|
|
244
259
|
type FunctionRegistryType,
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
|
|
2
|
+
import { describe, it, expect, vi, beforeEach } from 'vitest';
|
|
3
|
+
import { AxCrew } from '../src/index.js';
|
|
4
|
+
import type { AxCrewConfig } from '../src/types.js';
|
|
5
|
+
import * as axModule from '@ax-llm/ax';
|
|
6
|
+
|
|
7
|
+
// Mock the entire @ax-llm/ax module
|
|
8
|
+
vi.mock('@ax-llm/ax', async (importOriginal) => {
|
|
9
|
+
const actual = await importOriginal() as typeof axModule;
|
|
10
|
+
return {
|
|
11
|
+
...actual,
|
|
12
|
+
// Spy on the ai factory function
|
|
13
|
+
ai: vi.fn().mockImplementation((args) => {
|
|
14
|
+
// Return a dummy object that mimics enough of AxAI to satisfy AxCrew
|
|
15
|
+
return {
|
|
16
|
+
getName: () => args.name,
|
|
17
|
+
chat: vi.fn(),
|
|
18
|
+
defaults: { model: args.config?.model },
|
|
19
|
+
options: args.options // Store options so we can potentially inspect if needed, though we rely on the spy
|
|
20
|
+
};
|
|
21
|
+
}),
|
|
22
|
+
// We need to keep other exports working if they are used
|
|
23
|
+
AxAgent: actual.AxAgent,
|
|
24
|
+
AxAI: actual.AxAI,
|
|
25
|
+
AxDefaultCostTracker: actual.AxDefaultCostTracker
|
|
26
|
+
};
|
|
27
|
+
});
|
|
28
|
+
|
|
29
|
+
describe('AxCrew Telemetry', () => {
|
|
30
|
+
beforeEach(() => {
|
|
31
|
+
vi.clearAllMocks();
|
|
32
|
+
});
|
|
33
|
+
|
|
34
|
+
it('should pass telemetry options to the underlying AxAI factory', async () => {
|
|
35
|
+
const mockTracer = { isMockTracer: true };
|
|
36
|
+
const mockMeter = { isMockMeter: true };
|
|
37
|
+
|
|
38
|
+
const crewConfig: AxCrewConfig = {
|
|
39
|
+
crew: [
|
|
40
|
+
{
|
|
41
|
+
name: "telemetry-agent",
|
|
42
|
+
description: "An agent for testing telemetry propagation",
|
|
43
|
+
signature: "in:string -> out:string",
|
|
44
|
+
provider: "openai",
|
|
45
|
+
providerKeyName: "OPENAI_API_KEY",
|
|
46
|
+
ai: { model: "gpt-4o-mini" }
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
};
|
|
50
|
+
|
|
51
|
+
// Set dummy key
|
|
52
|
+
process.env.OPENAI_API_KEY = 'dummy-key';
|
|
53
|
+
|
|
54
|
+
const options = {
|
|
55
|
+
telemetry: {
|
|
56
|
+
tracer: mockTracer,
|
|
57
|
+
meter: mockMeter
|
|
58
|
+
}
|
|
59
|
+
};
|
|
60
|
+
|
|
61
|
+
// Initialize Crew with telemetry options (3rd argument now)
|
|
62
|
+
const crew = new AxCrew(crewConfig, {}, options as any);
|
|
63
|
+
|
|
64
|
+
// Create the agent
|
|
65
|
+
await crew.addAgent("telemetry-agent");
|
|
66
|
+
|
|
67
|
+
// Check if the 'ai' mock was called with the expected arguments
|
|
68
|
+
expect(axModule.ai).toHaveBeenCalled();
|
|
69
|
+
|
|
70
|
+
// Get the arguments of the first call to ai()
|
|
71
|
+
const callArgs = vi.mocked(axModule.ai).mock.calls[0][0];
|
|
72
|
+
|
|
73
|
+
// Verify the structure
|
|
74
|
+
expect(callArgs).toBeDefined();
|
|
75
|
+
expect(callArgs.options).toBeDefined();
|
|
76
|
+
|
|
77
|
+
// Assert tracer and meter were passed correctly
|
|
78
|
+
expect(callArgs.options.tracer).toBe(mockTracer);
|
|
79
|
+
expect(callArgs.options.meter).toBe(mockMeter);
|
|
80
|
+
});
|
|
81
|
+
});
|