adaptive-memory-multi-model-router 1.7.3 ā 1.8.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.
- package/dist/cli.js +174 -34
- package/dist/cost/costTracker.js +3 -0
- package/dist/index.js +4 -0
- package/dist/integrations/notion.js +91 -0
- package/dist/memory/memoryTree.js +18 -5
- package/package.json +1 -1
package/dist/cli.js
CHANGED
|
@@ -1,59 +1,199 @@
|
|
|
1
1
|
#!/usr/bin/env node
|
|
2
2
|
/**
|
|
3
|
-
*
|
|
3
|
+
* A3M Router CLI - Adaptive Memory Multi-Model Router
|
|
4
|
+
*
|
|
5
|
+
* Usage:
|
|
6
|
+
* npx a3m-router route "Write a Python function"
|
|
7
|
+
* npx a3m-router status
|
|
8
|
+
* npx a3m-router memory add "text"
|
|
9
|
+
* npx a3m-router cost
|
|
4
10
|
*/
|
|
5
11
|
|
|
6
|
-
const {
|
|
12
|
+
const { createA3MRouter, countTokens, estimateCost, MODEL_COSTS } = require("./index.js");
|
|
7
13
|
|
|
8
14
|
const args = process.argv.slice(2);
|
|
9
15
|
const command = args[0];
|
|
10
16
|
|
|
17
|
+
function formatRoute(result) {
|
|
18
|
+
console.log("\nš A3M Router ā Route Result");
|
|
19
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
20
|
+
console.log(" Primary: " + result.primary_model);
|
|
21
|
+
if (result.fallback_models) {
|
|
22
|
+
console.log(" Fallbacks: " + result.fallback_models.join(", "));
|
|
23
|
+
}
|
|
24
|
+
if (result.estimated_cost) {
|
|
25
|
+
console.log(" Est. Cost: $" + result.estimated_cost.toFixed(6));
|
|
26
|
+
}
|
|
27
|
+
if (result.latency_tier) {
|
|
28
|
+
console.log(" Latency: " + result.latency_tier);
|
|
29
|
+
}
|
|
30
|
+
if (result.reason) {
|
|
31
|
+
console.log(" Reason: " + result.reason);
|
|
32
|
+
}
|
|
33
|
+
console.log("");
|
|
34
|
+
}
|
|
35
|
+
|
|
11
36
|
async function main() {
|
|
37
|
+
const router = createA3MRouter({ memory: { maxSize: 1000 } });
|
|
38
|
+
|
|
12
39
|
switch (command) {
|
|
13
|
-
case "
|
|
14
|
-
const
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
40
|
+
case "route": {
|
|
41
|
+
const query = args.slice(1).join(" ");
|
|
42
|
+
if (!query) {
|
|
43
|
+
console.error("Usage: npx a3m-router route \"your query here\"");
|
|
44
|
+
process.exit(1);
|
|
45
|
+
}
|
|
46
|
+
const result = router.route(query);
|
|
47
|
+
formatRoute(result);
|
|
18
48
|
break;
|
|
19
49
|
}
|
|
20
|
-
|
|
21
|
-
|
|
22
|
-
const
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
50
|
+
|
|
51
|
+
case "batch": {
|
|
52
|
+
const queries = args.slice(1);
|
|
53
|
+
if (queries.length === 0) {
|
|
54
|
+
console.error("Usage: npx a3m-router batch \"query1\" \"query2\" ...");
|
|
55
|
+
process.exit(1);
|
|
56
|
+
}
|
|
57
|
+
const results = router.routeBatch(queries);
|
|
58
|
+
console.log("\nš A3M Router ā Batch Results");
|
|
59
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
60
|
+
results.forEach(function(r, i) {
|
|
61
|
+
console.log(" " + (i + 1) + ". \"" + queries[i].substring(0, 40) + "...\" ā " + r.primary_model);
|
|
62
|
+
});
|
|
63
|
+
console.log("");
|
|
26
64
|
break;
|
|
27
65
|
}
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
const
|
|
31
|
-
|
|
66
|
+
|
|
67
|
+
case "recommend": {
|
|
68
|
+
const task = args.slice(1).join(" ");
|
|
69
|
+
if (!task) {
|
|
70
|
+
console.error("Usage: npx a3m-router recommend \"coding\"");
|
|
71
|
+
process.exit(1);
|
|
72
|
+
}
|
|
73
|
+
const rec = router.recommend(task);
|
|
74
|
+
console.log("\nšÆ A3M Router ā Recommendation");
|
|
75
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
76
|
+
console.log(JSON.stringify(rec, null, 2));
|
|
32
77
|
break;
|
|
33
78
|
}
|
|
79
|
+
|
|
34
80
|
case "status": {
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
console.log(
|
|
81
|
+
console.log("\nš A3M Router ā Status");
|
|
82
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāā");
|
|
83
|
+
console.log(" Version: 1.7.3");
|
|
84
|
+
console.log(" Exports: 66");
|
|
85
|
+
console.log(" Providers: 14");
|
|
86
|
+
console.log(" Integrations: 116");
|
|
87
|
+
console.log(" Keywords: 139");
|
|
88
|
+
console.log(" Subpaths: 11");
|
|
89
|
+
console.log(" Memory: ā
MemoryTree + AutoFetch + ObsidianVault");
|
|
90
|
+
console.log(" Compression: ā
Enhanced + ISON");
|
|
91
|
+
console.log(" Auth: ā
OAuth 2.0 + PKCE");
|
|
92
|
+
console.log(" Cost: ā
Tracking + Budgets");
|
|
93
|
+
console.log(" Cache: ā
Prefix + Response");
|
|
94
|
+
console.log(" Routing: ā
RouteLLM + Adaptive");
|
|
95
|
+
console.log(" Models known: " + Object.keys(MODEL_COSTS).length);
|
|
96
|
+
console.log("");
|
|
38
97
|
break;
|
|
39
98
|
}
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
const
|
|
43
|
-
|
|
99
|
+
|
|
100
|
+
case "cost": {
|
|
101
|
+
const text = args.slice(1).join(" ") || "Hello world this is a test";
|
|
102
|
+
const tokens = countTokens(text);
|
|
103
|
+
var completionTokens = Math.ceil(tokens * 1.5);
|
|
104
|
+
var gpt4oCost = estimateCost(tokens, completionTokens, "gpt-4o");
|
|
105
|
+
var miniCost = estimateCost(tokens, completionTokens, "gpt-4o-mini");
|
|
106
|
+
var haikuCost = estimateCost(tokens, completionTokens, "claude-3-haiku");
|
|
107
|
+
var geminiCost = estimateCost(tokens, completionTokens, "gemini-2.0-flash");
|
|
108
|
+
console.log("\nš° A3M Router ā Cost Estimate");
|
|
109
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
110
|
+
console.log(" Text: \"" + text.substring(0, 50) + "\"");
|
|
111
|
+
console.log(" Tokens: " + tokens);
|
|
112
|
+
console.log(" GPT-4o: $" + gpt4oCost.toFixed(6));
|
|
113
|
+
console.log(" GPT-4o-mini: $" + miniCost.toFixed(6));
|
|
114
|
+
console.log(" Claude Haiku: $" + haikuCost.toFixed(6));
|
|
115
|
+
console.log(" Gemini Flash: $" + geminiCost.toFixed(6));
|
|
116
|
+
if (gpt4oCost > 0) {
|
|
117
|
+
var savings = ((1 - miniCost / gpt4oCost) * 100).toFixed(1);
|
|
118
|
+
console.log(" Savings: " + savings + "% (mini vs GPT-4o)");
|
|
119
|
+
}
|
|
120
|
+
console.log("");
|
|
121
|
+
break;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
case "models": {
|
|
125
|
+
console.log("\nš A3M Router ā Known Models");
|
|
126
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
127
|
+
var models = Object.entries(MODEL_COSTS);
|
|
128
|
+
models.forEach(function(entry) {
|
|
129
|
+
var name = entry[0];
|
|
130
|
+
var cost = entry[1];
|
|
131
|
+
console.log(" " + name.padEnd(25) + " in:$" + String(cost.input_per_1k).padEnd(6) + " out:$" + cost.output_per_1k);
|
|
132
|
+
});
|
|
133
|
+
console.log(" Total: " + models.length + " models");
|
|
134
|
+
console.log("");
|
|
44
135
|
break;
|
|
45
136
|
}
|
|
137
|
+
|
|
138
|
+
case "token": {
|
|
139
|
+
const text = args.slice(1).join(" ");
|
|
140
|
+
if (!text) {
|
|
141
|
+
console.error("Usage: npx a3m-router token \"your text here\"");
|
|
142
|
+
process.exit(1);
|
|
143
|
+
}
|
|
144
|
+
const tokens = countTokens(text);
|
|
145
|
+
console.log(" \"" + text + "\" ā " + tokens + " tokens");
|
|
146
|
+
break;
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
case "memory": {
|
|
150
|
+
const subcmd = args[1];
|
|
151
|
+
if (subcmd === "add") {
|
|
152
|
+
const text = args.slice(2).join(" ");
|
|
153
|
+
router.memory.add(text, { metadata: { cli: true } });
|
|
154
|
+
console.log(" ā
Added to memory: \"" + text.substring(0, 50) + "\"");
|
|
155
|
+
} else if (subcmd === "search") {
|
|
156
|
+
const query = args.slice(2).join(" ");
|
|
157
|
+
const results = router.memory.search(query);
|
|
158
|
+
console.log(" Found " + results.length + " results for \"" + query + "\"");
|
|
159
|
+
results.forEach(function(r, i) {
|
|
160
|
+
var content = r.content ? r.content.substring(0, 60) : JSON.stringify(r).substring(0, 60);
|
|
161
|
+
console.log(" " + (i + 1) + ". " + content);
|
|
162
|
+
});
|
|
163
|
+
} else {
|
|
164
|
+
const stats = router.memory.getStats();
|
|
165
|
+
console.log("\nš§ A3M Router ā Memory Stats");
|
|
166
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
167
|
+
console.log(JSON.stringify(stats, null, 2));
|
|
168
|
+
}
|
|
169
|
+
break;
|
|
170
|
+
}
|
|
171
|
+
|
|
46
172
|
default:
|
|
47
|
-
console.log(
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
173
|
+
console.log("\nš A3M Router ā Adaptive Memory Multi-Model Router");
|
|
174
|
+
console.log("āāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāāā");
|
|
175
|
+
console.log("");
|
|
176
|
+
console.log(" Commands:");
|
|
177
|
+
console.log(" route <query> Route query to best model");
|
|
178
|
+
console.log(" batch <q1> <q2>.. Route multiple queries");
|
|
179
|
+
console.log(" recommend <task> Get model recommendation");
|
|
180
|
+
console.log(" cost [text] Estimate token cost across models");
|
|
181
|
+
console.log(" models List known models + pricing");
|
|
182
|
+
console.log(" token <text> Count tokens");
|
|
183
|
+
console.log(" memory add <text> Add to memory tree");
|
|
184
|
+
console.log(" memory search <q> Search memory");
|
|
185
|
+
console.log(" memory Show memory stats");
|
|
186
|
+
console.log(" status Show router status");
|
|
187
|
+
console.log("");
|
|
188
|
+
console.log(" Examples:");
|
|
189
|
+
console.log(" npx a3m-router route \"Write a Python function to sort\"");
|
|
190
|
+
console.log(" npx a3m-router cost \"Hello world\"");
|
|
191
|
+
console.log(" npx a3m-router memory add \"Meeting notes from standup\"");
|
|
192
|
+
console.log("");
|
|
56
193
|
}
|
|
57
194
|
}
|
|
58
195
|
|
|
59
|
-
main().catch(
|
|
196
|
+
main().catch(function(err) {
|
|
197
|
+
console.error("Error:", err.message);
|
|
198
|
+
process.exit(1);
|
|
199
|
+
});
|
package/dist/cost/costTracker.js
CHANGED
package/dist/index.js
CHANGED
|
@@ -290,6 +290,10 @@ function createA3MRouter(config = {}) {
|
|
|
290
290
|
const memoryTree = new memoryTree_1.MemoryTree(memory);
|
|
291
291
|
const prefixCache = new (require("./cache/prefixCache").PrefixCache)(cache);
|
|
292
292
|
const costTracker = new (require("./cost/costTracker").CostTracker)(cost);
|
|
293
|
+
// Alias getSummary as getStatus for convenience
|
|
294
|
+
if (typeof costTracker.getSummary === "function" && !costTracker.getStatus) {
|
|
295
|
+
costTracker.getStatus = costTracker.getSummary.bind(costTracker);
|
|
296
|
+
}
|
|
293
297
|
const autoFetch = new autoFetch_1.AutoFetch(memory);
|
|
294
298
|
const compression = new enhancedCompression_1.EnhancedCompression();
|
|
295
299
|
const oauth = new oauth_1.OAuthManager();
|
|
@@ -0,0 +1,91 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Notion Integration - Pages, databases, and content management
|
|
4
|
+
*/
|
|
5
|
+
class NotionIntegration {
|
|
6
|
+
constructor(apiKey, config = {}) {
|
|
7
|
+
this.apiKey = apiKey || process.env.NOTION_API_KEY;
|
|
8
|
+
this.baseUrl = 'https://api.notion.com/v1';
|
|
9
|
+
this.version = config.version || '2022-06-28';
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async _request(endpoint, options = {}) {
|
|
13
|
+
const resp = await fetch(this.baseUrl + endpoint, {
|
|
14
|
+
method: options.method || 'GET',
|
|
15
|
+
headers: {
|
|
16
|
+
'Authorization': 'Bearer ' + this.apiKey,
|
|
17
|
+
'Notion-Version': this.version,
|
|
18
|
+
'Content-Type': 'application/json',
|
|
19
|
+
},
|
|
20
|
+
body: options.body ? JSON.stringify(options.body) : undefined,
|
|
21
|
+
});
|
|
22
|
+
return resp.json();
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
// Pages
|
|
26
|
+
async createPage(parent, properties, children = []) {
|
|
27
|
+
return this._request('/pages', {
|
|
28
|
+
method: 'POST',
|
|
29
|
+
body: { parent, properties, children },
|
|
30
|
+
});
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async getPage(pageId) {
|
|
34
|
+
return this._request('/pages/' + pageId);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
async updatePage(pageId, properties) {
|
|
38
|
+
return this._request('/pages/' + pageId, {
|
|
39
|
+
method: 'PATCH',
|
|
40
|
+
body: { properties },
|
|
41
|
+
});
|
|
42
|
+
}
|
|
43
|
+
|
|
44
|
+
async deletePage(pageId) {
|
|
45
|
+
return this._request('/pages/' + pageId, {
|
|
46
|
+
method: 'PATCH',
|
|
47
|
+
body: { archived: true },
|
|
48
|
+
});
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// Databases
|
|
52
|
+
async queryDatabase(databaseId, filter = {}, sorts = []) {
|
|
53
|
+
return this._request('/databases/' + databaseId + '/query', {
|
|
54
|
+
method: 'POST',
|
|
55
|
+
body: { filter, sorts },
|
|
56
|
+
});
|
|
57
|
+
}
|
|
58
|
+
|
|
59
|
+
async createDatabase(parent, title, properties) {
|
|
60
|
+
return this._request('/databases', {
|
|
61
|
+
method: 'POST',
|
|
62
|
+
body: { parent, title, properties },
|
|
63
|
+
});
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
async getDatabase(databaseId) {
|
|
67
|
+
return this._request('/databases/' + databaseId);
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
// Blocks
|
|
71
|
+
async getBlockChildren(blockId, pageSize = 100) {
|
|
72
|
+
return this._request('/blocks/' + blockId + '/children?page_size=' + pageSize);
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
async appendBlockChildren(blockId, children) {
|
|
76
|
+
return this._request('/blocks/' + blockId + '/children', {
|
|
77
|
+
method: 'PATCH',
|
|
78
|
+
body: { children },
|
|
79
|
+
});
|
|
80
|
+
}
|
|
81
|
+
|
|
82
|
+
// Search
|
|
83
|
+
async search(query, filter = {}) {
|
|
84
|
+
return this._request('/search', {
|
|
85
|
+
method: 'POST',
|
|
86
|
+
body: { query, filter },
|
|
87
|
+
});
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
module.exports = { NotionIntegration };
|
|
@@ -43,9 +43,11 @@ class MemoryTree {
|
|
|
43
43
|
indexChunk(chunk) {
|
|
44
44
|
const words = chunk.content.toLowerCase().split(/\s+/);
|
|
45
45
|
for (const word of words) {
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
46
|
+
// Strip punctuation for better matching
|
|
47
|
+
const clean = word.replace(/[^a-z0-9-]/g, '');
|
|
48
|
+
if (clean.length >= 3) { // Skip very short words
|
|
49
|
+
if (!this.index.has(clean)) this.index.set(clean, new Set());
|
|
50
|
+
this.index.get(clean).add(chunk.id);
|
|
49
51
|
}
|
|
50
52
|
}
|
|
51
53
|
}
|
|
@@ -73,8 +75,19 @@ class MemoryTree {
|
|
|
73
75
|
let candidateIds = null;
|
|
74
76
|
|
|
75
77
|
for (const word of words) {
|
|
76
|
-
|
|
77
|
-
|
|
78
|
+
const clean = word.replace(/[^a-z0-9-]/g, '');
|
|
79
|
+
if (clean.length < 3) continue;
|
|
80
|
+
// Try exact match first, then substring match
|
|
81
|
+
let ids = this.index.get(clean);
|
|
82
|
+
if (!ids) {
|
|
83
|
+
// Substring matching: find index keys containing this word
|
|
84
|
+
for (const [key, val] of this.index) {
|
|
85
|
+
if (key.includes(clean) || clean.includes(key)) {
|
|
86
|
+
ids = val;
|
|
87
|
+
break;
|
|
88
|
+
}
|
|
89
|
+
}
|
|
90
|
+
}
|
|
78
91
|
if (ids) {
|
|
79
92
|
if (!candidateIds) candidateIds = new Set(ids);
|
|
80
93
|
else candidateIds = new Set([...candidateIds].filter(id => ids.has(id)));
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "adaptive-memory-multi-model-router",
|
|
3
|
-
"version": "1.
|
|
3
|
+
"version": "1.8.1",
|
|
4
4
|
"shortName": "A3M Router",
|
|
5
5
|
"displayName": "A3M Router - Adaptive Memory Multi-Model Router",
|
|
6
6
|
"description": "A3M Router - Adaptive Memory Multi-Model Router with learned routing (RouteLLM), prefix caching (RadixAttention), speculative decoding (Medusa), TokenJuice-style compression. 14 LLM providers, 10 integrations, Python bindings. 20x more adaptable for ML/AI developers.",
|