@amitdeshmukh/ax-crew 8.6.0 → 8.7.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -0,0 +1,168 @@
1
+ ---
2
+ name: ax-crew-state
3
+ version: __VERSION__
4
+ description: "State management: shared state, StateInstance, set, get, getAll, reset, accessing state from class-based functions"
5
+ ---
6
+
7
+ # State
8
+
9
+ Every `AxCrew` instance has a shared `StateInstance` at `crew.state`. All agents and class-based functions in the crew can access the same state.
10
+
11
+ ## StateInstance API
12
+
13
+ ```ts
14
+ interface StateInstance {
15
+ set(key: string, value: any): void; // Set a value
16
+ get(key: string): any; // Get a value
17
+ getAll(): Record<string, any>; // Get all key-value pairs (shallow copy)
18
+ reset(): void; // Clear all state
19
+ }
20
+ ```
21
+
22
+ ## Core Behavior
23
+
24
+ - `crew.state` is created automatically when `new AxCrew(config)` is called.
25
+ - State is keyed by `crewId` (auto-generated UUID). Each crew instance has its own isolated state.
26
+ - State persists for the lifetime of the crew. Calling `crew.destroy()` calls `state.reset()`.
27
+ - Values can be any type: strings, objects, arrays, etc.
28
+
29
+ ## Canonical Pattern
30
+
31
+ ```ts
32
+ import { AxCrew } from '@amitdeshmukh/ax-crew';
33
+ import type { AxCrewConfig, FunctionRegistryType } from '@amitdeshmukh/ax-crew';
34
+ import type { AxFunction } from '@ax-llm/ax';
35
+ import dotenv from 'dotenv';
36
+ dotenv.config();
37
+
38
+ // Class-based function that reads from shared state
39
+ class SendEmail {
40
+ private state: Record<string, any>;
41
+ constructor(state: Record<string, any>) { this.state = state; }
42
+ toFunction(): AxFunction {
43
+ return {
44
+ name: 'SendEmail',
45
+ description: 'Send an email using configured SMTP credentials',
46
+ parameters: {
47
+ type: 'object',
48
+ properties: {
49
+ to: { type: 'string', description: 'Recipient email' },
50
+ subject: { type: 'string', description: 'Email subject' },
51
+ body: { type: 'string', description: 'Email body' },
52
+ },
53
+ required: ['to', 'subject', 'body']
54
+ },
55
+ func: async ({ to, subject, body }: { to: string; subject: string; body: string }) => {
56
+ // Access env credentials set via crew.state.set("env", {...})
57
+ const env = this.state.env || {};
58
+ const smtpHost = env.SMTP_HOST;
59
+ const smtpUser = env.SMTP_USER;
60
+ const smtpPass = env.SMTP_PASS;
61
+ // ... send email using credentials
62
+ return { sent: true, to };
63
+ }
64
+ };
65
+ }
66
+ }
67
+
68
+ const config: AxCrewConfig = {
69
+ crew: [
70
+ {
71
+ name: "notifier",
72
+ description: "An agent that sends email notifications",
73
+ signature: "task:string -> result:string",
74
+ provider: "openai",
75
+ providerKeyName: "OPENAI_API_KEY",
76
+ ai: { model: "gpt-4o-mini", temperature: 0 },
77
+ functions: ["SendEmail"],
78
+ }
79
+ ]
80
+ };
81
+
82
+ async function main() {
83
+ const functions: FunctionRegistryType = { SendEmail };
84
+ const crew = new AxCrew(config, functions);
85
+
86
+ // Set environment variables in shared state BEFORE adding agents
87
+ crew.state.set("env", {
88
+ SMTP_HOST: "smtp.example.com",
89
+ SMTP_USER: "user@example.com",
90
+ SMTP_PASS: "secret",
91
+ });
92
+
93
+ // You can also set arbitrary data
94
+ crew.state.set("company", "Acme Corp");
95
+ crew.state.set("maxRetries", 3);
96
+
97
+ await crew.addAllAgents();
98
+ const notifier = crew.agents?.get("notifier");
99
+
100
+ const result = await notifier?.forward({ task: "Notify user about order shipped" });
101
+ console.log(result?.result);
102
+
103
+ // Read state back
104
+ console.log("All state:", crew.state.getAll());
105
+ console.log("Company:", crew.state.get("company"));
106
+
107
+ // Reset state (clear all)
108
+ crew.state.reset();
109
+
110
+ crew.destroy();
111
+ }
112
+
113
+ main().catch(console.error);
114
+ ```
115
+
116
+ ## Setting Environment Credentials
117
+
118
+ The common pattern for passing credentials to class-based functions:
119
+
120
+ ```ts
121
+ crew.state.set("env", {
122
+ WORDPRESS_URL: "http://my-wordpress-site.com",
123
+ WORDPRESS_USERNAME: "my-username",
124
+ WORDPRESS_PASSWORD: "my-password",
125
+ });
126
+ ```
127
+
128
+ Inside the class-based function, access via `this.state.env`:
129
+
130
+ ```ts
131
+ class MyFunction {
132
+ private state: Record<string, any>;
133
+ constructor(state: Record<string, any>) { this.state = state; }
134
+ toFunction(): AxFunction {
135
+ return {
136
+ name: 'MyFunction',
137
+ description: 'Does something',
138
+ parameters: { type: 'object', properties: {} },
139
+ func: () => {
140
+ const url = this.state.env?.WORDPRESS_URL; // reads from shared state
141
+ return url;
142
+ }
143
+ };
144
+ }
145
+ }
146
+ ```
147
+
148
+ ## Accessing State from Agents
149
+
150
+ Each `StatefulAxAgent` has a `state` property that references the crew's shared state:
151
+
152
+ ```ts
153
+ const agent = crew.agents?.get("myAgent");
154
+ // agent.state is the same StateInstance as crew.state
155
+ ```
156
+
157
+ ## Do Not Generate
158
+
159
+ - Do NOT assume state values exist without checking; always use optional chaining (e.g. `this.state.env?.KEY`).
160
+ - Do NOT call `crew.state.set("env", ...)` after `addAllAgents()` if class-based functions read state during construction -- set state first.
161
+ - Do NOT confuse `crew.state` (StateInstance with `set`/`get`/`getAll`/`reset`) with plain objects. The state object passed to class-based function constructors is a plain record, not the `StateInstance` interface.
162
+ - Do NOT store sensitive credentials in state if the state object might be logged or serialized. The `getAll()` method returns all stored values.
163
+
164
+ ## References
165
+
166
+ - [write-post-and-publish-to-wordpress.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/write-post-and-publish-to-wordpress.ts)
167
+ - [src/state/index.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/state/index.ts)
168
+ - [src/types.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/src/types.ts)
@@ -0,0 +1,143 @@
1
+ ---
2
+ name: ax-crew-streaming
3
+ version: "__VERSION__"
4
+ description: "ax-crew streaming patterns: streaming, streamingForward, stream, delta, real-time, chunks, async generator consumption"
5
+ argument-hint: [topic]
6
+ allowed-tools: Read, Grep, Glob
7
+ ---
8
+
9
+ # ax-crew Streaming
10
+
11
+ ## streamingForward() vs forward()
12
+
13
+ `forward()` returns `Promise<Record<string, any>>` -- waits for full response.
14
+ `streamingForward()` returns `AxGenStreamingOut<any>` -- an async generator yielding chunks as they arrive.
15
+
16
+ Both accept the same input values object. Both work with `axgen` and `axagent` execution modes.
17
+
18
+ ```typescript
19
+ // Blocking
20
+ const result = await agent.forward({ question: "What is AI?" });
21
+ console.log(result.answer);
22
+
23
+ // Streaming
24
+ const stream = agent.streamingForward({ question: "What is AI?" });
25
+ for await (const chunk of stream) {
26
+ if (chunk.delta && 'answer' in chunk.delta) {
27
+ process.stdout.write(chunk.delta.answer);
28
+ }
29
+ }
30
+ ```
31
+
32
+ ## chunk.delta Structure
33
+
34
+ Each yielded chunk has a `delta` property containing partial values keyed by output field names from the agent's signature.
35
+
36
+ ```typescript
37
+ // For signature: 'question:string -> answer:string'
38
+ // chunk.delta = { answer: "partial text..." }
39
+
40
+ // For signature: 'query:string -> title:string, summary:string'
41
+ // chunk.delta may contain { title: "..." } or { summary: "..." }
42
+ ```
43
+
44
+ Always check for the field's existence before accessing:
45
+ ```typescript
46
+ if (chunk.delta && typeof chunk.delta === 'object' && 'answer' in chunk.delta) {
47
+ process.stdout.write(chunk.delta.answer);
48
+ }
49
+ ```
50
+
51
+ ## Stream Config in Agent Config
52
+
53
+ Enable streaming at the provider level via `ai.stream` or `options.stream`:
54
+
55
+ ```typescript
56
+ {
57
+ name: "StreamAgent",
58
+ ai: { model: "gemini-2.5-flash", stream: true }, // provider-level
59
+ options: { stream: true }, // forward options level
60
+ }
61
+ ```
62
+
63
+ ## Overload Signatures
64
+
65
+ ```typescript
66
+ // Without explicit AI instance (uses agent's configured AI)
67
+ agent.streamingForward(values: Record<string, any>, options?: AxProgramStreamingForwardOptions): AxGenStreamingOut<any>;
68
+
69
+ // With explicit AI instance
70
+ agent.streamingForward(ai: AxAI, values: Record<string, any>, options?: AxProgramStreamingForwardOptions): AxGenStreamingOut<any>;
71
+ ```
72
+
73
+ ## Canonical Pattern
74
+
75
+ ```typescript
76
+ import { AxCrew } from '@amitdeshmukh/ax-crew';
77
+ import type { AxCrewConfig } from '@amitdeshmukh/ax-crew';
78
+
79
+ const config: AxCrewConfig = {
80
+ crew: [
81
+ {
82
+ name: "ManagerAgent",
83
+ description: "Completes a user specified task",
84
+ signature:
85
+ 'question:string "a question to be answered" -> answer:string "the answer to the question"',
86
+ provider: "google-gemini",
87
+ providerKeyName: "GEMINI_API_KEY",
88
+ ai: { model: "gemini-2.5-flash", maxTokens: 1000, temperature: 0 },
89
+ options: { debug: true, stream: true },
90
+ agents: ["MathAgent"],
91
+ },
92
+ {
93
+ name: "MathAgent",
94
+ description: "Solves math problems",
95
+ signature:
96
+ 'mathProblem:string "a math problem" -> solution:string "the answer"',
97
+ provider: "google-gemini",
98
+ providerKeyName: "GEMINI_API_KEY",
99
+ ai: { model: "gemini-2.5-pro", temperature: 0 },
100
+ },
101
+ ],
102
+ };
103
+
104
+ async function main() {
105
+ const crew = new AxCrew(config);
106
+ await crew.addAllAgents();
107
+
108
+ const agent = crew.agents?.get("ManagerAgent");
109
+ if (!agent) throw new Error("Agent not found");
110
+
111
+ const stream = agent.streamingForward({
112
+ question: "Get me the first 5 fibonacci numbers divided by 2",
113
+ });
114
+
115
+ for await (const chunk of stream) {
116
+ if (chunk.delta && typeof chunk.delta === 'object' && 'answer' in chunk.delta) {
117
+ process.stdout.write(chunk.delta.answer);
118
+ }
119
+ }
120
+ console.log('\n');
121
+
122
+ // Metrics still available after streaming
123
+ console.log("Agent Metrics:", JSON.stringify(agent.getMetrics?.(), null, 2));
124
+ console.log("Crew Metrics:", JSON.stringify(crew.getCrewMetrics(), null, 2));
125
+
126
+ crew.destroy();
127
+ }
128
+
129
+ main().catch(console.error);
130
+ ```
131
+
132
+ ## Do Not Generate
133
+
134
+ - Do NOT `await` the return of `streamingForward()` -- it returns the async generator directly, not a promise.
135
+ - Do NOT assume `chunk.delta` is always a string -- it is an object keyed by output field names.
136
+ - Do NOT forget to check field existence in `chunk.delta` before accessing (`'fieldName' in chunk.delta`).
137
+ - Do NOT use `forward()` with the expectation of receiving streaming chunks -- use `streamingForward()`.
138
+ - Do NOT skip `crew.destroy()` -- it cleans up MCP servers and resources.
139
+
140
+ ## References
141
+
142
+ - [streaming.ts example](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/streaming.ts)
143
+ - [Source: StatefulAxAgent.streamingForward](https://github.com/amitdeshmukh/ax-crew/blob/main/src/agents/index.ts)
@@ -0,0 +1,203 @@
1
+ ---
2
+ name: ax-crew-sub-agents
3
+ description: AxCrew sub-agent composition via agents[] field. Covers agent delegation, dependency resolution, lazy agents (addLazyAgent), parent-child tool integration, and multi-agent orchestration patterns.
4
+ version: "__VERSION__"
5
+ ---
6
+
7
+ # AxCrew Sub-Agents
8
+
9
+ ## Core Concept
10
+
11
+ The `agents[]` field in `AgentConfig` lists names of other agents that become available as tools to the parent agent. AxCrew resolves dependencies and initializes sub-agents before their parent.
12
+
13
+ ## agents[] Field
14
+
15
+ ```typescript
16
+ {
17
+ name: "ParentAgent",
18
+ description: "Orchestrates sub-agents",
19
+ signature: "query:string -> answer:string",
20
+ provider: "openai",
21
+ ai: { model: "gpt-4o" },
22
+ agents: ["SubAgentA", "SubAgentB"], // these become callable tools
23
+ }
24
+ ```
25
+
26
+ The parent agent can call sub-agents as tools automatically. Each sub-agent's signature inputs become the tool's parameters, and its outputs are returned.
27
+
28
+ ## Simple Delegation (Researcher-Writer)
29
+
30
+ ```typescript
31
+ import { AxCrew } from 'ax-crew';
32
+ import type { AxCrewConfig } from 'ax-crew';
33
+
34
+ const config: AxCrewConfig = {
35
+ crew: [
36
+ {
37
+ name: "researcher",
38
+ description: "A research agent that finds information",
39
+ signature: "query:string -> research:string",
40
+ provider: "google-gemini",
41
+ providerKeyName: "GEMINI_API_KEY",
42
+ ai: { model: "gemini-2.5-flash-lite", maxTokens: 4000 },
43
+ },
44
+ {
45
+ name: "writer",
46
+ description: "A writing agent that creates content",
47
+ signature: "topic:string -> article:string",
48
+ provider: "google-gemini",
49
+ providerKeyName: "GEMINI_API_KEY",
50
+ ai: { model: "gemini-2.5-flash-lite", maxTokens: 4000 },
51
+ agents: ["researcher"], // writer delegates research to researcher
52
+ },
53
+ ],
54
+ };
55
+
56
+ async function main() {
57
+ const crew = new AxCrew(config);
58
+ // addAllAgents resolves dependency order automatically
59
+ await crew.addAllAgents();
60
+
61
+ const writer = crew.agents?.get("writer");
62
+ const { article } = await writer!.forward({
63
+ topic: "Quantum Computing Benefits",
64
+ });
65
+ console.log(article);
66
+ crew.destroy();
67
+ }
68
+
69
+ main().catch(console.error);
70
+ ```
71
+
72
+ ## Multi-Agent Orchestration (Customer Support)
73
+
74
+ ```typescript
75
+ import { AxJSRuntime, AxJSRuntimePermission } from '@ax-llm/ax';
76
+ import { AxCrew } from 'ax-crew';
77
+ import type { AxCrewConfig } from 'ax-crew';
78
+
79
+ const runtime = new AxJSRuntime({
80
+ permissions: [AxJSRuntimePermission.TIMING],
81
+ });
82
+
83
+ const config: AxCrewConfig = {
84
+ crew: [
85
+ {
86
+ name: "PolicyLookupAgent",
87
+ description: "Looks up policy details in the provided knowledge base.",
88
+ executionMode: "axagent",
89
+ signature:
90
+ 'question:string -> answer:string "Looks up company policies and returns a concise answer"',
91
+ provider: "google-gemini",
92
+ providerKeyName: "GEMINI_API_KEY",
93
+ ai: { model: "gemini-2.5-flash", temperature: 0 },
94
+ axAgentOptions: { contextFields: [], runtime },
95
+ },
96
+ {
97
+ name: "BillingHelperAgent",
98
+ description: "Answers billing and account questions.",
99
+ executionMode: "axagent",
100
+ signature:
101
+ 'question:string -> answer:string "Resolves billing and account questions"',
102
+ provider: "google-gemini",
103
+ providerKeyName: "GEMINI_API_KEY",
104
+ ai: { model: "gemini-2.5-flash", temperature: 0 },
105
+ axAgentOptions: { contextFields: [], runtime },
106
+ },
107
+ {
108
+ name: "SentimentClassifierAgent",
109
+ description: "Classifies customer message sentiment.",
110
+ executionMode: "axagent",
111
+ signature: 'question:string -> sentiment:string "positive, negative, or neutral"',
112
+ provider: "google-gemini",
113
+ providerKeyName: "GEMINI_API_KEY",
114
+ ai: { model: "gemini-2.5-flash", temperature: 0 },
115
+ axAgentOptions: {
116
+ contextFields: [],
117
+ fields: { excluded: ["knowledgeBase", "userId"] },
118
+ runtime,
119
+ },
120
+ },
121
+ {
122
+ name: "CustomerSupportAgent",
123
+ description: "Routes queries to specialists and returns a final answer.",
124
+ executionMode: "axagent",
125
+ signature: "query:string, knowledgeBase:string, userId:string -> answer:string",
126
+ provider: "google-gemini",
127
+ providerKeyName: "GEMINI_API_KEY",
128
+ ai: { model: "gemini-2.5-flash", temperature: 0 },
129
+ agents: [
130
+ "PolicyLookupAgent",
131
+ "BillingHelperAgent",
132
+ "SentimentClassifierAgent",
133
+ ],
134
+ axAgentOptions: {
135
+ contextFields: ["knowledgeBase"],
136
+ fields: { shared: ["knowledgeBase", "userId"] },
137
+ runtime,
138
+ },
139
+ },
140
+ ],
141
+ };
142
+
143
+ async function main() {
144
+ const crew = new AxCrew(config);
145
+ await crew.addAllAgents();
146
+
147
+ const support = crew.agents?.get("CustomerSupportAgent");
148
+ const result = await support!.forward({
149
+ query: "What is your refund policy?",
150
+ knowledgeBase: "Full refund within 30 days...",
151
+ userId: "cust-42",
152
+ });
153
+ console.log(result.answer);
154
+ crew.destroy();
155
+ }
156
+
157
+ main().catch(console.error);
158
+ ```
159
+
160
+ ## Dependency Resolution
161
+
162
+ `addAllAgents()` and `addAgentsToCrew()` resolve dependencies automatically:
163
+
164
+ ```typescript
165
+ // All agents initialized in dependency order
166
+ await crew.addAllAgents();
167
+
168
+ // Or add specific agents -- dependencies must be included or already added
169
+ await crew.addAgentsToCrew(["researcher"]); // add leaf first
170
+ await crew.addAgentsToCrew(["writer"]); // then parent
171
+ ```
172
+
173
+ Dependencies are checked: if agent A lists agent B in `agents[]`, B must be initialized first. `addAllAgents()` handles this automatically via topological ordering.
174
+
175
+ ## Lazy Agents (addLazyAgent)
176
+
177
+ Defers expensive initialization (MCP servers, AI client) until the agent is first called. Useful for sub-agents that may not be needed on every request.
178
+
179
+ ```typescript
180
+ const crew = new AxCrew(config);
181
+
182
+ // Eager: initialize immediately
183
+ await crew.addAgentsToCrew(["MainAgent"]);
184
+
185
+ // Lazy: tool schema is registered immediately, but actual agent
186
+ // initialization (MCP server startup, etc.) is deferred until first call
187
+ crew.addLazyAgent("RarelyUsedAgent");
188
+ ```
189
+
190
+ The lazy agent exposes the same `getFunction()` interface. When the parent agent delegates to it, the real agent is created on-demand.
191
+
192
+ ## Do Not Generate
193
+
194
+ - Do NOT list an agent in `agents[]` that is not defined in the crew config
195
+ - Do NOT manually wire sub-agent tool calls -- AxCrew does this automatically from the `agents[]` field
196
+ - Do NOT assume sub-agents share parent state by default -- use `axAgentOptions.fields.shared` for shared fields
197
+ - Do NOT add sub-agents after their parent without using `addAllAgents()` or correct ordering in `addAgentsToCrew()`
198
+ - Do NOT use `addLazyAgent()` for agents that are always needed -- it adds latency on first call
199
+
200
+ ## References
201
+
202
+ - [basic-researcher-writer.ts](../examples/basic-researcher-writer.ts) -- simple delegation
203
+ - [rlm-shared-fields.ts](../examples/rlm-shared-fields.ts) -- multi-agent with shared fields
@@ -0,0 +1,161 @@
1
+ ---
2
+ name: ax-crew-telemetry
3
+ version: __VERSION__
4
+ description: "AxCrew telemetry: OpenTelemetry tracing, metrics, observability with tracer and meter injection."
5
+ tags: [telemetry, opentelemetry, tracing, metrics, observability, tracer, meter]
6
+ ---
7
+
8
+ # Telemetry
9
+
10
+ AxCrew accepts OpenTelemetry `tracer` and `meter` instances via the `AxCrewOptions.telemetry` object. When provided, all agent `forward()` and `streamingForward()` calls emit spans and record token/cost metrics automatically.
11
+
12
+ ## AxCrewOptions.telemetry
13
+
14
+ ```ts
15
+ interface AxCrewOptions {
16
+ debug?: boolean;
17
+ telemetry?: {
18
+ tracer?: any; // OpenTelemetry Tracer instance
19
+ meter?: any; // OpenTelemetry Meter instance
20
+ };
21
+ }
22
+ ```
23
+
24
+ ## Setup
25
+
26
+ ```ts
27
+ // Required packages:
28
+ // npm install @opentelemetry/api @opentelemetry/sdk-trace-node @opentelemetry/sdk-metrics
29
+
30
+ import { metrics, trace } from "@opentelemetry/api";
31
+ import { ConsoleSpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
32
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
33
+ import { ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
34
+
35
+ // Tracing
36
+ const tracerProvider = new NodeTracerProvider({
37
+ spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())],
38
+ });
39
+ tracerProvider.register();
40
+
41
+ // Metrics
42
+ const meterProvider = new MeterProvider({
43
+ readers: [
44
+ new PeriodicExportingMetricReader({
45
+ exporter: new ConsoleMetricExporter(),
46
+ exportIntervalMillis: 5000,
47
+ }),
48
+ ],
49
+ });
50
+ metrics.setGlobalMeterProvider(meterProvider);
51
+
52
+ // Get instances
53
+ const tracer = trace.getTracer("ax-crew-example");
54
+ const meter = metrics.getMeter("ax-crew-example");
55
+ ```
56
+
57
+ ## Passing to AxCrew
58
+
59
+ ```ts
60
+ const crew = new AxCrew(crewConfig, functionsRegistry, {
61
+ telemetry: { tracer, meter },
62
+ });
63
+ ```
64
+
65
+ The third argument to `AxCrew` is `AxCrewOptions`. Telemetry is optional -- omit it and no spans or metrics are emitted.
66
+
67
+ ## Canonical Pattern
68
+
69
+ Full working example from `examples/telemetry-demo.ts`:
70
+
71
+ ```ts
72
+ import { AxCrew } from "ax-crew";
73
+ import { AxCrewFunctions } from "ax-crew/functions";
74
+ import type { AxCrewConfig, Provider } from "ax-crew";
75
+ import { metrics, trace } from "@opentelemetry/api";
76
+ import { ConsoleSpanExporter, SimpleSpanProcessor } from "@opentelemetry/sdk-trace-base";
77
+ import { NodeTracerProvider } from "@opentelemetry/sdk-trace-node";
78
+ import { ConsoleMetricExporter, MeterProvider, PeriodicExportingMetricReader } from "@opentelemetry/sdk-metrics";
79
+
80
+ // Setup OpenTelemetry
81
+ const tracerProvider = new NodeTracerProvider({
82
+ spanProcessors: [new SimpleSpanProcessor(new ConsoleSpanExporter())],
83
+ });
84
+ tracerProvider.register();
85
+
86
+ const meterProvider = new MeterProvider({
87
+ readers: [
88
+ new PeriodicExportingMetricReader({
89
+ exporter: new ConsoleMetricExporter(),
90
+ exportIntervalMillis: 5000,
91
+ }),
92
+ ],
93
+ });
94
+ metrics.setGlobalMeterProvider(meterProvider);
95
+
96
+ const tracer = trace.getTracer("my-app");
97
+ const meter = metrics.getMeter("my-app");
98
+
99
+ // Crew config
100
+ const crewConfig: AxCrewConfig = {
101
+ crew: [
102
+ {
103
+ name: "Researcher",
104
+ description: "Researches a topic and provides facts.",
105
+ signature: "topic:string -> facts:string[]",
106
+ provider: "openai" as Provider,
107
+ providerKeyName: "OPENAI_API_KEY",
108
+ ai: { model: "gpt-4o-mini", temperature: 0.7 },
109
+ functions: ["CurrentDateTime"],
110
+ },
111
+ {
112
+ name: "Writer",
113
+ description: "Writes a blog post from facts.",
114
+ signature: "facts:string[] -> blogPost:string",
115
+ provider: "google-gemini" as Provider,
116
+ providerKeyName: "GEMINI_API_KEY",
117
+ ai: { model: "gemini-flash-latest", temperature: 0.7 },
118
+ },
119
+ ],
120
+ };
121
+
122
+ async function main() {
123
+ const crew = new AxCrew(crewConfig, AxCrewFunctions, {
124
+ telemetry: { tracer, meter },
125
+ });
126
+
127
+ await crew.addAgent("Researcher");
128
+ await crew.addAgent("Writer");
129
+
130
+ const researcher = crew.agents!.get("Researcher")!;
131
+ const writer = crew.agents!.get("Writer")!;
132
+
133
+ const researchResult = await researcher.forward({
134
+ topic: "The future of AI agents",
135
+ });
136
+
137
+ const writerResult = await writer.forward({
138
+ facts: researchResult.facts,
139
+ });
140
+
141
+ console.log(writerResult.blogPost);
142
+
143
+ // Wait for metric export flush
144
+ await new Promise((resolve) => setTimeout(resolve, 6000));
145
+ crew.destroy();
146
+ }
147
+
148
+ main().catch(console.error);
149
+ ```
150
+
151
+ ## Do Not Generate
152
+
153
+ - Do NOT import OpenTelemetry types from `ax-crew` -- import them from `@opentelemetry/api` and the SDK packages.
154
+ - Do NOT pass the `MeterProvider` or `NodeTracerProvider` directly -- pass the `tracer` and `meter` instances obtained via `trace.getTracer()` and `metrics.getMeter()`.
155
+ - Do NOT forget to call `tracerProvider.register()` before creating the crew -- spans will not be emitted otherwise.
156
+ - Do NOT expect telemetry to work without installing `@opentelemetry/api`, `@opentelemetry/sdk-trace-node`, and `@opentelemetry/sdk-metrics`.
157
+
158
+ ## References
159
+
160
+ - [telemetry-demo.ts](https://github.com/amitdeshmukh/ax-crew/blob/main/examples/telemetry-demo.ts)
161
+ - [OpenTelemetry JS](https://opentelemetry.io/docs/languages/js/)