adaptive-memory-multi-model-router 2.13.18 → 2.13.20

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 (91) hide show
  1. package/.dockerignore +82 -0
  2. package/.env.example +303 -0
  3. package/.github/ISSUE_TEMPLATE/bug_report.md +83 -12
  4. package/.github/ISSUE_TEMPLATE/config.yml +12 -6
  5. package/.github/ISSUE_TEMPLATE/feature_request.md +61 -10
  6. package/.github/PULL_REQUEST_TEMPLATE.md +53 -26
  7. package/.github/dependabot.yml +9 -0
  8. package/.github/workflows/codeql.yml +38 -0
  9. package/.github/workflows/npm-publish.yml +20 -0
  10. package/.github/workflows/stale.yml +56 -0
  11. package/ARCHITECTURE.md +346 -0
  12. package/AUDIT_REPORT.md +28 -0
  13. package/CHANGELOG.md +386 -22
  14. package/CONTRIBUTORS.md +20 -0
  15. package/Dockerfile +53 -0
  16. package/Dockerfile.proxy +33 -0
  17. package/PR_STATUS_REPORT.md +148 -0
  18. package/README.md +22 -0
  19. package/RUNKIT.md +83 -0
  20. package/_schema.html +61 -15
  21. package/articles/AI_AGENT_LLM_ROUTING.md +150 -0
  22. package/articles/FROM_ZERO_TO_10K.md +107 -0
  23. package/articles/LLM_BENCHMARK_DEEP_DIVE.md +153 -0
  24. package/articles/TWEETS_10K_DOWNLOADS.md +47 -0
  25. package/articles/TWEETS_BENCHMARK_FIRST.md +46 -0
  26. package/articles/TWEETS_MCP_PLAY.md +51 -0
  27. package/articles/TWEETS_SEQUENTIAL_BROKEN.md +49 -0
  28. package/articles/TWEETS_WHY_BUILD.md +54 -0
  29. package/benchmark-results.json +26 -45
  30. package/cli/a3m +840 -0
  31. package/demo/package.json +13 -0
  32. package/demo/public/index.html +762 -0
  33. package/demo/server.js +405 -0
  34. package/dist/cli.js +4 -0
  35. package/docker-compose.yml +74 -0
  36. package/docs/.nojekyll +0 -0
  37. package/docs/BENCHMARK.md +96 -22
  38. package/docs/_config.yml +49 -0
  39. package/docs/api.html +513 -0
  40. package/docs/benchmark.html +387 -0
  41. package/docs/cli-cheatsheet.md +339 -0
  42. package/docs/comparison.md +108 -0
  43. package/docs/curl-examples.md +247 -0
  44. package/docs/index.html +390 -99
  45. package/docs/openapi.yaml +1318 -0
  46. package/docs/quick-start.html +366 -0
  47. package/docs/robots.txt +1 -1
  48. package/docs/sitemap.xml +23 -5
  49. package/docs/styles.css +682 -0
  50. package/examples/README.md +61 -0
  51. package/examples/a3m-sdk.js +124 -0
  52. package/examples/basic-route.js +54 -0
  53. package/examples/chat-loop.js +202 -0
  54. package/examples/classify-then-route.js +102 -0
  55. package/examples/cost-compare.js +120 -0
  56. package/examples/ensemble.js +160 -0
  57. package/integrations/langchain/README.md +216 -0
  58. package/integrations/langchain/a3m_langchain.ts +1360 -0
  59. package/integrations/langchain/example.ts +287 -0
  60. package/integrations/vercel-ai-sdk/README.md +49 -0
  61. package/integrations/vercel-ai-sdk/a3m_provider.ts +78 -0
  62. package/integrations/vercel-ai-sdk/example.ts +25 -0
  63. package/llms-full.txt +43 -0
  64. package/llms.txt +9 -0
  65. package/mcp-server/README.md +188 -0
  66. package/mcp-server/package.json +29 -0
  67. package/mcp-server/src/index.ts +744 -0
  68. package/mcp-server/tsconfig.json +19 -0
  69. package/package.json +3 -3
  70. package/proxy/README.md +227 -0
  71. package/proxy/package-lock.json +831 -0
  72. package/proxy/package.json +17 -0
  73. package/proxy/rate-limit.js +145 -0
  74. package/proxy/rate-limit.test.js +311 -0
  75. package/proxy/server.js +970 -0
  76. package/scripts/banner.js +29 -0
  77. package/scripts/compare-providers.sh +230 -0
  78. package/scripts/cross_post.py +443 -0
  79. package/scripts/publish_fcc.py +106 -0
  80. package/scripts/push-to-gitee.sh +52 -0
  81. package/src/tui/dashboard.ts +13 -0
  82. package/tests/__mocks__/tokenUtils.ts +22 -0
  83. package/tests/memory/episodicMemory.test.ts +227 -0
  84. package/tests/package-lock.json +1628 -0
  85. package/tests/package.json +18 -0
  86. package/tests/routing/ensembleVoting.test.ts +236 -0
  87. package/tests/routing/providerRetry.test.ts +360 -0
  88. package/tests/routing/queryTypePresets.test.ts +206 -0
  89. package/tests/tsconfig.json +21 -0
  90. package/tests/vitest.config.ts +18 -0
  91. package/.env +0 -2
