@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.
@@ -0,0 +1,7 @@
1
+ {
2
+ "permissions": {
3
+ "allow": [
4
+ "Bash(npm test:*)"
5
+ ]
6
+ }
7
+ }
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
  ![image](axcrew.png)
2
2
 
3
+ [![npm version](https://img.shields.io/npm/v/@amitdeshmukh/ax-crew.svg)](https://www.npmjs.com/package/@amitdeshmukh/ax-crew) [![npm downloads](https://img.shields.io/npm/dm/@amitdeshmukh/ax-crew.svg)](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>) => Promise<{
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) {
@@ -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.
@@ -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
- 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 };
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": "6.0.0",
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) {
@@ -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
- crewId: string = uuidv4()
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
+ });