modular-agent-examples 0.0.1

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.
Files changed (44) hide show
  1. package/chunking-demo.ts +339 -0
  2. package/cleanup-duplicates.ts +142 -0
  3. package/data/flower.jpg +0 -0
  4. package/generative.ts +128 -0
  5. package/graph/context-example.ts +209 -0
  6. package/graph/data-pipeline/agents.ts +60 -0
  7. package/graph/data-pipeline/fetchers.ts +166 -0
  8. package/graph/data-pipeline/index.ts +282 -0
  9. package/graph/index.ts +154 -0
  10. package/graph/map-example.ts +227 -0
  11. package/graph/metrics-example.ts +238 -0
  12. package/graph/parallel-example.ts +167 -0
  13. package/graph/pipeline-example.ts +225 -0
  14. package/graph/planning-example.ts +406 -0
  15. package/graph/router-example.ts +226 -0
  16. package/graph/sequential-example.ts +141 -0
  17. package/graph/voting-example.ts +159 -0
  18. package/graph-rag/docker-compose.yaml +14 -0
  19. package/graph-rag/index.js +99 -0
  20. package/graph-rag/init-db.sh +7 -0
  21. package/graph-rag/package.json +15 -0
  22. package/history-compression-example.ts +163 -0
  23. package/history-persistence.ts +347 -0
  24. package/index.ts +175 -0
  25. package/ingestion-pipeline.ts +353 -0
  26. package/mcp-airbnb-example.ts +69 -0
  27. package/mcp-http-example.ts +70 -0
  28. package/mcp-stdio-example.ts +63 -0
  29. package/multimodal.ts +144 -0
  30. package/ollama.ts +148 -0
  31. package/openai-compatible.ts +141 -0
  32. package/opensearch-vector-store.ts +342 -0
  33. package/package.json +24 -0
  34. package/pubmed.ts +289 -0
  35. package/reasoning-with-sub-agent.ts +311 -0
  36. package/synchronous/index.ts +48 -0
  37. package/tsconfig.json +8 -0
  38. package/vector-store-filtering.ts +303 -0
  39. package/vector-store.ts +210 -0
  40. package/vectorstore/index.ts +0 -0
  41. package/vectorstore/store/dbService.ts +80 -0
  42. package/voyage-embeddings.ts +99 -0
  43. package/weather-with-sub-agent.ts +276 -0
  44. package/weather.ts +389 -0