@@ -0,0 +1,29 @@
1
+ #!/usr/bin/env node
2
+ /**
3
+ * A3M Router — Terminal ASCII Art Banner
4
+ *
5
+ * Printed on CLI startup to reinforce A3M branding.
6
+ * Usage:
7
+ * node scripts/banner.js
8
+ * // or import './banner' in CLI entry point
9
+ */
10
+
11
+ const A3M_BANNER = `
12
+ ╔══════════════════════════════════════════════════════════╗
13
+ ║ ╔═╗╔═╗╔╗╔╔═╗ ║
14
+ ║ ╠═╣║ ║║║║║ ║ ║
15
+ ║ ╩ ╩╚═╝╝╚╝╚═╝ ║
16
+ ║ ║
17
+ ║ Parallel Multi-LLM Execution Engine ║
18
+ ║ ║
19
+ ║ 47+ Providers · Ensemble Voting · 62% Cost Savings ║
20
+ ║ ║
21
+ ║ ${'\x1b[2m'}https://github.com/Das-rebel/a3m-router${'\x1b[0m'}${' '.repeat(19)}║
22
+ ╚══════════════════════════════════════════════════════════╝
23
+ `;
24
+
25
+ module.exports = A3M_BANNER;
26
+
27
+ if (require.main === module) {
28
+ process.stdout.write(A3M_BANNER);
29
+ }
@@ -0,0 +1,230 @@
1
+ #!/bin/bash
2
+ # =============================================================================
3
+ # A3M Router — Provider Comparison Script
4
+ # =============================================================================
5
+ # Compares responses from multiple LLM providers to the SAME prompt.
6
+ # Works with any provider that has an OpenAI-compatible API.
7
+ #
8
+ # Usage:
9
+ # ./scripts/compare-providers.sh "What is 2+2?"
10
+ # ./scripts/compare-providers.sh # defaults
11
+ #
12
+ # Requires at least one of: GROQ_API_KEY, NV_API_KEY, CEREBRAS_API_KEY
13
+ # =============================================================================
14
+
15
+ set -e
16
+
17
+ PROMPT="${1:-Say hello in exactly 3 words.}"
18
+ TIMEOUT=15
19
+
20
+ # Color codes
21
+ RED='\033[0;31m'
22
+ GREEN='\033[0;32m'
23
+ YELLOW='\033[1;33m'
24
+ BLUE='\033[0;34m'
25
+ MAGENTA='\033[0;35m'
26
+ CYAN='\033[0;36m'
27
+ NC='\033[0m' # No Color
28
+
29
+ echo ""
30
+ echo -e "${BLUE}══════════════════════════════════════════════════════════════${NC}"
31
+ echo -e "${BLUE} A3M Router — Provider Comparison${NC}"
32
+ echo -e "${BLUE} Prompt: \"${PROMPT}\"${NC}"
33
+ echo -e "${BLUE}══════════════════════════════════════════════════════════════${NC}"
34
+ echo ""
35
+
36
+ RESULTS_DIR=$(mktemp -d)
37
+ trap 'rm -rf "$RESULTS_DIR"' EXIT
38
+
39
+ COMPLETED=0
40
+ FAILED=0
41
+
42
+ # -------------------------------------------------------------------------
43
+ # Helper: call a provider and save result
44
+ # -------------------------------------------------------------------------
45
+ call_provider() {
46
+ local name=$1
47
+ local url=$2
48
+ local auth_header=$3
49
+ local model=$4
50
+ local data_file="$RESULTS_DIR/${name//\//_}.json"
51
+
52
+ # Build the curl payload
53
+ local payload
54
+ payload=$(jq -n \
55
+ --arg model "$model" \
56
+ --arg content "$PROMPT" \
57
+ '{
58
+ model: $model,
59
+ messages: [{role: "user", content: $content}],
60
+ max_tokens: 100,
61
+ temperature: 0.7
62
+ }')
63
+
64
+ # Execute with timeout, capture response and HTTP code
65
+ local http_code
66
+ http_code=$(curl -s -o "$data_file" -w "%{http_code}" \
67
+ --max-time "$TIMEOUT" \
68
+ -H "Authorization: Bearer $auth_header" \
69
+ -H "Content-Type: application/json" \
70
+ -d "$payload" \
71
+ "$url" 2>/dev/null)
72
+
73
+ if [ "$http_code" = "200" ]; then
74
+ COMPLETED=$((COMPLETED + 1))
75
+ echo "$data_file"
76
+ else
77
+ FAILED=$((FAILED + 1))
78
+ # Store error for display
79
+ echo "{\"error\": true, \"http_code\": $http_code, \"body\": $(cat "$data_file" 2>/dev/null || echo '"timeout or connection error"')}" > "$data_file"
80
+ echo "$data_file"
81
+ fi
82
+ }
83
+
84
+ # -------------------------------------------------------------------------
85
+ # Print a single provider's result
86
+ # -------------------------------------------------------------------------
87
+ print_result() {
88
+ local name=$1
89
+ local color=$2
90
+ local data_file=$3
91
+
92
+ if jq -e '.error == true' "$data_file" >/dev/null 2>&1; then
93
+ local code
94
+ code=$(jq -r '.http_code' "$data_file")
95
+ echo -e "${color} ❌ $name${NC} (HTTP $code)"
96
+ jq -r '.body | if length > 200 then .[0:200] + "..." else . end' "$data_file" 2>/dev/null | sed 's/^/ /'
97
+ return
98
+ fi
99
+
100
+ local content model usage_in usage_out usage_total
101
+ content=$(jq -r '.choices[0].message.content // "N/A"' "$data_file" 2>/dev/null)
102
+ model=$(jq -r '.model // "N/A"' "$data_file" 2>/dev/null)
103
+ usage_in=$(jq -r '.usage.prompt_tokens // "?"' "$data_file" 2>/dev/null)
104
+ usage_out=$(jq -r '.usage.completion_tokens // "?"' "$data_file" 2>/dev/null)
105
+
106
+ echo -e "${color} ● $name${NC}"
107
+ echo " Model: $model"
108
+ echo " Tokens: ${usage_in} in / ${usage_out} out"
109
+ echo " Response: $content"
110
+ }
111
+
112
+ # =============================================================================
113
+ # Collect results (parallel execution)
114
+ # =============================================================================
115
+
116
+ echo -e "${YELLOW} Sending parallel requests...${NC}"
117
+ echo ""
118
+
119
+ # Groq
120
+ if [ -n "$GROQ_API_KEY" ]; then
121
+ groq_file=$(call_provider "Groq" \
122
+ "https://api.groq.com/openai/v1/chat/completions" \
123
+ "$GROQ_API_KEY" \
124
+ "llama-3.3-70b-versatile")
125
+ else
126
+ echo -e "${RED} ❌ Groq — GROQ_API_KEY not set${NC}"
127
+ FAILED=$((FAILED + 1))
128
+ fi
129
+
130
+ # NVIDIA NIM
131
+ if [ -n "$NV_API_KEY" ]; then
132
+ nvidia_file=$(call_provider "NVIDIA NIM" \
133
+ "https://integrate.api.nvidia.com/v1/chat/completions" \
134
+ "$NV_API_KEY" \
135
+ "meta/llama-3.3-70b-instruct")
136
+ else
137
+ echo -e "${RED} ❌ NVIDIA NIM — NV_API_KEY not set${NC}"
138
+ FAILED=$((FAILED + 1))
139
+ fi
140
+
141
+ # Cerebras
142
+ if [ -n "$CEREBRAS_API_KEY" ]; then
143
+ cerebras_file=$(call_provider "Cerebras" \
144
+ "https://api.cerebras.ai/v1/chat/completions" \
145
+ "$CEREBRAS_API_KEY" \
146
+ "llama-3.3-70b")
147
+ else
148
+ echo -e "${RED} ❌ Cerebras — CEREBRAS_API_KEY not set${NC}"
149
+ FAILED=$((FAILED + 1))
150
+ fi
151
+
152
+ # OpenAI
153
+ if [ -n "$OPENAI_API_KEY" ]; then
154
+ openai_file=$(call_provider "OpenAI" \
155
+ "https://api.openai.com/v1/chat/completions" \
156
+ "$OPENAI_API_KEY" \
157
+ "gpt-4o-mini")
158
+ else
159
+ echo -e "${RED} ❌ OpenAI — OPENAI_API_KEY not set${NC}"
160
+ FAILED=$((FAILED + 1))
161
+ fi
162
+
163
+ # Anthropic (different API shape, handle separately)
164
+ if [ -n "$ANTHROPIC_API_KEY" ]; then
165
+ anthro_file="$RESULTS_DIR/anthropic.json"
166
+ local http_code
167
+ http_code=$(curl -s -o "$anthro_file" -w "%{http_code}" \
168
+ --max-time "$TIMEOUT" \
169
+ -H "x-api-key: $ANTHROPIC_API_KEY" \
170
+ -H "anthropic-version: 2023-06-01" \
171
+ -H "Content-Type: application/json" \
172
+ -d "$(jq -n \
173
+ --arg content "$PROMPT" \
174
+ '{
175
+ model: "claude-3-5-haiku-latest",
176
+ max_tokens: 100,
177
+ messages: [{role: "user", content: $content}]
178
+ }')" \
179
+ "https://api.anthropic.com/v1/messages" 2>/dev/null)
180
+
181
+ if [ "$http_code" = "200" ]; then
182
+ COMPLETED=$((COMPLETED + 1))
183
+ else
184
+ echo "{\"error\": true, \"http_code\": $http_code}" > "$anthro_file"
185
+ FAILED=$((FAILED + 1))
186
+ fi
187
+ else
188
+ echo -e "${RED} ❌ Anthropic — ANTHROPIC_API_KEY not set${NC}"
189
+ FAILED=$((FAILED + 1))
190
+ fi
191
+
192
+ echo ""
193
+
194
+ # =============================================================================
195
+ # Print results
196
+ # =============================================================================
197
+ echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
198
+ echo -e "${GREEN} Results — ${COMPLETED} succeeded, ${FAILED} failed${NC}"
199
+ echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
200
+ echo ""
201
+
202
+ # Print each result if the file exists
203
+ [ -n "$groq_file" ] && [ -f "$groq_file" ] && print_result "Groq" "$MAGENTA" "$groq_file"
204
+ [ -n "$nvidia_file" ] && [ -f "$nvidia_file" ] && print_result "NVIDIA NIM" "$CYAN" "$nvidia_file"
205
+ [ -n "$cerebras_file" ] && [ -f "$cerebras_file" ] && print_result "Cerebras" "$GREEN" "$cerebras_file"
206
+ [ -n "$openai_file" ] && [ -f "$openai_file" ] && print_result "OpenAI" "$YELLOW" "$openai_file"
207
+ if [ -n "$anthro_file" ] && [ -f "$anthro_file" ]; then
208
+ if jq -e '.error == true' "$anthro_file" >/dev/null 2>&1; then
209
+ echo -e "${RED} ❌ Anthropic (HTTP $(jq -r '.http_code' "$anthro_file"))${NC}"
210
+ else
211
+ local content
212
+ content=$(jq -r '.content[0].text // "N/A"' "$anthro_file" 2>/dev/null)
213
+ local model
214
+ model=$(jq -r '.model // "N/A"' "$anthro_file" 2>/dev/null)
215
+ echo -e "${BLUE} ● Anthropic${NC}"
216
+ echo " Model: $model"
217
+ echo " Response: $content"
218
+ fi
219
+ fi
220
+
221
+ echo ""
222
+ echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
223
+ echo -e " Run with A3M Proxy (after ${YELLOW}npx a3m-router serve${NC}):"
224
+ echo ""
225
+ echo " curl http://localhost:8787/v1/chat/completions \\"
226
+ echo " -H \"Content-Type: application/json\" \\"
227
+ echo " -d '{\"model\":\"auto\",\"messages\":[{\"role\":\"user\",\"content\":\"$PROMPT\"}]}'"
228
+ echo ""
229
+ echo -e "${GREEN}━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━━${NC}"
230
+ echo ""