adaptive-memory-multi-model-router 2.13.18 → 2.13.22
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.
- package/.dockerignore +82 -0
- package/.env.example +303 -0
- package/.github/ISSUE_TEMPLATE/bug_report.md +83 -12
- package/.github/ISSUE_TEMPLATE/config.yml +12 -6
- package/.github/ISSUE_TEMPLATE/feature_request.md +61 -10
- package/.github/PULL_REQUEST_TEMPLATE.md +53 -26
- package/.github/dependabot.yml +9 -0
- package/.github/workflows/codeql.yml +38 -0
- package/.github/workflows/npm-publish.yml +20 -0
- package/.github/workflows/stale.yml +56 -0
- package/ARCHITECTURE.md +346 -0
- package/AUDIT_REPORT.md +28 -0
- package/CHANGELOG.md +386 -22
- package/CONTRIBUTORS.md +20 -0
- package/Dockerfile +53 -0
- package/Dockerfile.proxy +33 -0
- package/PR_STATUS_REPORT.md +148 -0
- package/README.md +22 -0
- package/RUNKIT.md +83 -0
- package/_schema.html +61 -15
- package/articles/AI_AGENT_LLM_ROUTING.md +150 -0
- package/articles/FROM_ZERO_TO_10K.md +107 -0
- package/articles/LLM_BENCHMARK_DEEP_DIVE.md +153 -0
- package/articles/TWEETS_10K_DOWNLOADS.md +47 -0
- package/articles/TWEETS_BENCHMARK_FIRST.md +46 -0
- package/articles/TWEETS_MCP_PLAY.md +51 -0
- package/articles/TWEETS_SEQUENTIAL_BROKEN.md +49 -0
- package/articles/TWEETS_WHY_BUILD.md +54 -0
- package/benchmark-results.json +26 -45
- package/cli/a3m +840 -0
- package/demo/package.json +13 -0
- package/demo/public/index.html +762 -0
- package/demo/server.js +405 -0
- package/dist/cli.js +4 -0
- package/docker-compose.yml +74 -0
- package/docs/.nojekyll +0 -0
- package/docs/BENCHMARK.md +96 -22
- package/docs/_config.yml +49 -0
- package/docs/api.html +513 -0
- package/docs/benchmark.html +387 -0
- package/docs/cli-cheatsheet.md +339 -0
- package/docs/comparison.md +108 -0
- package/docs/curl-examples.md +247 -0
- package/docs/index.html +390 -99
- package/docs/openapi.yaml +1318 -0
- package/docs/quick-start.html +366 -0
- package/docs/robots.txt +1 -1
- package/docs/sitemap.xml +23 -5
- package/docs/styles.css +682 -0
- package/examples/README.md +61 -0
- package/examples/a3m-sdk.js +124 -0
- package/examples/basic-route.js +54 -0
- package/examples/chat-loop.js +202 -0
- package/examples/classify-then-route.js +102 -0
- package/examples/cost-compare.js +120 -0
- package/examples/ensemble.js +160 -0
- package/integrations/langchain/README.md +216 -0
- package/integrations/langchain/a3m_langchain.ts +1360 -0
- package/integrations/langchain/example.ts +287 -0
- package/integrations/vercel-ai-sdk/README.md +49 -0
- package/integrations/vercel-ai-sdk/a3m_provider.ts +78 -0
- package/integrations/vercel-ai-sdk/example.ts +25 -0
- package/llms-full.txt +43 -0
- package/llms.txt +9 -0
- package/mcp-server/README.md +188 -0
- package/mcp-server/package.json +29 -0
- package/mcp-server/src/index.ts +744 -0
- package/mcp-server/tsconfig.json +19 -0
- package/package.json +3 -3
- package/proxy/README.md +227 -0
- package/proxy/package-lock.json +831 -0
- package/proxy/package.json +17 -0
- package/proxy/rate-limit.js +145 -0
- package/proxy/rate-limit.test.js +311 -0
- package/proxy/server.js +970 -0
- package/scripts/banner.js +29 -0
- package/scripts/compare-providers.sh +230 -0
- package/scripts/cross_post.py +443 -0
- package/scripts/publish_fcc.py +106 -0
- package/scripts/push-to-gitee.sh +52 -0
- package/src/tui/dashboard.ts +13 -0
- package/tests/__mocks__/tokenUtils.ts +22 -0
- package/tests/memory/episodicMemory.test.ts +227 -0
- package/tests/package-lock.json +1628 -0
- package/tests/package.json +18 -0
- package/tests/routing/ensembleVoting.test.ts +236 -0
- package/tests/routing/providerRetry.test.ts +360 -0
- package/tests/routing/queryTypePresets.test.ts +206 -0
- package/tests/tsconfig.json +21 -0
- package/tests/vitest.config.ts +18 -0
- 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 ""
|