@@ -0,0 +1,276 @@
1
+ /**
2
+ * Weather with Sub-Agent Example
3
+ *
4
+ * This example demonstrates using an agent as a tool for another agent.
5
+ * A cheap "weatherman" agent handles the tool calls (geocoding + weather API),
6
+ * while a main agent coordinates and provides the final response.
7
+ *
8
+ * Architecture:
9
+ * Main Agent (expensive model, no tools)
10
+ * └── Weatherman Agent (cheap model, has geocoding + weather tools)
11
+ */
12
+
13
+ import "dotenv/config";
14
+ import { createInterface } from "node:readline/promises";
15
+ import { ClaudeAgent } from "../lib/agents/anthropic/ClaudeAgent";
16
+ import { OpenAiAgent } from "../lib/agents/openai/OpenAiAgent";
17
+ import { MistralAgent } from "../lib/agents/mistral/MistralAgent";
18
+ import { OllamaAgent } from "../lib/agents/ollama/OllamaAgent";
19
+ import { BaseAgent } from "../lib/agents/BaseAgent";
20
+ import { Tool } from "../lib/tools/Tool";
21
+
22
+ const rl = createInterface({
23
+ input: process.stdin,
24
+ output: process.stdout,
25
+ });
26
+
27
+ // =============================================================================
28
+ // Weather Tools (used by the weatherman sub-agent)
29
+ // =============================================================================
30
+
31
+ const geoCodingTool = new Tool({
32
+ name: "geocoding",
33
+ description: `Look up coordinates for a location. Returns city name, latitude, longitude, country, and timezone.`,
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ term: {
38
+ type: "string",
39
+ description: "Location name to search for (city, town, etc.)",
40
+ },
41
+ },
42
+ required: ["term"],
43
+ },
44
+ execute: async (input: { term: string }): Promise<unknown> => {
45
+ const res = await fetch(
46
+ `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(
47
+ input.term
48
+ )}&count=3&language=en&format=json`
49
+ ).then((res) => res.json());
50
+
51
+ if (!res.results || res.results.length === 0) {
52
+ return { error: `No location found for "${input.term}"` };
53
+ }
54
+
55
+ return {
56
+ locations: res.results.map((r: Record<string, unknown>) => ({
57
+ name: r.name,
58
+ country: r.country,
59
+ latitude: r.latitude,
60
+ longitude: r.longitude,
61
+ timezone: r.timezone,
62
+ })),
63
+ };
64
+ },
65
+ });
66
+
67
+ const weatherTool = new Tool({
68
+ name: "weather",
69
+ description: `Get current weather for a location using latitude and longitude coordinates.`,
70
+ inputSchema: {
71
+ type: "object",
72
+ properties: {
73
+ lat: {
74
+ type: "number",
75
+ description: "Latitude",
76
+ },
77
+ long: {
78
+ type: "number",
79
+ description: "Longitude",
80
+ },
81
+ },
82
+ required: ["lat", "long"],
83
+ },
84
+ execute: async (input: { lat: number; long: number }): Promise<unknown> => {
85
+ const res = await fetch(
86
+ `https://api.open-meteo.com/v1/forecast?latitude=${input.lat}&longitude=${input.long}&current=temperature_2m,wind_speed_10m,weather_code&daily=temperature_2m_max,temperature_2m_min&timezone=auto`
87
+ ).then((res) => res.json());
88
+
89
+ return {
90
+ current: {
91
+ temperature: res.current?.temperature_2m,
92
+ unit: res.current_units?.temperature_2m,
93
+ windSpeed: res.current?.wind_speed_10m,
94
+ windUnit: res.current_units?.wind_speed_10m,
95
+ },
96
+ daily: res.daily
97
+ ? {
98
+ dates: res.daily.time?.slice(0, 3),
99
+ maxTemps: res.daily.temperature_2m_max?.slice(0, 3),
100
+ minTemps: res.daily.temperature_2m_min?.slice(0, 3),
101
+ }
102
+ : null,
103
+ };
104
+ },
105
+ });
106
+
107
+ // =============================================================================
108
+ // Sub-Agent Creation (the cheap weatherman)
109
+ // =============================================================================
110
+
111
+ const WEATHERMAN_DESCRIPTION = `You are a weather data retrieval specialist. Your job is to:
112
+ 1. Use the geocoding tool to find coordinates for the requested location
113
+ 2. Use the weather tool to get the current weather data
114
+ 3. Return the raw weather data in a structured format
115
+
116
+ Be efficient and only make the necessary tool calls. Return the data concisely.`;
117
+
118
+ function createWeathermanAgent(
119
+ provider: "claude" | "openai" | "mistral" | "ollama"
120
+ ): BaseAgent {
121
+ const config = {
122
+ id: "weatherman",
123
+ name: "Weatherman",
124
+ description: WEATHERMAN_DESCRIPTION,
125
+ tools: [geoCodingTool, weatherTool],
126
+ maxTokens: 1024,
127
+ };
128
+
129
+ if (provider === "openai") {
130
+ return new OpenAiAgent({
131
+ ...config,
132
+ apiKey: process.env.OPENAI_API_KEY as string,
133
+ model: "gpt-4o-mini", // Cheap model for tool calls
134
+ });
135
+ }
136
+
137
+ if (provider === "mistral") {
138
+ return new MistralAgent({
139
+ ...config,
140
+ apiKey: process.env.MISTRAL_API_KEY as string,
141
+ });
142
+ }
143
+ if (provider === "ollama") {
144
+ return new OllamaAgent({
145
+ ...config,
146
+ apiKey: "",
147
+ model: "gemma4:e4b",
148
+ think: false,
149
+ });
150
+ }
151
+ return new ClaudeAgent({
152
+ ...config,
153
+ apiKey: process.env.ANTHROPIC_API_KEY as string,
154
+ model: "claude-haiku-4-5", // Cheap model for tool calls
155
+ });
156
+ }
157
+
158
+ // =============================================================================
159
+ // Main Agent Creation (the coordinator)
160
+ // =============================================================================
161
+
162
+ const MAIN_AGENT_DESCRIPTION = `You are a helpful weather assistant. You have access to a weatherman assistant who can fetch weather data for any location.
163
+
164
+ When the user asks about weather:
165
+ 1. Ask the weatherman to get the weather data
166
+ 2. Interpret the data and provide a helpful, conversational response
167
+ 3. Include practical advice based on the weather conditions
168
+
169
+ Be friendly and helpful. Add interesting observations about the weather when appropriate.`;
170
+
171
+ function createMainAgent(
172
+ provider: "claude" | "openai" | "mistral" | "ollama",
173
+ weathermanTool: Tool<string>
174
+ ): BaseAgent {
175
+ const config = {
176
+ id: "main",
177
+ name: "Weather Assistant",
178
+ description: MAIN_AGENT_DESCRIPTION,
179
+ tools: [weathermanTool],
180
+ maxTokens: 2048,
181
+ };
182
+
183
+ if (provider === "openai") {
184
+ return new OpenAiAgent({
185
+ ...config,
186
+ apiKey: process.env.OPENAI_API_KEY as string,
187
+ model: "gpt-4o", // More capable model for the main agent
188
+ });
189
+ }
190
+
191
+ if (provider === "mistral") {
192
+ return new MistralAgent({
193
+ ...config,
194
+ apiKey: process.env.MISTRAL_API_KEY as string,
195
+ model: "mistral-large-latest",
196
+ });
197
+ }
198
+ if (provider === "ollama") {
199
+ return new OllamaAgent({
200
+ ...config,
201
+ apiKey: "",
202
+ model: "gemma4:e4b",
203
+ think: false,
204
+ });
205
+ }
206
+ return new ClaudeAgent({
207
+ ...config,
208
+ apiKey: process.env.ANTHROPIC_API_KEY as string,
209
+ model: "claude-sonnet-4-5", // More capable model for the main agent
210
+ });
211
+ }
212
+
213
+ // =============================================================================
214
+ // Main
215
+ // =============================================================================
216
+
217
+ async function main() {
218
+ console.log("=== Weather Assistant with Sub-Agent ===\n");
219
+ console.log(
220
+ "This example uses a cheap 'weatherman' agent to handle API calls,"
221
+ );
222
+ console.log("while a smarter main agent provides the final response.\n");
223
+
224
+ // Choose provider
225
+ const providerChoice = await rl.question(
226
+ "Which provider? [1] Claude (default), [2] OpenAI, or [3] Mistral or [4] Ollama: "
227
+ );
228
+ let provider: "claude" | "openai" | "mistral" | "ollama" = "claude";
229
+ if (providerChoice === "2") {
230
+ provider = "openai";
231
+ } else if (providerChoice === "3") {
232
+ provider = "mistral";
233
+ } else if (providerChoice === "4") {
234
+ provider = "ollama";
235
+ }
236
+ console.log(`Using ${provider}\n`);
237
+
238
+ // Create the weatherman sub-agent
239
+ const weathermanAgent = createWeathermanAgent(provider);
240
+ console.log("Created weatherman sub-agent (cheap model for tool calls)");
241
+
242
+ // Wrap the weatherman agent as a tool
243
+ const weathermanTool = Tool.fromAgent(
244
+ weathermanAgent,
245
+ "Use this assistant to get weather data for any location. Provide the location name and it will return current weather conditions and a 3-day forecast."
246
+ );
247
+ console.log("Wrapped weatherman as a tool for the main agent");
248
+
249
+ // Create the main agent with the weatherman tool
250
+ const mainAgent = createMainAgent(provider, weathermanTool);
251
+ console.log("Created main agent (smarter model for responses)\n");
252
+
253
+ // Get weather query from user
254
+ const location = await rl.question(
255
+ "For what location do you want to know the weather?\n> "
256
+ );
257
+
258
+ console.log("\nProcessing your request...\n");
259
+ console.log("--- Agent Activity ---");
260
+
261
+ try {
262
+ const result = await mainAgent.execute(
263
+ `What's the weather like in ${location}? Please provide current conditions and any advice for the day.`
264
+ );
265
+
266
+ console.log("\n--- Response ---");
267
+ console.log(result);
268
+ } catch (error) {
269
+ console.error("Error:", error);
270
+ }
271
+
272
+ rl.close();
273
+ process.exit(0);
274
+ }
275
+
276
+ main().catch(console.error);
package/weather.ts ADDED
@@ -0,0 +1,389 @@
1
+ /**
2
+ * Weather Agent Example
3
+ *
4
+ * Demonstrates a multi-provider weather agent that:
5
+ * - Uses geocoding to find locations
6
+ * - Fetches weather data from Open-Meteo API
7
+ * - Supports Claude, OpenAI, Mistral, and Gemini
8
+ * - Includes proper error handling, type safety, and validation
9
+ */
10
+
11
+ import "dotenv/config";
12
+ import { ClaudeAgent } from "../lib/agents/anthropic/ClaudeAgent";
13
+ import { MistralAgent } from "../lib/agents/mistral/MistralAgent";
14
+ import { OpenAiAgent } from "../lib/agents/openai/OpenAiAgent";
15
+ import { GeminiAgent } from "../lib";
16
+ import { Tool } from "../lib/tools/Tool";
17
+ import { createInterface } from "node:readline/promises";
18
+ import { BaseAgent } from "../lib/agents/BaseAgent";
19
+
20
+ // =============================================================================
21
+ // Type Definitions
22
+ // =============================================================================
23
+
24
+ interface GeocodingInput {
25
+ term: string;
26
+ }
27
+
28
+ interface Location {
29
+ name: string;
30
+ latitude: number;
31
+ longitude: number;
32
+ country: string;
33
+ country_code: string;
34
+ timezone: string;
35
+ elevation?: number;
36
+ feature_code?: string;
37
+ population?: number;
38
+ postcodes?: string[];
39
+ }
40
+
41
+ interface GeocodingResult {
42
+ results?: Location[];
43
+ generationtime_ms?: number;
44
+ }
45
+
46
+ interface WeatherInput {
47
+ lat: number;
48
+ long: number;
49
+ }
50
+
51
+ interface CurrentWeather {
52
+ temperature_2m: number;
53
+ wind_speed_10m: number;
54
+ time: string;
55
+ }
56
+
57
+ interface HourlyWeather {
58
+ temperature_2m: number[];
59
+ relative_humidity_2m: number[];
60
+ time: string[];
61
+ }
62
+
63
+ interface WeatherResult {
64
+ current: CurrentWeather;
65
+ current_units: {
66
+ temperature_2m: string;
67
+ wind_speed_10m: string;
68
+ };
69
+ hourly: HourlyWeather;
70
+ hourly_units: {
71
+ temperature_2m: string;
72
+ relative_humidity_2m: string;
73
+ };
74
+ latitude: number;
75
+ longitude: number;
76
+ timezone: string;
77
+ }
78
+
79
+ type Provider = "claude" | "openai" | "mistral" | "gemini";
80
+
81
+ // =============================================================================
82
+ // Tools with Proper Type Safety and Error Handling
83
+ // =============================================================================
84
+
85
+ const geoCodingTool = new Tool({
86
+ name: "geocodingTool",
87
+ description: `This tool accepts a search term and returns a list of matching locations.
88
+ Returns an array of results including city name, latitude, longitude, elevation, feature_code, country_code, timezone, population, and postcodes.`,
89
+ inputSchema: {
90
+ type: "object",
91
+ properties: {
92
+ term: {
93
+ type: "string",
94
+ description:
95
+ "String to search for. An empty string or only 1 character will return an empty result. 2 characters will only match exact matching locations. 3 and more characters will perform fuzzy matching. The search string can be a location name or a postal code, should NOT contain a country or state code.",
96
+ },
97
+ },
98
+ required: ["term"],
99
+ },
100
+ execute: async (input: GeocodingInput): Promise<GeocodingResult> => {
101
+ try {
102
+ if (!input.term || input.term.trim().length === 0) {
103
+ throw new Error("Search term cannot be empty");
104
+ }
105
+
106
+ // Encode URL parameter to prevent injection
107
+ const encodedTerm = encodeURIComponent(input.term.trim());
108
+ const url = `https://geocoding-api.open-meteo.com/v1/search?name=${encodedTerm}&count=10&language=en&format=json`;
109
+
110
+ const response = await fetch(url);
111
+
112
+ if (!response.ok) {
113
+ throw new Error(
114
+ `Geocoding API error: ${response.status} ${response.statusText}`
115
+ );
116
+ }
117
+
118
+ const data = (await response.json()) as GeocodingResult;
119
+
120
+ // Return empty results if no locations found
121
+ if (!data.results || data.results.length === 0) {
122
+ return { results: [] };
123
+ }
124
+
125
+ return data;
126
+ } catch (error) {
127
+ const errorMessage =
128
+ error instanceof Error ? error.message : "Unknown error";
129
+ throw new Error(`Failed to geocode location: ${errorMessage}`);
130
+ }
131
+ },
132
+ });
133
+
134
+ const weatherTool = new Tool({
135
+ name: "weatherTool",
136
+ description: `This tool accepts latitude and longitude coordinates and returns the current weather forecast for the location.`,
137
+ inputSchema: {
138
+ type: "object",
139
+ properties: {
140
+ lat: {
141
+ type: "number",
142
+ description: "Latitude for the location.",
143
+ },
144
+ long: {
145
+ type: "number",
146
+ description: "Longitude for the location.",
147
+ },
148
+ },
149
+ required: ["lat", "long"],
150
+ },
151
+ execute: async (input: WeatherInput): Promise<WeatherResult> => {
152
+ try {
153
+ // Validate coordinates
154
+ if (input.lat < -90 || input.lat > 90) {
155
+ throw new Error("Latitude must be between -90 and 90");
156
+ }
157
+ if (input.long < -180 || input.long > 180) {
158
+ throw new Error("Longitude must be between -180 and 180");
159
+ }
160
+
161
+ const url = `https://api.open-meteo.com/v1/forecast?latitude=${input.lat}&longitude=${input.long}&current=temperature_2m,wind_speed_10m&hourly=temperature_2m,relative_humidity_2m`;
162
+
163
+ const response = await fetch(url);
164
+
165
+ if (!response.ok) {
166
+ throw new Error(
167
+ `Weather API error: ${response.status} ${response.statusText}`
168
+ );
169
+ }
170
+
171
+ const data = (await response.json()) as WeatherResult;
172
+ return data;
173
+ } catch (error) {
174
+ const errorMessage =
175
+ error instanceof Error ? error.message : "Unknown error";
176
+ throw new Error(`Failed to fetch weather data: ${errorMessage}`);
177
+ }
178
+ },
179
+ });
180
+
181
+ const tools = [geoCodingTool, weatherTool];
182
+
183
+ // =============================================================================
184
+ // Agent Configuration
185
+ // =============================================================================
186
+
187
+ const AGENT_DESCRIPTION = `You are an agent that gets the weather for a specific location. You are concise and to the point.
188
+ Do NOT ask follow up questions, but end the conversation. Format the output as JSON in the following format:
189
+ <tooluse>List the tools used</tooluse>
190
+ <result>{
191
+ textContent: textual description of the weather,
192
+ currentTempinC: Number,
193
+ currentWind: Number,
194
+ currentPrecip: 'None' | 'light' | 'heavy'
195
+ }</result>`;
196
+
197
+ /**
198
+ * Create the agent based on provider choice
199
+ */
200
+ function createAgent(provider: Provider): BaseAgent {
201
+ const commonConfig = {
202
+ id: "weather-agent",
203
+ name: "Weather Agent",
204
+ description: AGENT_DESCRIPTION,
205
+ tools,
206
+ };
207
+
208
+ switch (provider) {
209
+ case "openai": {
210
+ const apiKey = process.env.OPENAI_API_KEY;
211
+ if (!apiKey) {
212
+ throw new Error("OPENAI_API_KEY environment variable is not set");
213
+ }
214
+ return new OpenAiAgent({
215
+ ...commonConfig,
216
+ model: "gpt-4o-mini",
217
+ apiKey,
218
+ });
219
+ }
220
+
221
+ case "gemini": {
222
+ const apiKey = process.env.GEMINI_API_KEY;
223
+ if (!apiKey) {
224
+ throw new Error("GEMINI_API_KEY environment variable is not set");
225
+ }
226
+ return new GeminiAgent({
227
+ ...commonConfig,
228
+ model: "gemini-flash-lite-latest",
229
+ apiKey,
230
+ });
231
+ }
232
+
233
+ case "mistral": {
234
+ const apiKey = process.env.MISTRAL_API_KEY;
235
+ if (!apiKey) {
236
+ throw new Error("MISTRAL_API_KEY environment variable is not set");
237
+ }
238
+ return new MistralAgent({
239
+ ...commonConfig,
240
+ model: "ministral-3b-latest",
241
+ apiKey,
242
+ disableParallelToolUse: false,
243
+ });
244
+ }
245
+
246
+ case "claude":
247
+ default: {
248
+ const apiKey = process.env.ANTHROPIC_API_KEY;
249
+ if (!apiKey) {
250
+ throw new Error("ANTHROPIC_API_KEY environment variable is not set");
251
+ }
252
+ return new ClaudeAgent({
253
+ ...commonConfig,
254
+ model: "claude-haiku-4-5",
255
+ apiKey,
256
+ });
257
+ }
258
+ }
259
+ }
260
+
261
+ /**
262
+ * Validate user input for location
263
+ */
264
+ function validateLocationInput(input: string): string {
265
+ const trimmed = input.trim();
266
+
267
+ if (!trimmed) {
268
+ throw new Error("Location cannot be empty");
269
+ }
270
+
271
+ if (trimmed.length < 2) {
272
+ throw new Error("Location must be at least 2 characters");
273
+ }
274
+
275
+ // Basic sanitization - remove potentially dangerous characters
276
+ const sanitized = trimmed.replace(/[<>]/g, "");
277
+
278
+ return sanitized;
279
+ }
280
+
281
+ /**
282
+ * Parse provider choice from user input
283
+ */
284
+ function parseProviderChoice(choice: string): Provider {
285
+ const trimmed = choice.trim();
286
+
287
+ switch (trimmed) {
288
+ case "2":
289
+ return "openai";
290
+ case "3":
291
+ return "mistral";
292
+ case "4":
293
+ return "gemini";
294
+ case "1":
295
+ case "":
296
+ default:
297
+ return "claude";
298
+ }
299
+ }
300
+
301
+ // =============================================================================
302
+ // Main Function
303
+ // =============================================================================
304
+
305
+ async function getWeatherExample(): Promise<void> {
306
+ const rl = createInterface({
307
+ input: process.stdin,
308
+ output: process.stdout,
309
+ });
310
+
311
+ try {
312
+ console.log("=== Weather Agent Example ===\n");
313
+
314
+ // Choose provider
315
+ const providerChoice = await rl.question(
316
+ "Which provider? [1] Claude (default), [2] OpenAI, [3] Mistral, [4] Gemini: "
317
+ );
318
+ const provider = parseProviderChoice(providerChoice);
319
+
320
+ console.log(`Using ${provider}\n`);
321
+
322
+ // Create agent
323
+ let agent: BaseAgent;
324
+ try {
325
+ agent = createAgent(provider);
326
+ } catch (error) {
327
+ const errorMessage =
328
+ error instanceof Error ? error.message : "Unknown error";
329
+ console.error(`Failed to create agent: ${errorMessage}`);
330
+ console.error(
331
+ "Please ensure the appropriate API key is set in your .env file"
332
+ );
333
+ rl.close();
334
+ process.exit(1);
335
+ }
336
+
337
+ // Get location from user
338
+ const locationInput = await rl.question(
339
+ "For what location do you want to know the weather?\n> "
340
+ );
341
+
342
+ let location: string;
343
+ try {
344
+ location = validateLocationInput(locationInput);
345
+ } catch (error) {
346
+ const errorMessage =
347
+ error instanceof Error ? error.message : "Invalid input";
348
+ console.error(`Input validation error: ${errorMessage}`);
349
+ rl.close();
350
+ process.exit(1);
351
+ }
352
+
353
+ // Execute agent
354
+ console.log("\nFetching weather data...\n");
355
+
356
+ const result = await agent.execute(
357
+ `Find the weather forecast for ${location}. Format the answer in the correct way. Give some advice about the weather as well. If the location isn't obvious, make an informed guess.`
358
+ );
359
+
360
+ console.log("\n=== Weather Forecast ===\n");
361
+ console.log(result);
362
+
363
+ // Display token usage if available (without exposing sensitive data)
364
+ const tokenUsage = (agent as { lastTokenUsage?: unknown }).lastTokenUsage;
365
+ if (tokenUsage) {
366
+ console.log(`\n--- Model: ${agent.getModel()} ---`);
367
+ console.log("Token usage:", JSON.stringify(tokenUsage, null, 2));
368
+ }
369
+
370
+ rl.close();
371
+ process.exit(0);
372
+ } catch (error) {
373
+ const errorMessage =
374
+ error instanceof Error ? error.message : "Unknown error";
375
+ console.error("\n=== Error ===");
376
+ console.error(errorMessage);
377
+
378
+ if (error instanceof Error && error.stack) {
379
+ console.error("\nStack trace:");
380
+ console.error(error.stack);
381
+ }
382
+
383
+ rl.close();
384
+ process.exit(1);
385
+ }
386
+ }
387
+
388
+ // Run the example
389
+ getWeatherExample();