adaptive-memory-multi-model-router 2.14.17 → 2.14.19

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 (45) hide show
  1. package/AGENT_COUNCIL_FINDINGS.md +142 -0
  2. package/LAUNCH_CHECKLIST.md +141 -0
  3. package/README.md.bak +836 -0
  4. package/articles/CHINESE_SUBMISSIONS_READY.md +322 -0
  5. package/articles/DEVTO_READY.md +255 -0
  6. package/articles/HN_POST_READY.md +137 -0
  7. package/articles/INDIEHACKERS_READY.md +120 -0
  8. package/articles/NEWSLETTER_SEND_NOW.md +259 -0
  9. package/articles/PRODUCTHUNT_READY.md +106 -0
  10. package/articles/REDDIT_SUBMISSION_READY.md +348 -0
  11. package/articles/TWEET_STORM_READY.md +165 -0
  12. package/benchmark-results.json +24 -24
  13. package/council-votes/architecture-vote.md +121 -0
  14. package/council-votes/coverage-vote.md +93 -0
  15. package/dist/cost/costTracker.d.ts +109 -44
  16. package/dist/cost/costTracker.js +321 -98
  17. package/dist/cost/costTracker.js.map +1 -1
  18. package/dist/index.d.ts +6 -4
  19. package/dist/routing/advancedRouter.d.ts +38 -43
  20. package/dist/routing/advancedRouter.js +396 -408
  21. package/dist/routing/advancedRouter.js.map +1 -1
  22. package/dist/routing/providers/providerConfig.d.ts +49 -0
  23. package/dist/routing/providers/providerConfig.js +883 -0
  24. package/dist/routing/routing/advancedRouter.d.ts +62 -0
  25. package/dist/routing/routing/advancedRouter.js +447 -0
  26. package/dist/routing/utils/tokenUtils.d.ts +52 -0
  27. package/dist/routing/utils/tokenUtils.js +129 -0
  28. package/dist/server/proxyServer.d.ts +1 -1
  29. package/dist/utils/costUtils.d.ts +57 -0
  30. package/dist/utils/costUtils.js +150 -0
  31. package/dist/utils/costUtils.js.map +1 -0
  32. package/dist/utils/sorting.d.ts +12 -0
  33. package/dist/utils/sorting.js +37 -0
  34. package/dist/utils/sorting.js.map +1 -0
  35. package/package.json +1 -1
  36. package/research/ensemble-voting.md +324 -0
  37. package/research/loss-functions.md +545 -0
  38. package/research-log.md +49 -0
  39. package/src/cost/costTracker.ts +576 -0
  40. package/src/routing/advancedRouter.ts +540 -0
  41. package/src/utils/costUtils.ts +157 -0
  42. package/src/utils/sorting.ts +42 -0
  43. package/test-council/AGENT_COUNCIL_ARCHITECTURE.md +349 -0
  44. package/tests/security/guardrailEngine.test.ts +700 -0
  45. package/research/PUBLISH_LOG.md +0 -3
