@amitdeshmukh/ax-crew 6.0.0 → 8.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,329 @@
1
+ /**
2
+ * ACE Feedback Loop Demo - Google Flights Assistant
3
+ *
4
+ * This example demonstrates:
5
+ * - An agent using Google Flights MCP server for real flight searches
6
+ * - ACE learning from user feedback to improve recommendations
7
+ * - Interactive CLI for flight queries
8
+ * - Playbook persistence and display
9
+ *
10
+ * ═══════════════════════════════════════════════════════════════════
11
+ * SETUP: Google Flights MCP Server
12
+ * ═══════════════════════════════════════════════════════════════════
13
+ *
14
+ * This example uses the Google Flights MCP Server:
15
+ * https://github.com/opspawn/Google-Flights-MCP-Server
16
+ *
17
+ * Installation:
18
+ *
19
+ * 1. Clone the MCP server:
20
+ * git clone https://github.com/opspawn/Google-Flights-MCP-Server.git
21
+ * cd Google-Flights-MCP-Server
22
+ *
23
+ * 2. Create virtual environment and install dependencies:
24
+ * python -m venv .venv
25
+ * source .venv/bin/activate # On Windows: .venv\Scripts\activate
26
+ * pip install -r requirements.txt
27
+ *
28
+ * 3. Install Playwright browsers (needed by fast_flights):
29
+ * playwright install
30
+ *
31
+ * 4. Set environment variable and run from activated venv:
32
+ * export GOOGLE_FLIGHTS_SERVER_PATH="/path/to/Google-Flights-MCP-Server/server.py"
33
+ * # Python is auto-detected from your PATH (uses activated venv)
34
+ *
35
+ * Available MCP Tools:
36
+ * - get_flights_on_date: One-way flights for a specific date
37
+ * - get_round_trip_flights: Round-trip flights with departure/return dates
38
+ * - find_all_flights_in_range: Find flights within a date range
39
+ *
40
+ * ═══════════════════════════════════════════════════════════════════
41
+ *
42
+ * Usage: npx tsx examples/ace-feedback-routing.ts
43
+ */
44
+
45
+ import { AxCrew } from "../dist/index.js";
46
+ import { AxCrewFunctions } from "../dist/functions/index.js";
47
+ import type { AxCrewConfig } from "../dist/types.js";
48
+ import type { Provider } from "../dist/types.js";
49
+ import "dotenv/config";
50
+ import * as readline from "readline";
51
+
52
+ // --- 1. Configuration ---
53
+
54
+ // Google Flights MCP Server paths - set via env vars
55
+ // See: https://github.com/opspawn/Google-Flights-MCP-Server
56
+ const GOOGLE_FLIGHTS_SERVER = process.env.GOOGLE_FLIGHTS_SERVER_PATH
57
+ || "/path/to/Google-Flights-MCP-Server/server.py";
58
+ // Uses system python by default - activate your venv before running!
59
+ const GOOGLE_FLIGHTS_PYTHON = process.env.PYTHON_PATH || "python";
60
+
61
+ const crewConfig: AxCrewConfig = {
62
+ crew: [
63
+ {
64
+ name: "FlightAssistant",
65
+ description: `A helpful flight booking assistant that searches Google Flights and provides recommendations.`,
66
+ signature: "query:string -> recommendation:string",
67
+ provider: "google-gemini" as Provider,
68
+ providerKeyName: "GEMINI_API_KEY",
69
+ ai: {
70
+ model: "gemini-flash-latest",
71
+ temperature: 0.7,
72
+ },
73
+ options: {
74
+ debug: false,
75
+ stream: false
76
+ },
77
+ // Google Flights MCP Server - see https://github.com/opspawn/Google-Flights-MCP-Server
78
+ mcpServers: {
79
+ "google-flights": {
80
+ command: GOOGLE_FLIGHTS_PYTHON,
81
+ args: [GOOGLE_FLIGHTS_SERVER],
82
+ env: {}
83
+ }
84
+ },
85
+ // Enable ACE for learning from feedback
86
+ ace: {
87
+ teacher: {
88
+ provider: "google-gemini" as Provider,
89
+ providerKeyName: "GEMINI_API_KEY",
90
+ ai: { model: "gemini-flash-latest" }
91
+ },
92
+ options: {
93
+ maxEpochs: 1,
94
+ allowDynamicSections: true
95
+ },
96
+ persistence: {
97
+ playbookPath: "playbooks/flight-assistant.json",
98
+ autoPersist: true
99
+ },
100
+ metric: { primaryOutputField: "recommendation" },
101
+ compileOnStart: false,
102
+ }
103
+ }
104
+ ]
105
+ };
106
+
107
+ // --- 2. CLI Helper Functions ---
108
+
109
+ const rl = readline.createInterface({
110
+ input: process.stdin,
111
+ output: process.stdout
112
+ });
113
+
114
+ const prompt = (question: string): Promise<string> => {
115
+ return new Promise((resolve) => {
116
+ rl.question(question, (answer) => {
117
+ resolve(answer.trim());
118
+ });
119
+ });
120
+ };
121
+
122
+ const displayPlaybook = (playbook: any, agentName: string) => {
123
+ console.log(`\n📘 ACE Playbook for ${agentName}:`);
124
+ console.log("─".repeat(60));
125
+
126
+ if (!playbook) {
127
+ console.log(" (No playbook yet - will be created after first feedback)");
128
+ console.log("─".repeat(60));
129
+ return;
130
+ }
131
+
132
+ if (playbook.sections) {
133
+ for (const [sectionName, bullets] of Object.entries(playbook.sections)) {
134
+ console.log(`\n 📂 ${sectionName}:`);
135
+ if (Array.isArray(bullets)) {
136
+ bullets.forEach((bullet: any, i: number) => {
137
+ const content = typeof bullet === 'string' ? bullet : bullet.content || JSON.stringify(bullet);
138
+ console.log(` ${i + 1}. ${content}`);
139
+ });
140
+ }
141
+ }
142
+ } else {
143
+ console.log(" " + JSON.stringify(playbook, null, 2).replace(/\n/g, "\n "));
144
+ }
145
+
146
+ if (playbook.updatedAt) {
147
+ console.log(` 🕐 Last updated: ${new Date(playbook.updatedAt).toLocaleString()}`);
148
+ }
149
+
150
+ console.log("─".repeat(60));
151
+ };
152
+
153
+ const displayHelp = () => {
154
+ console.log("\n📋 Example flight queries:");
155
+ console.log(" • Find flights from JFK to LAX on 2025-02-15");
156
+ console.log(" • Round trip from SFO to LHR, leaving 2025-03-01 returning 2025-03-10");
157
+ console.log(" • Cheapest flights from NYC to Tokyo in March 2025");
158
+ console.log("\n📝 Feedback examples to train the assistant:");
159
+ console.log(" • Always show price in USD");
160
+ console.log(" • Prioritize direct flights");
161
+ console.log(" • Include flight duration");
162
+ };
163
+
164
+ // --- 3. Main Interactive Loop ---
165
+
166
+ async function main() {
167
+ console.log("\n✈️ ACE Flight Assistant - Google Flights Edition");
168
+ console.log("═".repeat(60));
169
+ console.log("Search real flights and train the assistant with feedback.");
170
+ console.log("MCP Server: https://github.com/opspawn/Google-Flights-MCP-Server\n");
171
+
172
+ // Check configuration
173
+ const isConfigured = !GOOGLE_FLIGHTS_SERVER.includes("/path/to/");
174
+ if (!isConfigured) {
175
+ console.log("⚠️ MCP Server not configured!");
176
+ console.log(" 1. Clone: git clone https://github.com/opspawn/Google-Flights-MCP-Server.git");
177
+ console.log(" 2. Setup: cd Google-Flights-MCP-Server && python -m venv .venv");
178
+ console.log(" 3. Activate: source .venv/bin/activate");
179
+ console.log(" 4. Install: pip install -r requirements.txt && playwright install");
180
+ console.log(" 5. Export: export GOOGLE_FLIGHTS_SERVER_PATH=/path/to/server.py");
181
+ console.log("\n Demo will continue but flight lookups won't work.\n");
182
+ }
183
+
184
+ // Initialize AxCrew
185
+ const crew = new AxCrew(crewConfig, AxCrewFunctions);
186
+
187
+ try {
188
+ console.log("⏳ Initializing Flight Assistant...");
189
+ await crew.addAgentsToCrew(["FlightAssistant"]);
190
+ const assistant = crew.agents!.get("FlightAssistant")!;
191
+
192
+ console.log("✅ Flight Assistant ready with ACE enabled");
193
+ if (isConfigured) {
194
+ console.log("✅ Google Flights MCP server connected\n");
195
+ }
196
+
197
+ // Show initial playbook (if loaded from persistence)
198
+ const initialPlaybook = (assistant as any).getPlaybook?.();
199
+ displayPlaybook(initialPlaybook, "FlightAssistant");
200
+
201
+ displayHelp();
202
+
203
+ let continueLoop = true;
204
+ let queryCount = 0;
205
+ let feedbackCount = 0;
206
+
207
+ while (continueLoop) {
208
+ console.log(`\n${"═".repeat(60)}`);
209
+ console.log(`✈️ Search #${queryCount + 1}`);
210
+ console.log("═".repeat(60));
211
+
212
+ // Get flight query from user
213
+ const query = await prompt("\n🔍 Flight query (or 'help'/'quit'): ");
214
+
215
+ if (query.toLowerCase() === 'quit' || query.toLowerCase() === 'exit' || query === '') {
216
+ continueLoop = false;
217
+ continue;
218
+ }
219
+
220
+ if (query.toLowerCase() === 'help') {
221
+ displayHelp();
222
+ continue;
223
+ }
224
+
225
+ if (query.toLowerCase() === 'playbook') {
226
+ const currentPlaybook = (assistant as any).getPlaybook?.();
227
+ displayPlaybook(currentPlaybook, "FlightAssistant");
228
+ continue;
229
+ }
230
+
231
+ queryCount++;
232
+ console.log("\n⏳ Searching Google Flights...\n");
233
+
234
+ try {
235
+ // Execute the query
236
+ const result = await assistant.forward({ query });
237
+ const taskId = (result as any)._taskId;
238
+
239
+ console.log("─".repeat(60));
240
+ console.log("🛫 Flight Results:");
241
+ console.log("─".repeat(60));
242
+ console.log(result.recommendation);
243
+ console.log("─".repeat(60));
244
+
245
+ // Get feedback from user
246
+ console.log("\n💬 Train the assistant (Enter feedback or press Enter to skip):");
247
+
248
+ const feedback = await prompt("📝 Feedback: ");
249
+
250
+ if (feedback.toLowerCase() === 'quit' || feedback.toLowerCase() === 'exit') {
251
+ continueLoop = false;
252
+ continue;
253
+ }
254
+
255
+ if (feedback && feedback.length > 0) {
256
+ console.log("\n⏳ Updating ACE playbook...");
257
+
258
+ // Apply feedback via ACE
259
+ if (taskId) {
260
+ await crew.applyTaskFeedback({
261
+ taskId,
262
+ feedback,
263
+ strategy: "all"
264
+ });
265
+ } else {
266
+ await (assistant as any).applyOnlineUpdate?.({
267
+ example: { query },
268
+ prediction: result,
269
+ feedback
270
+ });
271
+ }
272
+
273
+ feedbackCount++;
274
+ console.log("✅ Playbook updated!\n");
275
+
276
+ // Display updated playbook
277
+ const updatedPlaybook = (assistant as any).getPlaybook?.();
278
+ displayPlaybook(updatedPlaybook, "FlightAssistant");
279
+
280
+ console.log("\n💡 The assistant has learned from your feedback!");
281
+ } else {
282
+ console.log("\n⏭️ Skipped feedback.");
283
+ }
284
+
285
+ } catch (error: any) {
286
+ console.error(`\n❌ Error: ${error.message}`);
287
+ if (error.message.includes("MCP") || error.message.includes("transport")) {
288
+ console.log(" Check MCP server configuration.");
289
+ console.log(" See: https://github.com/opspawn/Google-Flights-MCP-Server");
290
+ }
291
+ }
292
+ }
293
+
294
+ // Final summary
295
+ console.log("\n" + "═".repeat(60));
296
+ console.log("📊 Session Summary");
297
+ console.log("═".repeat(60));
298
+ console.log(` Searches: ${queryCount}`);
299
+ console.log(` Feedback given: ${feedbackCount}`);
300
+
301
+ const finalPlaybook = (assistant as any).getPlaybook?.();
302
+ if (finalPlaybook?.sections) {
303
+ const bulletCount = Object.values(finalPlaybook.sections)
304
+ .reduce((acc: number, bullets: any) => acc + (Array.isArray(bullets) ? bullets.length : 0), 0);
305
+ console.log(` Playbook insights: ${bulletCount}`);
306
+ }
307
+ console.log(` Saved to: playbooks/flight-assistant.json`);
308
+
309
+ console.log("\n✈️ Thanks for using ACE Flight Assistant!");
310
+ console.log(" Your preferences are saved for next time.\n");
311
+
312
+ } catch (error: any) {
313
+ console.error("\n❌ Error:", error.message);
314
+ console.log("\nTroubleshooting:");
315
+ console.log("• Ensure OPENAI_API_KEY is set");
316
+ console.log("• For MCP: https://github.com/opspawn/Google-Flights-MCP-Server");
317
+ } finally {
318
+ crew.cleanupOldExecutions(60000);
319
+ crew.destroy();
320
+ rl.close();
321
+ }
322
+ }
323
+
324
+ // --- 4. Run ---
325
+
326
+ main().catch((error) => {
327
+ console.error("Fatal error:", error);
328
+ process.exit(1);
329
+ });
@@ -0,0 +1,165 @@
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
+ {
115
+ telemetry: {
116
+ tracer,
117
+ meter
118
+ }
119
+ }
120
+ );
121
+
122
+ try {
123
+ // Initialize agents
124
+ await crew.addAgent("Researcher");
125
+ await crew.addAgent("Writer");
126
+
127
+ const researcher = crew.agents!.get("Researcher")!;
128
+ const writer = crew.agents!.get("Writer")!;
129
+
130
+ // Step 1: Research
131
+ console.log("\n--- Step 1: Researching ---");
132
+ // This call will be traced, including the 'CurrentDateTime' tool usage
133
+ const researchResult = await researcher.forward({
134
+ topic: "The future of AI agents in 2025"
135
+ });
136
+ console.log("Research output:", researchResult.facts);
137
+
138
+ // Step 2: Writing
139
+ console.log("\n--- Step 2: Writing ---");
140
+ // This call will be traced under a different provider
141
+ const writerResult = await writer.forward({
142
+ facts: researchResult.facts
143
+ });
144
+ console.log("Blog Post:\n", writerResult.blogPost);
145
+
146
+ console.log("\n--- Done ---");
147
+ console.log("Check your console output above for OpenTelemetry traces and metrics.");
148
+ if (JaegerExporter) {
149
+ console.log("Check Jaeger UI at http://localhost:16686 for enhanced trace visualization.");
150
+ } else {
151
+ console.log("For enhanced visualization, install Jaeger: npm install @opentelemetry/exporter-jaeger");
152
+ console.log("Then run: docker run -d --name jaeger -p 16686:16686 -p 14268:14268 jaegertracing/all-in-one:latest");
153
+ }
154
+
155
+ // Wait a moment for metrics to export before exiting
156
+ await new Promise(resolve => setTimeout(resolve, 6000));
157
+
158
+ } catch (error) {
159
+ console.error("Error running crew:", error);
160
+ } finally {
161
+ crew.destroy();
162
+ }
163
+ }
164
+
165
+ 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": "8.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",