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,347 @@
1
+ /**
2
+ * History Persistence Example
3
+ *
4
+ * This example demonstrates:
5
+ * 1. Loading conversation history from a JSON file
6
+ * 2. Having a conversation with an AI agent that can use tools
7
+ * 3. Saving the history back to the file on exit
8
+ * 4. Tool calls are preserved in history and work across providers
9
+ *
10
+ * The history persists across sessions, so you can continue
11
+ * conversations where you left off.
12
+ */
13
+
14
+ import "dotenv/config";
15
+ import { createInterface } from "node:readline/promises";
16
+ import { readFile, writeFile } from "node:fs/promises";
17
+ import { existsSync } from "node:fs";
18
+ import { ClaudeAgent } from "../lib/agents/anthropic/ClaudeAgent";
19
+ import { OpenAiAgent } from "../lib/agents/openai/OpenAiAgent";
20
+ import { MistralAgent } from "../lib/agents/mistral/MistralAgent";
21
+ import { History, HistoryEntry } from "../lib/history/History";
22
+ import { BaseAgent } from "../lib/agents/BaseAgent";
23
+ import { Tool } from "../lib/tools/Tool";
24
+
25
+ const HISTORY_FILE = "./history.json";
26
+
27
+ const rl = createInterface({
28
+ input: process.stdin,
29
+ output: process.stdout,
30
+ });
31
+
32
+ // =============================================================================
33
+ // Tools
34
+ // =============================================================================
35
+
36
+ const geoCodingTool = new Tool({
37
+ name: "geocoding",
38
+ description: `Look up coordinates for a location. Returns city name, latitude, longitude, country, and timezone.`,
39
+ inputSchema: {
40
+ type: "object",
41
+ properties: {
42
+ term: {
43
+ type: "string",
44
+ description: "Location name to search for (city, town, etc.)",
45
+ },
46
+ },
47
+ required: ["term"],
48
+ },
49
+ execute: async (input: { term: string }): Promise<unknown> => {
50
+ const res = await fetch(
51
+ `https://geocoding-api.open-meteo.com/v1/search?name=${encodeURIComponent(
52
+ input.term
53
+ )}&count=3&language=en&format=json`
54
+ ).then((res) => res.json());
55
+
56
+ if (!res.results || res.results.length === 0) {
57
+ return { error: `No location found for "${input.term}"` };
58
+ }
59
+
60
+ // Return simplified results
61
+ return {
62
+ locations: res.results.map((r: Record<string, unknown>) => ({
63
+ name: r.name,
64
+ country: r.country,
65
+ latitude: r.latitude,
66
+ longitude: r.longitude,
67
+ timezone: r.timezone,
68
+ })),
69
+ };
70
+ },
71
+ });
72
+
73
+ const weatherTool = new Tool({
74
+ name: "weather",
75
+ description: `Get current weather for a location using latitude and longitude coordinates.`,
76
+ inputSchema: {
77
+ type: "object",
78
+ properties: {
79
+ lat: {
80
+ type: "number",
81
+ description: "Latitude",
82
+ },
83
+ long: {
84
+ type: "number",
85
+ description: "Longitude",
86
+ },
87
+ },
88
+ required: ["lat", "long"],
89
+ },
90
+ execute: async (input: { lat: number; long: number }): Promise<unknown> => {
91
+ const res = await fetch(
92
+ `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`
93
+ ).then((res) => res.json());
94
+
95
+ return {
96
+ current: {
97
+ temperature: res.current?.temperature_2m,
98
+ unit: res.current_units?.temperature_2m,
99
+ windSpeed: res.current?.wind_speed_10m,
100
+ windUnit: res.current_units?.wind_speed_10m,
101
+ },
102
+ daily: res.daily
103
+ ? {
104
+ dates: res.daily.time?.slice(0, 3),
105
+ maxTemps: res.daily.temperature_2m_max?.slice(0, 3),
106
+ minTemps: res.daily.temperature_2m_min?.slice(0, 3),
107
+ }
108
+ : null,
109
+ };
110
+ },
111
+ });
112
+
113
+ const tools = [geoCodingTool, weatherTool];
114
+
115
+ // =============================================================================
116
+ // History Management
117
+ // =============================================================================
118
+
119
+ /**
120
+ * Load history from JSON file if it exists
121
+ */
122
+ async function loadHistory(): Promise<History> {
123
+ if (existsSync(HISTORY_FILE)) {
124
+ try {
125
+ const data = await readFile(HISTORY_FILE, "utf-8");
126
+ const entries = JSON.parse(data) as HistoryEntry[];
127
+ console.log(
128
+ `Loaded ${entries.length} history entries from ${HISTORY_FILE}`
129
+ );
130
+ return new History(entries, { transient: false });
131
+ } catch (error) {
132
+ console.warn(`Could not load history file: ${error}`);
133
+ }
134
+ }
135
+ console.log("Starting with fresh history");
136
+ return new History([], { transient: false });
137
+ }
138
+
139
+ /**
140
+ * Save history to JSON file
141
+ */
142
+ async function saveHistory(history: History): Promise<void> {
143
+ try {
144
+ const json = history.toJSON();
145
+ await writeFile(HISTORY_FILE, json, "utf-8");
146
+ console.log(`Saved ${history.length} history entries to ${HISTORY_FILE}`);
147
+ } catch (error) {
148
+ console.error(`Failed to save history: ${error}`);
149
+ }
150
+ }
151
+
152
+ // =============================================================================
153
+ // Agent Creation
154
+ // =============================================================================
155
+
156
+ const AGENT_DESCRIPTION = `You are a helpful assistant with access to weather information.
157
+ You can look up weather for any location using the geocoding and weather tools.
158
+ You remember previous conversations and can reference them when relevant.
159
+ Be concise but helpful.`;
160
+
161
+ /**
162
+ * Create the agent with shared history
163
+ */
164
+ function createAgent(
165
+ provider: "claude" | "openai" | "mistral",
166
+ history: History
167
+ ): BaseAgent {
168
+ if (provider === "openai") {
169
+ return new OpenAiAgent(
170
+ {
171
+ id: "assistant",
172
+ name: "Assistant",
173
+ description: AGENT_DESCRIPTION,
174
+ apiKey: process.env.OPENAI_API_KEY as string,
175
+ model: "gpt-4o-mini",
176
+ maxTokens: 1024,
177
+ tools,
178
+ },
179
+ history
180
+ );
181
+ }
182
+
183
+ if (provider === "mistral") {
184
+ return new MistralAgent(
185
+ {
186
+ id: "assistant",
187
+ name: "Assistant",
188
+ description: AGENT_DESCRIPTION,
189
+ apiKey: process.env.MISTRAL_API_KEY as string,
190
+ maxTokens: 1024,
191
+ tools,
192
+ },
193
+ history
194
+ );
195
+ }
196
+
197
+ return new ClaudeAgent(
198
+ {
199
+ id: "assistant",
200
+ name: "Assistant",
201
+ description: AGENT_DESCRIPTION,
202
+ apiKey: process.env.ANTHROPIC_API_KEY as string,
203
+ model: "claude-haiku-4-5",
204
+ maxTokens: 1024,
205
+ tools,
206
+ },
207
+ history
208
+ );
209
+ }
210
+
211
+ // =============================================================================
212
+ // Main
213
+ // =============================================================================
214
+
215
+ async function main() {
216
+ // Load existing history
217
+ const history = await loadHistory();
218
+
219
+ // Show previous conversation summary if history exists
220
+ if (history.length > 0) {
221
+ console.log("\n--- Previous Conversation Summary ---");
222
+ const entries = history.entries.slice(-6); // Show last 6 entries
223
+ for (const entry of entries) {
224
+ if (entry.role === "system") continue;
225
+
226
+ // Show text content
227
+ const textParts = entry.content
228
+ .filter((c) => c.type === "text")
229
+ .map((c) => (c as { text: string }).text);
230
+
231
+ // Show tool use summary
232
+ const toolUses = entry.content.filter((c) => c.type === "tool_use");
233
+ const toolResults = entry.content.filter((c) => c.type === "tool_result");
234
+
235
+ if (textParts.length > 0) {
236
+ const preview = textParts.join(" ").slice(0, 80);
237
+ console.log(
238
+ `[${entry.role}]: ${preview}${preview.length >= 80 ? "..." : ""}`
239
+ );
240
+ }
241
+ if (toolUses.length > 0) {
242
+ const toolNames = toolUses
243
+ .map((t) => (t as { name: string }).name)
244
+ .join(", ");
245
+ console.log(`[${entry.role}]: 🔧 Used tools: ${toolNames}`);
246
+ }
247
+ if (toolResults.length > 0) {
248
+ console.log(`[${entry.role}]: 📋 Tool results received`);
249
+ }
250
+ }
251
+ console.log("------------------------------------\n");
252
+ }
253
+
254
+ // Choose provider
255
+ const providerChoice = await rl.question(
256
+ "Which provider? [1] Claude (default), [2] OpenAI, or [3] Mistral: "
257
+ );
258
+ let provider: "claude" | "openai" | "mistral" = "claude";
259
+ if (providerChoice === "2") {
260
+ provider = "openai";
261
+ } else if (providerChoice === "3") {
262
+ provider = "mistral";
263
+ }
264
+ console.log(`Using ${provider}`);
265
+
266
+ // Create agent with the loaded history
267
+ const agent = createAgent(provider, history);
268
+
269
+ console.log("\n----- Chat Assistant (with Weather Tools) -----");
270
+ console.log("Your conversation history is preserved between sessions.");
271
+ console.log("Try asking about the weather in different cities!");
272
+ console.log("Commands:");
273
+ console.log(" 'exit' - Save and quit");
274
+ console.log(" 'clear' - Clear history and start fresh");
275
+ console.log(" 'history' - Show conversation history");
276
+ console.log("----------------------------------------------\n");
277
+
278
+ let running = true;
279
+
280
+ // Handle graceful shutdown
281
+ const shutdown = async () => {
282
+ console.log("\nSaving history...");
283
+ await saveHistory(history);
284
+ rl.close();
285
+ process.exit(0);
286
+ };
287
+
288
+ process.on("SIGINT", shutdown);
289
+ process.on("SIGTERM", shutdown);
290
+
291
+ while (running) {
292
+ const input = await rl.question("\nYou: ");
293
+ const trimmedInput = input.trim().toLowerCase();
294
+
295
+ if (trimmedInput === "exit") {
296
+ await shutdown();
297
+ break;
298
+ }
299
+
300
+ if (trimmedInput === "clear") {
301
+ history.clear();
302
+ console.log("History cleared. Starting fresh.");
303
+ continue;
304
+ }
305
+
306
+ if (trimmedInput === "history") {
307
+ console.log("\n--- Full History ---");
308
+ for (const entry of history.entries) {
309
+ if (entry.role === "system") continue;
310
+
311
+ console.log(`\n[${entry.role.toUpperCase()}]:`);
312
+
313
+ for (const block of entry.content) {
314
+ if (block.type === "text") {
315
+ console.log((block as { text: string }).text);
316
+ } else if (block.type === "tool_use") {
317
+ const tool = block as { name: string; input: unknown };
318
+ console.log(`🔧 Tool: ${tool.name}`);
319
+ console.log(` Input: ${JSON.stringify(tool.input)}`);
320
+ } else if (block.type === "tool_result") {
321
+ const result = block as { content: string };
322
+ const preview = result.content.slice(0, 100);
323
+ console.log(
324
+ `📋 Result: ${preview}${result.content.length > 100 ? "..." : ""}`
325
+ );
326
+ }
327
+ }
328
+ }
329
+ console.log("\n--- End History ---");
330
+ continue;
331
+ }
332
+
333
+ if (!input.trim()) {
334
+ continue;
335
+ }
336
+
337
+ try {
338
+ console.log("\nAssistant: Thinking...");
339
+ const response = await agent.execute(input);
340
+ console.log(`\nAssistant: ${response}`);
341
+ } catch (error) {
342
+ console.error(`Error: ${error}`);
343
+ }
344
+ }
345
+ }
346
+
347
+ main().catch(console.error);
package/index.ts ADDED
@@ -0,0 +1,175 @@
1
+ import "dotenv/config";
2
+
3
+ import { ClaudeAgent } from "../lib/agents/anthropic/ClaudeAgent";
4
+ import { OpenAiAgent } from "../lib/agents/openai/OpenAiAgent";
5
+ import { Tool, ToolEvent } from "../lib/tools/Tool";
6
+ import { readFile, readdir } from "fs/promises";
7
+
8
+ // Import chunking demo
9
+ import "./chunking-demo";
10
+
11
+ // const config =
12
+ // const agent = new ClaudeAgent();
13
+
14
+ // Example usage
15
+ async function exampleAgents() {
16
+ const geoCodingTool = new Tool({
17
+ name: "geocodingTool",
18
+ description: `This tool accepts a search term and returns a list of matching locations.
19
+ returns an array of results include city name, latitude, longitude, elevation, feature_code, country_code, timezone, population, postcodes`,
20
+ inputSchema: {
21
+ type: "object",
22
+ properties: {
23
+ term: {
24
+ type: "string",
25
+ description:
26
+ "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.",
27
+ },
28
+ },
29
+ required: ["term"],
30
+ },
31
+ execute: async (input): Promise<any> => {
32
+ const res = await fetch(
33
+ `https://geocoding-api.open-meteo.com/v1/search?name=${input.term}&count=10&language=en&format=json`
34
+ ).then(async (res) => await res.json());
35
+ return res;
36
+ },
37
+ });
38
+
39
+ const weatherTool = new Tool({
40
+ name: "weatherTool",
41
+ description: `This tool accepts a lat/long code and returns the weather forecast for the location.`,
42
+ inputSchema: {
43
+ type: "object",
44
+ properties: {
45
+ lat: {
46
+ type: "number",
47
+ description: "Latitude for the location.",
48
+ },
49
+ long: {
50
+ type: "number",
51
+ description: "Longitude for the location.",
52
+ },
53
+ },
54
+ required: ["lat", "long"],
55
+ },
56
+ execute: async (input): Promise<any> => {
57
+ console.log(
58
+ "weather tool input",
59
+ input,
60
+ `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`
61
+ );
62
+ return await fetch(
63
+ `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`
64
+ ).then((res) => res.json());
65
+ },
66
+ });
67
+
68
+ const directoryReadTool = new Tool({
69
+ name: "directoryReadTool",
70
+ description: `This tool can list the files in a local directory.`,
71
+ inputSchema: {
72
+ type: "object",
73
+ properties: {
74
+ path: {
75
+ type: "string",
76
+ description: "relative path, the default should be .",
77
+ },
78
+ },
79
+ required: ["path"],
80
+ },
81
+ execute: async (input): Promise<any> => {
82
+ console.log("directoryReadTool", input);
83
+ return await readdir(__dirname + "/../" + input.path, {
84
+ recursive: true,
85
+ });
86
+ },
87
+ });
88
+
89
+ const fileReadTool = new Tool({
90
+ name: "fileReadTool",
91
+ description: `This tool can list the files in a local directory, you should not ask to read file that can contain private info lime .env files.`,
92
+ inputSchema: {
93
+ type: "object",
94
+ properties: {
95
+ path: {
96
+ type: "string",
97
+ description: "relative path of a file",
98
+ },
99
+ },
100
+ required: ["path"],
101
+ },
102
+ execute: async (input): Promise<any> => {
103
+ console.log("fileReadTool", input);
104
+ return await readFile(__dirname + "/../" + input.path, {
105
+ encoding: "utf8",
106
+ });
107
+ },
108
+ });
109
+
110
+ const agent = new ClaudeAgent({
111
+ id: "1",
112
+ description:
113
+ "This agent gets the weather for a specific location, it is sarcastic and to the point and tries to make me laugh",
114
+ name: "Funny sarcastic weather agent",
115
+ model: "claude-3-5-haiku-latest",
116
+ tools: [geoCodingTool, weatherTool],
117
+ apiKey: process.env.ANTHROPIC_API_KEY as string,
118
+ // Optional: Force tool usage
119
+ // toolChoice: { type: "any" },
120
+ // Optional: Disable parallel tool use
121
+ disableParallelToolUse: false,
122
+ });
123
+
124
+ const agent2 = new ClaudeAgent({
125
+ id: "1",
126
+ description:
127
+ "This helps with software projects, it takes commands, reads files and suggests improvements",
128
+ name: "Powerfull AI coder",
129
+ model: "claude-3-5-haiku-latest",
130
+ tools: [directoryReadTool, fileReadTool],
131
+ apiKey: process.env.ANTHROPIC_API_KEY as string,
132
+ // Optional: Force tool usage
133
+ // toolChoice: { type: "any" },
134
+ // Optional: Disable parallel tool use
135
+ disableParallelToolUse: false,
136
+ });
137
+
138
+ const openaiAgent = new OpenAiAgent({
139
+ id: "1",
140
+ description:
141
+ "This helps with software projects, it takes commands, reads files and suggests improvements",
142
+ name: "Powerfull AI coder",
143
+ model: "gpt-4o-mini",
144
+ // tools: [directoryReadTool, fileReadTool],
145
+ apiKey: process.env.OPENAI_API_KEY as string,
146
+ // Optional: Force tool usage
147
+ // toolChoice: { type: "any" },
148
+ // Optional: Disable parallel tool use
149
+ disableParallelToolUse: false,
150
+ });
151
+
152
+ try {
153
+ // const result = await agent.execute(
154
+ // "find the waether forcast for Benidorm and tell me something interesting about it"
155
+ // );
156
+ //
157
+ //
158
+ directoryReadTool.on(ToolEvent.EXECUTE, (event) => {
159
+ console.log("directoryReadTool execution:", event);
160
+ });
161
+ agent;
162
+ agent2;
163
+ openaiAgent;
164
+ const result = await agent2.execute(
165
+ "Complete the tests for ./lib/agents/CommonAgents.spec.ts"
166
+ );
167
+
168
+ console.log("Result:", result);
169
+ } catch (error) {
170
+ console.error("Error:", error);
171
+ }
172
+ }
173
+
174
+ // Uncomment to run agent examples
175
+ // exampleAgents();