@@ -0,0 +1,42 @@
1
+ /**
2
+ * Quickselect algorithm for O(n) top-K selection
3
+ * Returns top k elements sorted descending by compare function
4
+ *
5
+ * Best case: O(n), Worst case: O(n log n), Average: O(n)
6
+ * vs Timsort O(n log n) for full sort + O(k log n) for top-k slice
7
+ */
8
+ export function quickselectTopK<T>(
9
+ arr: T[],
10
+ k: number,
11
+ compare: (a: T) => number
12
+ ): T[] {
13
+ if (arr.length <= k) {
14
+ return arr.slice().sort((a, b) => compare(b) - compare(a));
15
+ }
16
+
17
+ const pivotIndex = Math.floor(Math.random() * arr.length);
18
+ const pivotVal = compare(arr[pivotIndex]);
19
+
20
+ const left = arr.filter(a => compare(a) > pivotVal);
21
+ const right = arr.filter(a => compare(a) < pivotVal);
22
+ const middle = arr.filter(a => compare(a) === pivotVal);
23
+
24
+ if (left.length >= k) {
25
+ return quickselectTopK(left, k, compare);
26
+ }
27
+
28
+ const needed = k - left.length;
29
+ if (needed <= middle.length) {
30
+ return [...left, ...middle.slice(0, needed)];
31
+ }
32
+
33
+ return [...left, ...middle, ...quickselectTopK(right, k - left.length - middle.length, compare)];
34
+ }
35
+
36
+ /**
37
+ * Select top candidate using Quickselect
38
+ */
39
+ export function selectTop<T>(arr: T[], compare: (a: T) => number): T | undefined {
40
+ const result = quickselectTopK(arr, 1, compare);
41
+ return result[0];
42
+ }
@@ -0,0 +1,349 @@
1
+ # Agent Council: Architecture Deepening Analysis
2
+
3
+ **Router:** adaptive-memory-multi-model-router (TMLPD)
4
+ **Date:** 2026-06-03
5
+ **Council Role:** Architecture Agent
6
+
7
+ ---
8
+
9
+ ## Executive Summary
10
+
11
+ The codebase has a **structural anomaly**: two critical modules are imported throughout the codebase but do not exist as TypeScript files. Beyond this, the architecture shows four high-value deepening candidates. The unique differentiator (ensemble parallel voting) is under-implemented relative to its strategic importance.
12
+
13
+ ---
14
+
15
+ ## Anomaly: Missing TypeScript Modules
16
+
17
+ Two modules are exported and imported everywhere but have no corresponding `.ts` file:
18
+
19
+ | Missing File | Referenced In | Exported From |
20
+ |---|---|---|
21
+ | `src/routing/advancedRouter.ts` | `src/index.ts`, `src/sdk.ts`, `src/server/modelMapper.ts` | `routeQuery`, `routeBatch`, `recommendForTask`, `extractQueryFeatures`, `MODEL_PROFILES`, `updateModelProfile`, `getProviderHealth` |
22
+ | `src/cost/costTracker.ts` | `src/index.ts`, `src/server/proxyServer.ts` | `CostTracker` class |
23
+
24
+ **Impact:** The main routing engine and cost recording system are entirely absent. The TypeScript routing layer is non-functional without these files. The Python `universal_router.py` partially fills this gap but has no TypeScript integration point.
25
+
26
+ ---
27
+
28
+ ## Candidate 1: The Routing Engine Hole (P0)
29
+
30
+ ### Files Affected
31
+ - **MISSING:** `src/routing/advancedRouter.ts`
32
+ - **AFFECTED:** `src/sdk.ts`, `src/server/modelMapper.ts`, `src/index.ts`
33
+
34
+ ### Problem: Interface Without Implementation
35
+
36
+ The TypeScript surface area exports routing functions (`routeQuery`, `extractQueryFeatures`, `routeBatch`) but the file containing them does not exist. The `modelMapper.ts` imports from this non-existent file:
37
+
38
+ ```typescript
39
+ // modelMapper.ts line 5
40
+ import { routeQuery } from "../routing/advancedRouter"; // FILE DOES NOT EXIST
41
+ ```
42
+
43
+ The routing decision pipeline is split across three uncoordinated systems:
44
+ 1. **TypeScript `modelMapper.ts`** (trivial: alias resolution only)
45
+ 2. **TypeScript `proxyServer.ts`** (handles fallback chain manually)
46
+ 3. **Python `universal_router.py`** (learned routing, but no TypeScript bridge)
47
+
48
+ ### Solution: Implement `advancedRouter.ts`
49
+
50
+ Create `src/routing/advancedRouter.ts` as the single routing decision engine:
51
+
52
+ ```
53
+ src/routing/advancedRouter.ts
54
+ ├── extractQueryFeatures() // Feature extraction (10+ signals)
55
+ ├── MODEL_PROFILES // Static quality profiles per model
56
+ ├── updateModelProfile() // Runtime profile updates
57
+ ├── getProviderHealth() // Health status per provider
58
+ ├── routeQuery() // Main routing decision
59
+ ├── routeBatch() // Batch routing
60
+ └── recommendForTask() // Task-based recommendations
61
+ ```
62
+
63
+ This module should:
64
+ - Call `UniversalModelRouter` from Python via FFI/subprocess for learned routing
65
+ - Provide the TypeScript feature extraction layer (complexity, domain, code, math, multilingual signals)
66
+ - Manage the tier-based routing (free/cheap/mid/premium)
67
+ - Integrate with `ProviderHealthManager` for fallback chain ordering
68
+
69
+ ### Benefits
70
+ - **Leverage:** Enables the full routing pipeline. Without this, the TypeScript SDK is non-functional.
71
+ - **Locality:** All routing logic in one place. Currently scattered across 3 files.
72
+ - **Testability:** Can unit-test feature extraction and routing decisions independently.
73
+ - **Seam quality:** Provides a clean interface for the Python learned router.
74
+
75
+ ---
76
+
77
+ ## Candidate 2: Ensemble Voting is Shallow (P0 — Core Differentiator)
78
+
79
+ ### Files Affected
80
+ - `src/ensemble.ts` (66 lines)
81
+ - `src/index.ts` (EnsembleOrchestrator)
82
+
83
+ ### Problem: Shallow Module
84
+
85
+ The ARCHITECTURE.md claims the **ensemble parallel voting** is the unique differentiator — "Nobody does parallel multi-LLM execution with result merging." Yet the `EnsembleOrchestrator` class is 66 lines of naive code:
86
+
87
+ ```typescript
88
+ // ensemble.ts — current implementation
89
+ if (strategy === 'majority') {
90
+ const counts = {};
91
+ successful.forEach(r => counts[r.answer] = (counts[r.answer] || 0) + 1);
92
+ // ... naive string equality matching
93
+ }
94
+ ```
95
+
96
+ Problems:
97
+ 1. **No parallel execution:** Uses `Promise.all` but treats results as independent strings
98
+ 2. **No confidence weighting:** Doesn't use quality scores from provider responses
99
+ 3. **No semantic similarity:** Uses exact string matching for voting — answers rarely match exactly
100
+ 4. **No answer fusion:** Doesn't merge partial or complementary answers
101
+ 5. **No model quality profiles:** Ignores the `MODEL_PROFILES` that should inform weighting
102
+ 6. **Dead code:** `EnsembleOrchestrator` receives `router: A3MRouter` but the A3MRouter has no `.chat()` method — the ensemble can't actually call providers
103
+
104
+ ### Solution: Deepen the Ensemble System
105
+
106
+ Split `ensemble.ts` into a proper subsystem:
107
+
108
+ ```
109
+ src/ensemble/
110
+ ├── index.ts # EnsembleOrchestrator facade
111
+ ├── parallelExecutor.ts # Promise.all parallel dispatch
112
+ ├── semanticVoter.ts # Cosine-similarity-based answer voting
113
+ ├── confidenceWeighter.ts # Model quality profile weighting
114
+ ├── answerFusion.ts # Merge complementary partial answers
115
+ └── types.ts # EnsembleStrategy, EnsembleResult types
116
+ ```
117
+
118
+ Key improvements:
119
+ - **Semantic voting:** Embed provider answers and compute cosine similarity for fuzzy agreement
120
+ - **Confidence-weighted merging:** Use `MODEL_PROFILES` quality scores as vote weights
121
+ - **Partial answer fusion:** When no answer achieves consensus, merge the most complementary responses
122
+ - **Timeout-aware parallel:** Set per-provider timeouts and cancel slow providers
123
+
124
+ ### Benefits
125
+ - **Leverage:** This IS the product's unique value. Currently it's marketing, not code.
126
+ - **Locality:** Ensemble logic isolated to its own subsystem.
127
+ - **Testability:** Each sub-component (voter, weighter, fusion) independently testable.
128
+ - **Strategic:** Reinforces the core competitive advantage.
129
+
130
+ ---
131
+
132
+ ## Candidate 3: Cost Tracking System is Missing (P1)
133
+
134
+ ### Files Affected
135
+ - **MISSING:** `src/cost/costTracker.ts`
136
+ - **AFFECTED:** `src/server/proxyServer.ts`, `src/index.ts`
137
+
138
+ ### Problem: Incomplete Module
139
+
140
+ `proxyServer.ts` imports and instantiates `CostTracker`:
141
+ ```typescript
142
+ import { CostTracker } from "../cost/costTracker"; // FILE DOES NOT EXIST
143
+ const costTracker = new CostTracker();
144
+ ```
145
+
146
+ The `CostAnalytics.ts` (304 lines) is fully implemented and rich. But the core `CostTracker` class it depends on is missing. This means cost recording at the proxy layer doesn't exist.
147
+
148
+ ### Solution: Implement `costTracker.ts`
149
+
150
+ ```typescript
151
+ // src/cost/costTracker.ts
152
+ export class CostTracker {
153
+ private records: Map<string, CostRecord[]>;
154
+
155
+ record(provider: string, model: string, inputTokens: number, outputTokens: number, latencyMs: number): void
156
+ getSummary(period?: 'hour' | 'day' | 'week' | 'month'): CostSummary
157
+ getByProvider(): Record<string, ProviderCostSummary>
158
+ getByModel(provider: string): Record<string, ModelCostSummary>
159
+ reset(): void
160
+ }
161
+ ```
162
+
163
+ This should:
164
+ - Record every request with full metadata (provider, model, tokens, latency)
165
+ - Compute per-request cost using provider's `costPerK` rates
166
+ - Feed into `CostAnalytics` for aggregate reporting
167
+ - Provide the data layer that `costAnalytics.ts` operates on
168
+
169
+ ### Benefits
170
+ - **Leverage:** Enables the cost tracking and savings reporting (key user value).
171
+ - **Locality:** Cost recording isolated to its own class.
172
+ - **Seam quality:** Clean interface between the proxy server and analytics.
173
+ - **Testability:** Can test cost calculations independently.
174
+
175
+ ---
176
+
177
+ ## Candidate 4: Monolithic Provider Configuration (P1)
178
+
179
+ ### Files Affected
180
+ - `src/providers/providerConfig.ts` (951 lines, single file)
181
+
182
+ ### Problem: Shallow/Tightly-Coupled Monolith
183
+
184
+ All 40+ providers are defined in a single 951-line file with zero internal structure:
185
+
186
+ ```typescript
187
+ // All in one file:
188
+ export const DEFAULT_PROVIDERS: Record<string, ProviderDefinition> = {
189
+ ollama: { ... },
190
+ lmstudio: { ... },
191
+ groq: { ... },
192
+ // 40 more...
193
+ deepseek: { ... },
194
+ // ...all in one object
195
+ };
196
+ ```
197
+
198
+ Problems:
199
+ 1. **Interface ≈ Implementation:** The file IS the data. Adding a provider requires editing the file.
200
+ 2. **No lazy loading:** All providers initialized on import, even if API keys are missing.
201
+ 3. **Testability:** Cannot test one provider's logic in isolation.
202
+ 4. **Maintenance:** Hard to track provider-specific behavior (Chinese provider latency handling is scattered in `providerRetry.ts`).
203
+ 5. **Coupling:** Provider definitions, config loading, runtime registration, and health checking all in one file.
204
+
205
+ ### Solution: Provider Subsystem Deepening
206
+
207
+ Split into a provider subsystem:
208
+
209
+ ```
210
+ src/providers/
211
+ ├── index.ts # Re-exports
212
+ ├── types.ts # ProviderTier, ProviderFormat, ProviderType, ProviderDefinition
213
+ ├── defaults/
214
+ │ ├── free.ts # Ollama, LM Studio, vLLM, Google, NVIDIA NIM
215
+ │ ├── cheap.ts # Groq, Cerebras, DeepInfra, Together, Fireworks...
216
+ │ ├── mid.ts # DeepSeek, Mistral, Perplexity, Cohere...
217
+ │ ├── premium.ts # OpenAI, Anthropic, xAI
218
+ │ └── enterprise.ts # Azure, Bedrock, Vertex
219
+ ├── registry.ts # ProviderRegistry class (lazy loading, runtime registration)
220
+ ├── loader.ts # Config file loading (JSON → ProviderDefinition[])
221
+ ├── health.ts # ProviderHealth interface + checker
222
+ └── providerConfig.ts # Keep only the runtime API (registerProvider, getAvailableProviders)
223
+ ```
224
+
225
+ Each tier file exports only its provider definitions:
226
+ ```typescript
227
+ // src/providers/defaults/cheap.ts
228
+ export const CHEAP_PROVIDERS: ProviderDefinition[] = [
229
+ groqProvider(),
230
+ cerebrasProvider(),
231
+ // ...
232
+ ];
233
+
234
+ function groqProvider(): ProviderDefinition {
235
+ return {
236
+ id: 'groq',
237
+ name: 'Groq',
238
+ baseUrl: 'https://api.groq.com/openai/v1/chat/completions',
239
+ // ...
240
+ };
241
+ }
242
+ ```
243
+
244
+ Benefits:
245
+ - **Leverage:** Lazy loading means only providers with valid API keys are initialized.
246
+ - **Locality:** Provider-specific behavior (timeout, retry config) co-located with the provider definition.
247
+ - **Testability:** Each provider factory function independently testable.
248
+ - **Seam quality:** Breaking the monolith creates clean seams between tiers.
249
+
250
+ ---
251
+
252
+ ## Candidate 5: Proxy Server Monolith (P1)
253
+
254
+ ### Files Affected
255
+ - `src/server/proxyServer.ts` (1105 lines, single file)
256
+
257
+ ### Problem: God Object
258
+
259
+ The proxy server handles **6 distinct responsibilities** in one 1105-line file:
260
+
261
+ 1. HTTP server setup + routing
262
+ 2. OpenAI-compatible request parsing
263
+ 3. Provider calls (OpenAI, Anthropic, Google, local)
264
+ 4. Streaming SSE handling (5+ code paths)
265
+ 5. Fallback chain logic
266
+ 6. Request logging + health reporting
267
+
268
+ This makes it impossible to:
269
+ - Test streaming logic without a full HTTP server
270
+ - Swap the Anthropic API formatter independently
271
+ - Modify the fallback logic without touching provider call code
272
+ - Add a new API format (e.g., Cohere, AWS Bedrock) cleanly
273
+
274
+ ### Solution: Deepen into Subsystem
275
+
276
+ ```
277
+ src/server/
278
+ ├── proxyServer.ts # Thin HTTP glue (~100 lines)
279
+ ├── handlers/
280
+ │ ├── chatCompletions.ts # POST /v1/chat/completions
281
+ │ ├── completions.ts # POST /v1/completions
282
+ │ └── models.ts # GET /v1/models
283
+ ├── providers/
284
+ │ ├── openaiCompat.ts # Standard OpenAI-compatible calls
285
+ │ ├── anthropic.ts # Anthropic Messages API
286
+ │ ├── google.ts # Google Gemini API
287
+ │ └── local.ts # Ollama, vLLM, LM Studio
288
+ ├── streaming/
289
+ │ ├── sseNormalizer.ts # Normalize SSE streams to OpenAI format
290
+ │ └── streamManager.ts # Timeout, backpressure, chunk handling
291
+ ├── fallback/
292
+ │ └── fallbackChain.ts # Provider fallback orchestration
293
+ └── healthReporter.ts # /health endpoint data assembly
294
+ ```
295
+
296
+ ### Benefits
297
+ - **Leverage:** Each provider format independently swappable.
298
+ - **Locality:** A bug in streaming logic doesn't touch provider calls.
299
+ - **Testability:** Each handler and provider formatter testable in isolation.
300
+ - **Extensibility:** Adding Cohere or Bedrock formats = new file, no existing file edited.
301
+
302
+ ---
303
+
304
+ ## Summary Table
305
+
306
+ | Candidate | Files | Severity | Impact | Effort |
307
+ |---|---|---|---|---|
308
+ | 1. Routing Engine Hole | `advancedRouter.ts` (MISSING) | P0 | TypeScript SDK non-functional | Medium |
309
+ | 2. Ensemble Voting | `ensemble.ts` (66 lines) | P0 | Core differentiator under-built | High |
310
+ | 3. CostTracker Missing | `costTracker.ts` (MISSING) | P1 | Cost recording doesn't work | Medium |
311
+ | 4. Provider Monolith | `providerConfig.ts` (951 lines) | P1 | Hard to maintain, test | Medium |
312
+ | 5. Proxy Monolith | `proxyServer.ts` (1105 lines) | P1 | Untestable streaming logic | High |
313
+
314
+ ---
315
+
316
+ ## Top 3 Recommendations for Council Vote
317
+
318
+ ### Recommendation 1: CRITICAL — Fix the Missing Routing Engine (Vote: P0)
319
+ **File to create:** `src/routing/advancedRouter.ts`
320
+
321
+ This unblocks the entire TypeScript routing pipeline. Without it, the SDK, proxy, and modelMapper all have dangling imports. Prioritize before any other work.
322
+
323
+ **Acceptance criteria:**
324
+ - `routeQuery()` returns a valid `RouterDecision` for any string input
325
+ - `extractQueryFeatures()` produces 10+ signal feature vector
326
+ - Integrates with `ProviderHealthManager` for health-aware fallback ordering
327
+ - Exports `MODEL_PROFILES` as the static quality baseline
328
+
329
+ ### Recommendation 2: HIGH — Deepen Ensemble Voting (Vote: P0)
330
+ **File to modify:** `src/ensemble.ts` → `src/ensemble/`
331
+
332
+ Build the ensemble subsystem that is the claimed unique differentiator. Start with semantic voting (cosine similarity on embeddings) and confidence weighting. The current exact-string-match voting will never produce results in production.
333
+
334
+ **Acceptance criteria:**
335
+ - Ensemble dispatches to 3+ providers in parallel with per-provider timeouts
336
+ - Voting uses semantic similarity (embeddings) not exact string match
337
+ - Confidence-weighted merging uses `MODEL_PROFILES` quality scores
338
+ - "Uncertain" flag triggers fallback or partial answer fusion
339
+
340
+ ### Recommendation 3: HIGH — Implement CostTracker (Vote: P1)
341
+ **File to create:** `src/cost/costTracker.ts`
342
+
343
+ The `CostAnalytics` (304 lines) is the most complete analytics module in the codebase. It needs the `CostTracker` data layer to function. This unblocks the savings reporting which is a key selling point.
344
+
345
+ **Acceptance criteria:**
346
+ - `CostTracker.record()` stores per-request data (provider, model, tokens, latency, cost)
347
+ - Integrates with proxy server to record every request
348
+ - Feeds `CostAnalytics` for aggregate reporting
349
+ - Supports reset and export