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,141 @@
1
+ /**
2
+ * Sequential Executor Example
3
+ *
4
+ * Demonstrates chaining agents in sequence where each agent's output
5
+ * becomes the input for the next agent.
6
+ *
7
+ * Use case: Research pipeline with fact-checking and summarization
8
+ *
9
+ * For data-fetching pipelines with factories, see: ./data-pipeline/
10
+ */
11
+ import "dotenv/config";
12
+ import { ClaudeAgent } from "../../lib/agents/anthropic/ClaudeAgent";
13
+ import { AgentGraph } from "../../lib/graph/AgentGraph";
14
+
15
+ const apiKey = process.env.ANTHROPIC_API_KEY;
16
+
17
+ if (!apiKey) {
18
+ console.error("Please set ANTHROPIC_API_KEY environment variable");
19
+ process.exit(1);
20
+ }
21
+
22
+ // ============================================================================
23
+ // Agent Definitions
24
+ // ============================================================================
25
+
26
+ const researchAgent = new ClaudeAgent({
27
+ id: "researcher",
28
+ name: "Research Agent",
29
+ description: `You are a research agent. Given a topic, provide detailed factual information.
30
+ Be thorough but concise. Focus on key facts, dates, and verifiable information.
31
+ Output your findings in a structured format.`,
32
+ apiKey,
33
+ maxTokens: 1024,
34
+ });
35
+
36
+ const factCheckerAgent = new ClaudeAgent({
37
+ id: "fact-checker",
38
+ name: "Fact Checker Agent",
39
+ description: `You are a fact-checking agent. You receive research findings and verify their accuracy.
40
+ Mark any claims that seem questionable or need verification.
41
+ Add confidence levels (High/Medium/Low) to each fact.
42
+ Correct any obvious errors you find.`,
43
+ apiKey,
44
+ maxTokens: 1024,
45
+ });
46
+
47
+ const summaryAgent = new ClaudeAgent({
48
+ id: "summarizer",
49
+ name: "Summary Agent",
50
+ description: `You are a summarization agent. You receive fact-checked research and create a clear,
51
+ concise summary suitable for a general audience.
52
+ Highlight the most important points.
53
+ Include a brief conclusion with key takeaways.
54
+ Keep the summary under 200 words.`,
55
+ apiKey,
56
+ maxTokens: 512,
57
+ });
58
+
59
+ // ============================================================================
60
+ // Example 1: Basic Sequential Pipeline
61
+ // ============================================================================
62
+
63
+ async function runSequentialExample() {
64
+ console.log("=== Sequential Executor Example ===\n");
65
+ console.log("Pipeline: Research -> Fact-Check -> Summarize\n");
66
+
67
+ const researchPipeline = AgentGraph.sequential(
68
+ researchAgent,
69
+ factCheckerAgent,
70
+ summaryAgent
71
+ );
72
+
73
+ const topic = "The discovery of penicillin and its impact on medicine";
74
+ console.log(`Topic: "${topic}"\n`);
75
+
76
+ try {
77
+ const result = await researchPipeline.execute(topic);
78
+ console.log("\n=== Final Summary ===\n");
79
+ console.log(result);
80
+ } catch (error) {
81
+ console.error("Pipeline failed:", error);
82
+ }
83
+ }
84
+
85
+ // ============================================================================
86
+ // Example 2: Raw Input Mode (no JSON wrapping)
87
+ // ============================================================================
88
+
89
+ async function runRawSequentialExample() {
90
+ console.log("\n=== Sequential with Raw Input ===\n");
91
+
92
+ const translator = new ClaudeAgent({
93
+ id: "translator",
94
+ name: "Translator",
95
+ description: `Translate the input text to French. Output only the translation, nothing else.`,
96
+ apiKey,
97
+ maxTokens: 256,
98
+ });
99
+
100
+ const polisher = new ClaudeAgent({
101
+ id: "polisher",
102
+ name: "Polisher",
103
+ description: `You receive French text. Make it more elegant and poetic while keeping the meaning.
104
+ Output only the polished French text.`,
105
+ apiKey,
106
+ maxTokens: 256,
107
+ });
108
+
109
+ // wrapInput: false passes raw output between agents
110
+ const translationPipeline = AgentGraph.sequential(
111
+ { wrapInput: false },
112
+ translator,
113
+ polisher
114
+ );
115
+
116
+ const englishText =
117
+ "The sun sets slowly over the mountains, painting the sky in shades of orange and purple.";
118
+ console.log(`English: "${englishText}"\n`);
119
+
120
+ try {
121
+ const result = await translationPipeline.execute(englishText);
122
+ console.log(`Polished French: "${result}"`);
123
+ } catch (error) {
124
+ console.error("Translation failed:", error);
125
+ }
126
+ }
127
+
128
+ // ============================================================================
129
+ // Main
130
+ // ============================================================================
131
+
132
+ async function main() {
133
+ await runSequentialExample();
134
+ await runRawSequentialExample();
135
+
136
+ console.log("\n---");
137
+ console.log("For data-fetching pipelines with DB/API integration,");
138
+ console.log("see: examples/graph/data-pipeline/");
139
+ }
140
+
141
+ main().catch(console.error);
@@ -0,0 +1,159 @@
1
+ /**
2
+ * Voting System Example
3
+ *
4
+ * Demonstrates using a judge agent to select the best answer
5
+ * from multiple expert responses.
6
+ *
7
+ * Use case: Getting diverse opinions and synthesizing the best answer
8
+ */
9
+ import "dotenv/config";
10
+ import { ClaudeAgent } from "../../lib/agents/anthropic/ClaudeAgent";
11
+ import { AgentGraph, VotingInput } from "../../lib/graph/AgentGraph";
12
+
13
+ const apiKey = process.env.ANTHROPIC_API_KEY as string;
14
+
15
+ if (!apiKey) {
16
+ console.error("Please set ANTHROPIC_API_KEY environment variable");
17
+ process.exit(1);
18
+ }
19
+
20
+ // Three experts with different approaches
21
+ const conservativeExpert = new ClaudeAgent({
22
+ id: "conservative",
23
+ name: "Conservative Expert",
24
+ description: `You are a conservative, risk-averse advisor.
25
+ When answering questions, prioritize safety, proven methods, and caution.
26
+ Recommend established approaches over experimental ones.
27
+ Keep responses under 100 words.`,
28
+ apiKey,
29
+ maxTokens: 256,
30
+ });
31
+
32
+ const innovativeExpert = new ClaudeAgent({
33
+ id: "innovative",
34
+ name: "Innovative Expert",
35
+ description: `You are an innovative, forward-thinking advisor.
36
+ When answering questions, prioritize new approaches, cutting-edge solutions, and bold ideas.
37
+ Recommend creative and unconventional methods.
38
+ Keep responses under 100 words.`,
39
+ apiKey,
40
+ maxTokens: 256,
41
+ });
42
+
43
+ const balancedExpert = new ClaudeAgent({
44
+ id: "balanced",
45
+ name: "Balanced Expert",
46
+ description: `You are a balanced, pragmatic advisor.
47
+ When answering questions, weigh both traditional and innovative approaches.
48
+ Recommend solutions that balance risk and reward.
49
+ Keep responses under 100 words.`,
50
+ apiKey,
51
+ maxTokens: 256,
52
+ });
53
+
54
+ // Judge agent to evaluate and select the best answer
55
+ const judgeAgent = new ClaudeAgent({
56
+ id: "judge",
57
+ name: "Judge",
58
+ description: `You are an impartial judge evaluating expert opinions.
59
+ Analyze each expert's response for:
60
+ - Practicality and feasibility
61
+ - Completeness of the answer
62
+ - Quality of reasoning
63
+
64
+ Select the best answer OR synthesize a superior answer combining the best elements.
65
+ Explain your choice briefly, then provide the final recommended answer.`,
66
+ apiKey,
67
+ maxTokens: 512,
68
+ });
69
+
70
+ async function runBasicVotingExample() {
71
+ console.log("=== Basic Voting System Example ===\n");
72
+
73
+ const question = "What's the best strategy for a small business to adopt AI?";
74
+ console.log(`Question: "${question}"\n`);
75
+
76
+ // Get opinions from all experts
77
+ console.log("Gathering expert opinions...\n");
78
+
79
+ const [conservative, innovative, balanced] = await Promise.all([
80
+ conservativeExpert.execute(question),
81
+ innovativeExpert.execute(question),
82
+ balancedExpert.execute(question),
83
+ ]);
84
+
85
+ console.log("--- Conservative Expert ---");
86
+ console.log(conservative);
87
+ console.log("\n--- Innovative Expert ---");
88
+ console.log(innovative);
89
+ console.log("\n--- Balanced Expert ---");
90
+ console.log(balanced);
91
+
92
+ // Create voting system
93
+ const votingSystem = AgentGraph.votingSystem(judgeAgent);
94
+
95
+ // Submit for voting
96
+ const votingInput: VotingInput = {
97
+ originalInput: question,
98
+ solutions: [
99
+ conservative as string,
100
+ innovative as string,
101
+ balanced as string,
102
+ ],
103
+ };
104
+
105
+ console.log("\n--- Judge's Verdict ---\n");
106
+ const verdict = await votingSystem.execute(votingInput);
107
+ console.log(verdict);
108
+ }
109
+
110
+ // Custom prompt template example
111
+ async function runCustomTemplateExample() {
112
+ console.log("\n=== Custom Voting Template Example ===\n");
113
+
114
+ const customJudge = new ClaudeAgent({
115
+ id: "custom-judge",
116
+ name: "Custom Judge",
117
+ description: `You evaluate code solutions. Pick the best one based on:
118
+ - Code quality and readability
119
+ - Performance considerations
120
+ - Best practices
121
+
122
+ Output only the winning solution number and a brief explanation.`,
123
+ apiKey,
124
+ maxTokens: 256,
125
+ });
126
+
127
+ const votingSystem = AgentGraph.votingSystem(customJudge, {
128
+ promptTemplate: `## Code Review Task
129
+
130
+ **Original Request:** {originalQuestion}
131
+
132
+ **Submitted Solutions:**
133
+ {expertAnswers}
134
+
135
+ Evaluate each solution and declare a winner.`,
136
+ });
137
+
138
+ const solutions = [
139
+ "Solution using a for loop: for(let i=0; i<arr.length; i++) { sum += arr[i]; }",
140
+ "Solution using reduce: arr.reduce((sum, val) => sum + val, 0)",
141
+ "Solution using forEach: let sum=0; arr.forEach(x => sum += x);",
142
+ ];
143
+
144
+ const result = await votingSystem.execute({
145
+ originalInput: "Sum all numbers in an array",
146
+ solutions,
147
+ });
148
+
149
+ console.log("Judge's decision:");
150
+ console.log(result);
151
+ }
152
+
153
+ // Run examples
154
+ async function main() {
155
+ await runBasicVotingExample();
156
+ await runCustomTemplateExample();
157
+ }
158
+
159
+ main().catch(console.error);
@@ -0,0 +1,14 @@
1
+ version: "3.8"
2
+ services:
3
+ neo4j:
4
+ image: neo4j:latest
5
+ container_name: neo4j_db
6
+ ports:
7
+ - "7474:7474"
8
+ - "7687:7687"
9
+ environment:
10
+ NEO4J_AUTH: neo4j/your_password
11
+ volumes:
12
+ - neo4j-data:/data
13
+ volumes:
14
+ neo4j-data:
@@ -0,0 +1,99 @@
1
+ const neo4j = require("neo4j-driver");
2
+
3
+ // Neo4j connection details
4
+ const uri = "bolt://localhost:7687";
5
+ const user = "neo4j";
6
+ const password = "your_password";
7
+
8
+ const driver = neo4j.driver(uri, neo4j.auth.basic(user, password));
9
+
10
+ // Helper function to generate random vectors
11
+ function generateRandomVector(dim) {
12
+ return Array.from({ length: dim }, () => (Math.random() * 2 - 1).toFixed(4));
13
+ }
14
+
15
+ async function runTests() {
16
+ const session = driver.session();
17
+
18
+ try {
19
+ // Clear the database
20
+ await session.run("MATCH (n) DETACH DELETE n");
21
+ console.log("Database cleared.");
22
+
23
+ // Insert nodes with vector embeddings
24
+ const items = [
25
+ { name: "Item1", embedding: generateRandomVector(3) },
26
+ { name: "Item2", embedding: generateRandomVector(3) },
27
+ { name: "Item3", embedding: generateRandomVector(3) },
28
+ ];
29
+
30
+ for (const item of items) {
31
+ await session.run(
32
+ "CREATE (i:Item {name: $name, embedding: $embedding})",
33
+ { name: item.name, embedding: item.embedding.map(Number) }
34
+ );
35
+ console.log(
36
+ `Inserted node: ${item.name} with embedding ${item.embedding}`
37
+ );
38
+ }
39
+
40
+ // Perform vector similarity search (cosine similarity)
41
+ const queryVector = generateRandomVector(3);
42
+ console.log(`Querying with vector: ${queryVector}`);
43
+
44
+ const result = await session.run(
45
+ `
46
+ MATCH (i:Item)
47
+ WITH i, $queryVector AS query
48
+ RETURN i.name, i.embedding,
49
+ gds.similarity.cosine(i.embedding, query) AS similarity
50
+ ORDER BY similarity DESC
51
+ LIMIT 2
52
+ `,
53
+ { queryVector: queryVector.map(Number) }
54
+ );
55
+
56
+ console.log("Vector similarity search results:");
57
+ result.records.forEach((record) => {
58
+ console.log(
59
+ `Item: ${record.get("i.name")}, Embedding: ${record.get(
60
+ "i.embedding"
61
+ )}, Similarity: ${record.get("similarity")}`
62
+ );
63
+ });
64
+
65
+ // Create a simple graph with relationships
66
+ await session.run(
67
+ `
68
+ MATCH (i1:Item {name: 'Item1'}), (i2:Item {name: 'Item2'})
69
+ CREATE (i1)-[:RELATED_TO {weight: 0.8}]->(i2)
70
+ `
71
+ );
72
+ console.log("Created relationship between Item1 and Item2.");
73
+
74
+ // Query the graph
75
+ const graphResult = await session.run(
76
+ `
77
+ MATCH (i:Item)-[r:RELATED_TO]->(other)
78
+ RETURN i.name, type(r), other.name, r.weight
79
+ `
80
+ );
81
+
82
+ console.log("Graph query results:");
83
+ graphResult.records.forEach((record) => {
84
+ console.log(
85
+ `From: ${record.get("i.name")} -> ${record.get(
86
+ "other.name"
87
+ )} via ${record.get("type(r)")}, Weight: ${record.get("r.weight")}`
88
+ );
89
+ });
90
+ } catch (error) {
91
+ console.error("Error:", error);
92
+ } finally {
93
+ await session.close();
94
+ await driver.close();
95
+ }
96
+ }
97
+
98
+ // Run the tests
99
+ runTests();
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ psql -v ON_ERROR_STOP=1 --username "$POSTGRES_USER" --dbname "$POSTGRES_DB" <<-EOSQL
3
+ CREATE EXTENSION IF NOT EXISTS vector;
4
+ CREATE EXTENSION IF NOT EXISTS age;
5
+ LOAD 'age';
6
+ SET search_path = ag_catalog, "\$user", public;
7
+ EOSQL
@@ -0,0 +1,15 @@
1
+ {
2
+ "name": "graph-rag",
3
+ "version": "1.0.0",
4
+ "main": "index.js",
5
+ "scripts": {
6
+ "test": "echo \"Error: no test specified\" && exit 1"
7
+ },
8
+ "keywords": [],
9
+ "author": "",
10
+ "license": "ISC",
11
+ "description": "",
12
+ "dependencies": {
13
+ "neo4j-driver": "^5.28.1"
14
+ }
15
+ }
@@ -0,0 +1,163 @@
1
+ /**
2
+ * History Compression Example
3
+ *
4
+ * Demonstrates the history plugin system with two complementary strategies:
5
+ *
6
+ * 1. toolResultMaskingPlugin — Read-time masking of old tool results.
7
+ * Full content is always stored; only the view seen by the LLM is masked.
8
+ * The agent can fetch any masked result on demand via retrieve_tool_result.
9
+ *
10
+ * 2. compressionPlugin — Rolling LLM summarization of old conversation turns.
11
+ * Older turns are replaced by a compact summary entry, reducing token usage
12
+ * while preserving the gist of what happened.
13
+ *
14
+ * Both plugins compose on the same history instance. Tool result masking is
15
+ * free (sync, no LLM calls). Rolling summarization runs automatically via
16
+ * autoReduceWhen when the token budget is exceeded — no manual history.reduce()
17
+ * call required.
18
+ */
19
+
20
+ import "dotenv/config";
21
+ import { ClaudeAgent } from "../lib/agents/anthropic/ClaudeAgent";
22
+ import { History } from "../lib/history/History";
23
+ import { Tool } from "../lib/tools/Tool";
24
+ import { compressionPlugin } from "../lib/history/plugins/compressionPlugin";
25
+ import { toolResultMaskingPlugin } from "../lib/history/plugins/toolResultMaskingPlugin";
26
+
27
+ // ---------------------------------------------------------------------------
28
+ // Simulated tools
29
+ // ---------------------------------------------------------------------------
30
+
31
+ const searchTool = new Tool<string>({
32
+ name: "web_search",
33
+ description: "Search the web for information",
34
+ inputSchema: {
35
+ type: "object",
36
+ properties: {
37
+ query: { type: "string", description: "Search query" },
38
+ },
39
+ required: ["query"],
40
+ },
41
+ execute: async ({ query }: { query: string }): Promise<string> => {
42
+ // Simulate a large search result
43
+ return (
44
+ `Search results for "${query}":\n` +
45
+ "1. Result one with detailed information about the topic...\n".repeat(
46
+ 20
47
+ ) +
48
+ "2. Result two with more details...\n".repeat(20)
49
+ );
50
+ },
51
+ });
52
+
53
+ const calculatorTool = new Tool<number>({
54
+ name: "calculator",
55
+ description: "Perform arithmetic calculations",
56
+ inputSchema: {
57
+ type: "object",
58
+ properties: {
59
+ expression: {
60
+ type: "string",
61
+ description: "Math expression to evaluate",
62
+ },
63
+ },
64
+ required: ["expression"],
65
+ },
66
+ execute: async ({ expression }: { expression: string }): Promise<number> => {
67
+ // Safe eval for demo purposes only
68
+ return Function(`"use strict"; return (${expression})`)() as number;
69
+ },
70
+ });
71
+
72
+ // ---------------------------------------------------------------------------
73
+ // Setup
74
+ // ---------------------------------------------------------------------------
75
+
76
+ const apiKey = process.env.ANTHROPIC_API_KEY;
77
+ if (!apiKey) throw new Error("ANTHROPIC_API_KEY is required");
78
+
79
+ // Dedicated summarization agent — cheap, fast model, no tools
80
+ const summaryAgent = new ClaudeAgent({
81
+ id: "summarizer",
82
+ name: "Summarizer",
83
+ description: "Summarizes conversation history concisely",
84
+ apiKey,
85
+ model: "claude-haiku-4-5-20251001",
86
+ });
87
+
88
+ // Masking plugin: keep 1 recent web_search result verbatim; mask older ones.
89
+ // Calculator results are excluded — they're tiny and always useful verbatim.
90
+ const maskingPlugin = toolResultMaskingPlugin({
91
+ keepRecentResults: 1,
92
+ exclude: ["calculator"],
93
+ minTokensToMask: 50,
94
+ });
95
+
96
+ // Shared history with both plugins registered.
97
+ // compressionPlugin auto-triggers when the history exceeds 2 000 tokens.
98
+ const history = new History([], { maxTokens: 2000 })
99
+ .use(maskingPlugin)
100
+ .use(compressionPlugin(summaryAgent, { autoReduceWhen: { maxTokens: 2000 } }));
101
+
102
+ // Surface async plugin errors
103
+ history.on("pluginError", (error: Error, _plugin: unknown, hook: string) => {
104
+ console.error(`[pluginError] ${hook}: ${error.message}`);
105
+ });
106
+
107
+ // Main agent with the retrieve tool wired in
108
+ const agent = new ClaudeAgent(
109
+ {
110
+ id: "assistant",
111
+ name: "Research Assistant",
112
+ description: "A research assistant with web search and calculation tools",
113
+ apiKey,
114
+ model: "claude-sonnet-4-6",
115
+ tools: [
116
+ searchTool,
117
+ calculatorTool,
118
+ maskingPlugin.retrieveTool, // allows retrieval of masked results
119
+ ],
120
+ },
121
+ history
122
+ );
123
+
124
+ // ---------------------------------------------------------------------------
125
+ // Simulated conversation
126
+ // ---------------------------------------------------------------------------
127
+
128
+ async function main() {
129
+ console.log("=== History Compression Example ===\n");
130
+
131
+ const turns = [
132
+ "Search for information about the history of the internet.",
133
+ "Now search for information about artificial intelligence milestones.",
134
+ "What is 1024 * 768?",
135
+ "Search for quantum computing breakthroughs in 2024.",
136
+ "Summarise everything we've discussed so far.",
137
+ ];
138
+
139
+ for (const turn of turns) {
140
+ console.log(`\n[User] ${turn}`);
141
+ console.log(
142
+ ` History before: ${history.length} entries, ~${history.totalEstimatedTokens} tokens`
143
+ );
144
+
145
+ const response = await agent.execute(turn);
146
+ console.log(`[Assistant] ${response.slice(0, 200)}...`);
147
+ console.log(
148
+ ` History after: ${history.length} entries, ~${history.totalEstimatedTokens} tokens`
149
+ );
150
+ }
151
+
152
+ console.log(
153
+ "\nNote: compressionPlugin auto-triggered whenever history exceeded 2 000 tokens."
154
+ );
155
+ console.log(
156
+ "Tool results are masked in the agent view but stored in full."
157
+ );
158
+ console.log(
159
+ "The agent can call retrieve_tool_result(tool_call_id) to access any masked result."
160
+ );
161
+ }
162
+
163
+ main().catch(console.error);