adaptive-memory-multi-model-router 2.16.0 → 2.16.2
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/.github/workflows/adapters-ci.yml +142 -0
- package/.github/workflows/auto-submit-sitemap.yml +41 -0
- package/.github/workflows/ci.yml +2 -5
- package/.github/workflows/mcp-pypi-publish.yml +34 -0
- package/.github/workflows/pypi-publish.yml +146 -0
- package/.github/workflows/tmlpd-publish.yml +23 -0
- package/README.md +245 -148
- package/RELEASE_v2.16.0.md +149 -0
- package/TECHNICAL_README.md +253 -0
- package/adapters/README.md +36 -0
- package/adapters/__init__.py +25 -0
- package/adapters/a3m_adapter/__init__.py +51 -0
- package/adapters/a3m_adapter/adapter/__init__.py +22 -0
- package/adapters/a3m_adapter/adapter/autogen.py +169 -0
- package/adapters/a3m_adapter/adapter/config.py +100 -0
- package/adapters/a3m_adapter/adapter/haystack.py +197 -0
- package/adapters/a3m_adapter/adapter/langchain.py +155 -0
- package/adapters/a3m_adapter/adapter/langgraph.py +196 -0
- package/adapters/a3m_adapter/adapter/llamaindex.py +162 -0
- package/adapters/a3m_adapter/adapter/pinecone.py +217 -0
- package/adapters/a3m_adapter/adapter/vercel.py +188 -0
- package/adapters/a3m_adapter/tests/__init__.py +1 -0
- package/adapters/a3m_adapter/tests/test_adapters.py +118 -0
- package/adapters/a3m_adapter/tests/test_integration.py +80 -0
- package/adapters/requirements-dev.txt +6 -0
- package/adapters/requirements.txt +4 -0
- package/adapters/setup.py +23 -0
- package/demo.py +251 -0
- package/discoverability-diagnosis.md +280 -0
- package/dist/analytics/costAnalytics.d.ts.map +1 -1
- package/dist/benchmark/reproducible.d.ts.map +1 -1
- package/dist/cache/semanticCache.d.ts.map +1 -1
- package/dist/cli/setupWizard.d.ts +257 -50
- package/dist/cli/setupWizard.d.ts.map +1 -1
- package/dist/cli/setupWizard.js +419 -109
- package/dist/cli/setupWizard.js.map +1 -1
- package/dist/cli/tui.d.ts +6 -0
- package/dist/cli/tui.js +96 -67
- package/dist/cli/tui.js.map +1 -0
- package/dist/cli.js +9 -0
- package/dist/cost/budgetEnforcer.d.ts.map +1 -1
- package/dist/cost/costTracker.d.ts.map +1 -1
- package/dist/ensemble/multiRoundDialog.d.ts.map +1 -1
- package/dist/ensemble/shapleyValue.d.ts.map +1 -1
- package/dist/ensemble.d.ts +1 -1
- package/dist/ensemble.js +141 -0
- package/dist/integrations/langchainAdapter.d.ts.map +1 -1
- package/dist/integrations/langchainAdapter.js +3 -3
- package/dist/integrations/langchainAdapter.js.map +1 -1
- package/dist/integrations/oauth.d.ts.map +1 -1
- package/dist/integrations/scienceAdapter.d.ts.map +1 -1
- package/dist/memory/autoFetch.d.ts.map +1 -1
- package/dist/memory/hybridMemory.d.ts.map +1 -1
- package/dist/memory/memoryTree.d.ts.map +1 -1
- package/dist/memory/obsidianVault.d.ts.map +1 -1
- package/dist/memory/reasoningBank.d.ts.map +1 -1
- package/dist/observability/metrics.d.ts.map +1 -1
- package/dist/observability/tracer.d.ts.map +1 -1
- package/dist/providers/providerConfig.d.ts.map +1 -1
- package/dist/providers/providerConfig.js +32 -17
- package/dist/providers/providerConfig.js.map +1 -1
- package/dist/routing/advancedRouter.d.ts.map +1 -1
- package/dist/routing/advancedRouter.js +106 -14
- package/dist/routing/advancedRouter.js.map +1 -1
- package/dist/routing/providerHealth.d.ts.map +1 -1
- package/dist/routing/providerRetry.d.ts.map +1 -1
- package/dist/routing/shadowSampler.d.ts.map +1 -0
- package/dist/routing/shadowSampler.js.map +1 -1
- package/dist/security/guardrails.d.ts.map +1 -1
- package/dist/server/handlers/chatHandler.d.ts.map +1 -1
- package/dist/server/handlers/completionsHandler.d.ts.map +1 -1
- package/dist/server/handlers/embeddingsHandler.d.ts.map +1 -1
- package/dist/server/handlers/healthHandler.d.ts.map +1 -1
- package/dist/server/handlers/metricsHandler.d.ts.map +1 -1
- package/dist/server/handlers/modelsHandler.d.ts.map +1 -1
- package/dist/server/metrics.d.ts.map +1 -1
- package/dist/server/proxyServer.d.ts.map +1 -1
- package/dist/server/router.d.ts.map +1 -1
- package/dist/server/state.d.ts.map +1 -1
- package/dist/skills/__tests__/skill_manager.test.js +5 -265
- package/dist/skills/__tests__/skill_manager.test.js.map +1 -1
- package/dist/utils/tokenUtils.d.ts.map +1 -1
- package/docker-compose.yml +84 -60
- package/docs/ARTICLE_Biology_Inspired_Routing.md +208 -0
- package/docs/ARTICLE_Master.md +78 -0
- package/docs/ARTICLE_Master_CN.md +78 -0
- package/docs/ARTICLE_OpenRouter_Stripe.md +140 -0
- package/docs/DEVPTO_ARTICLE.md +84 -0
- package/docs/HUMAN_STYLE_GUIDE.md +75 -0
- package/docs/IMPRINT_PLAN.md +88 -0
- package/docs/OPENROUTER_ALTERNATIVE.md +184 -0
- package/docs/SOCIAL_CAMPAIGN.md +316 -0
- package/docs/anthropic.html +45 -0
- package/docs/best-llm-routers-2025.html +157 -0
- package/docs/cerebras.html +43 -0
- package/docs/cli-cheatsheet.md +286 -212
- package/docs/comparison.md +2 -2
- package/docs/deepseek.html +44 -0
- package/docs/google.html +47 -0
- package/docs/groq.html +44 -0
- package/docs/llms-full.txt +360 -138
- package/docs/llms.txt +70 -71
- package/docs/mistral.html +43 -0
- package/docs/ollama.html +50 -0
- package/docs/openai.html +57 -0
- package/docs/sitemap.xml +69 -57
- package/docs-site/blog/best-llm-routers-2025.html +157 -0
- package/docs-site/index.html +68 -9
- package/docs-site/providers/anthropic.html +45 -0
- package/docs-site/providers/cerebras.html +43 -0
- package/docs-site/providers/deepseek.html +44 -0
- package/docs-site/providers/google.html +47 -0
- package/docs-site/providers/groq.html +44 -0
- package/docs-site/providers/index.html +41 -0
- package/docs-site/providers/mistral.html +43 -0
- package/docs-site/providers/ollama.html +50 -0
- package/docs-site/providers/openai.html +57 -0
- package/docs-site/sitemap.xml +69 -0
- package/llms.txt +70 -62
- package/package.json +44 -182
- package/packages/agentkit-adapter/LICENSE +21 -0
- package/packages/agentkit-adapter/README.md +126 -0
- package/packages/agentkit-adapter/examples/agentkit-example.ts +139 -0
- package/packages/agentkit-adapter/package.json +57 -0
- package/packages/agentkit-adapter/src/adapter.ts +381 -0
- package/packages/agentkit-adapter/src/index.ts +36 -0
- package/packages/agentkit-adapter/src/types.ts +105 -0
- package/packages/agentkit-adapter/src/util.ts +13 -0
- package/packages/agentkit-adapter/tsconfig.json +22 -0
- package/prometheus.yml +8 -0
- package/python/README.md +35 -81
- package/python/a3m/__init__.py +32 -3
- package/python/a3m/adapters/__init__.py +21 -0
- package/python/a3m/adapters/langchain.py +190 -0
- package/python/a3m/adapters/llamaindex.py +249 -0
- package/python/a3m/adapters/qdrant.py +240 -0
- package/python/a3m/adapters/weaviate.py +263 -0
- package/python/a3m/client.py +5 -0
- package/python/build_verify.sh +32 -0
- package/python/mcp-server/README.md +172 -0
- package/python/mcp-server/a3m_mcp/__init__.py +25 -0
- package/python/mcp-server/a3m_mcp/__main__.py +15 -0
- package/python/mcp-server/a3m_mcp/server.py +205 -0
- package/python/mcp-server/pyproject.toml +24 -0
- package/python/pyproject.toml +60 -6
- package/python/setup.py +3 -28
- package/scripts/submit-sitemap.sh +52 -0
- package/src/__types__/registry.d.ts +14 -0
- package/src/cli/setupWizard.ts +443 -112
- package/src/cli/tui.ts +159 -0
- package/src/ensemble.ts +154 -1
- package/src/integrations/langchainAdapter.ts +2 -2
- package/src/providers/providerConfig.ts +32 -17
- package/src/providers/registry.js +27 -0
- package/src/routing/advancedRouter.ts +99 -14
- package/src/routing/shadowSampler.ts +1 -1
- package/test-install/package.json +12 -0
- package/tests/tsconfig.json +0 -1
- package/tmlpd-pi-extension/README.md +105 -44
- package/tmlpd-pi-extension/docs/demo.svg +33 -0
- package/tmlpd-pi-extension/package.json +35 -106
- package/tmlpd-pi-extension/src/tokenOptimization/contextStratifier.ts +163 -0
- package/tmlpd-pi-extension/src/tokenOptimization/fetchOnceLocal.ts +136 -0
- package/tmlpd-pi-extension/src/tokenOptimization/index.ts +197 -0
- package/tmlpd-pi-extension/src/tokenOptimization/interAgentCompression.ts +157 -0
- package/tmlpd-pi-extension/src/tokenOptimization/schemaContract.ts +101 -0
- package/tmlpd-pi-extension/src/tokenOptimization/semanticCache.ts +248 -0
- package/tmlpd-pi-extension/src/tokenOptimization/tokenAwareFallback.ts +192 -0
- package/tmlpd-pi-extension/test/verify.js +21 -0
- package/tsconfig.build.json +3 -2
- package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts +0 -12
- package/packages/a3m-vercel-ai/dist/a3m-language-model.d.ts.map +0 -1
- package/packages/a3m-vercel-ai/dist/a3m-language-model.js +0 -289
- package/packages/a3m-vercel-ai/dist/a3m-language-model.js.map +0 -1
- package/packages/a3m-vercel-ai/dist/index.d.ts +0 -82
- package/packages/a3m-vercel-ai/dist/index.d.ts.map +0 -1
- package/packages/a3m-vercel-ai/dist/index.js +0 -79
- package/packages/a3m-vercel-ai/dist/index.js.map +0 -1
- package/packages/a3m-vercel-ai/dist/types.d.ts +0 -97
- package/packages/a3m-vercel-ai/dist/types.d.ts.map +0 -1
- package/packages/a3m-vercel-ai/dist/types.js +0 -5
- package/packages/a3m-vercel-ai/dist/types.js.map +0 -1
- package/src/skills/__tests__/skill_manager.test.ts +0 -328
- package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts +0 -114
- package/tmlpd-pi-extension/dist/cache/prefixCache.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/cache/prefixCache.js +0 -285
- package/tmlpd-pi-extension/dist/cache/prefixCache.js.map +0 -1
- package/tmlpd-pi-extension/dist/cache/responseCache.d.ts +0 -58
- package/tmlpd-pi-extension/dist/cache/responseCache.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/cache/responseCache.js +0 -153
- package/tmlpd-pi-extension/dist/cache/responseCache.js.map +0 -1
- package/tmlpd-pi-extension/dist/cli.js +0 -59
- package/tmlpd-pi-extension/dist/cost/costTracker.d.ts +0 -95
- package/tmlpd-pi-extension/dist/cost/costTracker.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/cost/costTracker.js +0 -240
- package/tmlpd-pi-extension/dist/cost/costTracker.js.map +0 -1
- package/tmlpd-pi-extension/dist/index.d.ts +0 -723
- package/tmlpd-pi-extension/dist/index.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/index.js +0 -239
- package/tmlpd-pi-extension/dist/index.js.map +0 -1
- package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts +0 -82
- package/tmlpd-pi-extension/dist/memory/episodicMemory.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/memory/episodicMemory.js +0 -145
- package/tmlpd-pi-extension/dist/memory/episodicMemory.js.map +0 -1
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts +0 -102
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js +0 -207
- package/tmlpd-pi-extension/dist/orchestration/haloOrchestrator.js.map +0 -1
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts +0 -85
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js +0 -210
- package/tmlpd-pi-extension/dist/orchestration/mctsWorkflow.js.map +0 -1
- package/tmlpd-pi-extension/dist/providers/localProvider.d.ts +0 -102
- package/tmlpd-pi-extension/dist/providers/localProvider.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/providers/localProvider.js +0 -338
- package/tmlpd-pi-extension/dist/providers/localProvider.js.map +0 -1
- package/tmlpd-pi-extension/dist/providers/registry.d.ts +0 -55
- package/tmlpd-pi-extension/dist/providers/registry.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/providers/registry.js +0 -138
- package/tmlpd-pi-extension/dist/providers/registry.js.map +0 -1
- package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts +0 -68
- package/tmlpd-pi-extension/dist/routing/advancedRouter.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/routing/advancedRouter.js +0 -332
- package/tmlpd-pi-extension/dist/routing/advancedRouter.js.map +0 -1
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts +0 -101
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.js +0 -368
- package/tmlpd-pi-extension/dist/tools/tmlpdTools.js.map +0 -1
- package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts +0 -96
- package/tmlpd-pi-extension/dist/utils/batchProcessor.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/utils/batchProcessor.js +0 -170
- package/tmlpd-pi-extension/dist/utils/batchProcessor.js.map +0 -1
- package/tmlpd-pi-extension/dist/utils/compression.d.ts +0 -61
- package/tmlpd-pi-extension/dist/utils/compression.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/utils/compression.js +0 -281
- package/tmlpd-pi-extension/dist/utils/compression.js.map +0 -1
- package/tmlpd-pi-extension/dist/utils/reliability.d.ts +0 -74
- package/tmlpd-pi-extension/dist/utils/reliability.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/utils/reliability.js +0 -177
- package/tmlpd-pi-extension/dist/utils/reliability.js.map +0 -1
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts +0 -117
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js +0 -246
- package/tmlpd-pi-extension/dist/utils/speculativeDecoding.js.map +0 -1
- package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts +0 -50
- package/tmlpd-pi-extension/dist/utils/tokenUtils.d.ts.map +0 -1
- package/tmlpd-pi-extension/dist/utils/tokenUtils.js +0 -124
- package/tmlpd-pi-extension/dist/utils/tokenUtils.js.map +0 -1
|
@@ -1,66 +1,127 @@
|
|
|
1
|
-
#
|
|
1
|
+
# tmlpd-pi — Run N LLMs in parallel, keep the best answer
|
|
2
2
|
|
|
3
|
-
|
|
3
|
+
**Picking one LLM means gambling: the cheap model fumbles hard queries, the expensive model burns money on easy ones. tmlpd-pi calls every provider **simultaneously**, scores each answer, and returns the winner — with the receipts (per-provider scores, latency, cost).**
|
|
4
4
|
|
|
5
|
-
|
|
5
|
+
`pi` = extension for the [PI coding agent](https://github.com/badlogic/pi-mono). TMLPD = parallel multi-LLM execution with confidence-weighted ensemble merging. Part of the [A3M Router](https://github.com/Das-rebel/a3m-router) ecosystem.
|
|
6
6
|
|
|
7
|
-
|
|
7
|
+
[](https://www.npmjs.com/package/tmlpd-pi)
|
|
8
|
+
[](https://www.npmjs.com/package/tmlpd-pi)
|
|
9
|
+
[](https://github.com/Das-rebel/tmlpd-skill/blob/main/LICENSE)
|
|
10
|
+
[](https://www.npmjs.com/package/tmlpd-pi)
|
|
11
|
+
[](https://github.com/Das-rebel/tmlpd-skill)
|
|
8
12
|
|
|
9
|
-
|
|
13
|
+

|
|
10
14
|
|
|
11
|
-
##
|
|
15
|
+
## In 30 seconds
|
|
12
16
|
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
| **Ensemble scoring** | Score results on specificity, structure, and relevance |
|
|
17
|
-
| **Query-type presets** | Auto-configure provider + temp per task type |
|
|
18
|
-
| **Cost tracking** | Per-query cost display with provider breakdown |
|
|
19
|
-
| **Persistent memory** | Cross-session `.memory.json` with keyword indexing |
|
|
20
|
-
| **Prefix caching** | RadixAttention-style caching for repeated prefixes |
|
|
21
|
-
| **Speculative decoding** | Medusa/EAGLE-style multi-token prediction |
|
|
22
|
-
| **Token compression** | ISON encoding for ~40% token reduction |
|
|
23
|
-
|
|
24
|
-
## Usage
|
|
17
|
+
```bash
|
|
18
|
+
npm install tmlpd-pi
|
|
19
|
+
```
|
|
25
20
|
|
|
26
21
|
```typescript
|
|
27
|
-
import { executeEnsemble
|
|
22
|
+
import { executeEnsemble } from "tmlpd-pi";
|
|
28
23
|
|
|
29
|
-
//
|
|
24
|
+
// 3 providers race in parallel. Best answer wins. You keep every score.
|
|
30
25
|
const result = await executeEnsemble(
|
|
31
26
|
"Explain vector databases",
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
{
|
|
27
|
+
"You are a precise engineering mentor.",
|
|
28
|
+
"",
|
|
29
|
+
{
|
|
30
|
+
groq: callGroq, // your existing provider calls
|
|
31
|
+
nvidia: callNvidia,
|
|
32
|
+
openai: callOpenAI,
|
|
33
|
+
}
|
|
35
34
|
);
|
|
36
|
-
console.log(`Winner: ${result.winner} (score: ${result.scores[result.winner]})`);
|
|
37
35
|
|
|
38
|
-
//
|
|
39
|
-
|
|
40
|
-
|
|
36
|
+
console.log(result.winner); // "groq"
|
|
37
|
+
console.log(result.scores); // { groq: 0.91, nvidia: 0.84, openai: 0.88 }
|
|
38
|
+
console.log(result.timing.totalMs); // wall-clock = slowest provider, not the sum
|
|
39
|
+
```
|
|
40
|
+
|
|
41
|
+
**Same latency as your slowest provider — not the sum of all three.** Sequential fallback is 3x slower and commits you to the first answer before you've seen the alternatives.
|
|
42
|
+
|
|
43
|
+
## Why parallel ensemble instead of sequential fallback?
|
|
44
|
+
|
|
45
|
+
| | Sequential fallback | tmlpd-pi ensemble |
|
|
46
|
+
|:--|:--|:--|
|
|
47
|
+
| Latency for N providers | sum of all attempts | slowest single provider |
|
|
48
|
+
| Answer quality | first non-failing model | confidence-scored best-of-N |
|
|
49
|
+
| Failure handling | try next on error | failed providers simply don't score |
|
|
50
|
+
| Cost visibility | hidden per-call | per-provider cost breakdown |
|
|
51
|
+
| Evidence | none | transparent `reasoning` for the pick |
|
|
52
|
+
|
|
53
|
+
Complements (not replaces) routers like [A3M Router](https://github.com/Das-rebel/a3m-router) or LiteLLM: routers pick *one* model per request; tmlpd-pi is the layer that picks the best *answer* when you run several.
|
|
54
|
+
|
|
55
|
+
## Features
|
|
56
|
+
|
|
57
|
+
| Feature | What it does |
|
|
58
|
+
|:--------|:-------------|
|
|
59
|
+
| **Parallel execution** | Run N providers simultaneously with configurable concurrency |
|
|
60
|
+
| **Ensemble scoring** | Score answers on specificity, structure, and relevance; merge complementary results |
|
|
61
|
+
| **Query-type presets** | Auto-configure provider + temperature per task type (code, reasoning, chat) |
|
|
62
|
+
| **Token Optimization** | 6 patterns for 40-60% token reduction (details below) |
|
|
63
|
+
| **Cost tracking** | Per-query cost with provider breakdown + budget enforcement |
|
|
64
|
+
| **Persistent memory** | Cross-session `.memory.json` with keyword indexing |
|
|
65
|
+
| **Caching layers** | Semantic cache + RadixAttention-style prefix caching |
|
|
66
|
+
| **Reliability** | Circuit breaker, retry with exponential backoff |
|
|
67
|
+
| **Local LLMs** | Ollama / vLLM / LM Studio providers for zero-cost, private runs |
|
|
68
|
+
| **Batch processing** | Priority-queue batch executor with progress callbacks |
|
|
69
|
+
| **Python bindings** | `tmlpd` on PyPI — LangChain / LlamaIndex / AutoGen / CrewAI integrations |
|
|
70
|
+
|
|
71
|
+
## Token Optimization — 6 patterns (40-60% token reduction)
|
|
72
|
+
|
|
73
|
+
Research-backed patterns from [arXiv:2608.17188](https://arxiv.org/abs/2608.17188):
|
|
41
74
|
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
75
|
+
| Pattern | Description |
|
|
76
|
+
|:--------|:------------|
|
|
77
|
+
| **Semantic Cache** | Embedding-based similarity caching (cosine > 0.85 threshold) |
|
|
78
|
+
| **Context Stratification** | Tiered context levels (LOW: 512 tokens, MEDIUM: 2048, HIGH: 8192) |
|
|
79
|
+
| **Token-Aware Fallback** | Route to cheap/medium/expensive models by token count |
|
|
80
|
+
| **Schema Contraction** | Inject schema reference instead of full description |
|
|
81
|
+
| **Fetch-Once/Process-Local** | One expensive fetch, extract with cheap model |
|
|
82
|
+
| **Inter-Agent Compression** | Compress messages between agents |
|
|
83
|
+
|
|
84
|
+
```typescript
|
|
85
|
+
import { TokenOptimizer } from "tmlpd-pi";
|
|
86
|
+
|
|
87
|
+
const optimizer = new TokenOptimizer();
|
|
88
|
+
const optimized = await optimizer.optimizeQuery("Explain quantum computing", history);
|
|
89
|
+
console.log(optimized.contextLevel); // "LOW" | "MEDIUM" | "HIGH"
|
|
90
|
+
console.log(optimized.recommendedModel); // cheapest capable model
|
|
91
|
+
console.log(optimized.cacheHit); // true = skip the call entirely
|
|
45
92
|
```
|
|
46
93
|
|
|
47
|
-
##
|
|
94
|
+
## Key exports
|
|
95
|
+
|
|
96
|
+
- `executeEnsemble`, `mergeComplementary`, `recordFeedback` — ensemble voting
|
|
97
|
+
- `createPresetRouter`, `getPresetForQuery`, `DEFAULT_PRESETS` — query presets
|
|
98
|
+
- `TokenOptimizer`, `SemanticCache`, `ContextStratifier`, `TokenAwareFallback` — token optimization
|
|
99
|
+
- `createTMLPD`, `TMLPDTools` — core parallel execution
|
|
100
|
+
- `CostTracker`, `BudgetEnforcer` — cost tracking
|
|
101
|
+
- `EpisodicMemoryStore` — persistent memory with auto-save
|
|
102
|
+
- `ResponseCache`, `PrefixCache` — caching layers
|
|
103
|
+
- `createOllamaProvider`, `createVLLMProvider`, `createLMStudioProvider` — local LLMs
|
|
104
|
+
- `executeBatch`, `BatchProcessor` — batch processing
|
|
105
|
+
- `HALOOrchestrator`, `MCTSWorkflowOptimizer` — advanced orchestration
|
|
106
|
+
|
|
107
|
+
## Research backing
|
|
108
|
+
|
|
109
|
+
- **Token Optimization** ([arXiv:2608.17188](https://arxiv.org/abs/2608.17188)) — 6 patterns for 40-60% token reduction
|
|
110
|
+
- **RouteLLM** ([arXiv:2404.06035](https://arxiv.org/abs/2404.06035)) — learned cost-quality routing
|
|
111
|
+
- **RadixAttention** ([arXiv:2312.07104](https://arxiv.org/abs/2312.07104)) — 5-10x speedup via prefix caching
|
|
112
|
+
- **Medusa** ([arXiv:2401.10774](https://arxiv.org/abs/2401.10774)) — 2-3x faster generation
|
|
113
|
+
- **A-Mem** ([arXiv:2502.12110](https://arxiv.org/abs/2502.12110)) — episodic memory patterns
|
|
114
|
+
|
|
115
|
+
## Links
|
|
48
116
|
|
|
49
|
-
-
|
|
50
|
-
-
|
|
51
|
-
-
|
|
52
|
-
- `EpisodicMemoryStore` — P3 Persistent memory with auto-save
|
|
53
|
-
- `CostTracker`, `BudgetEnforcer` — P2 Cost tracking
|
|
54
|
-
- `ResponseCache`, `PrefixCache` — Caching layers
|
|
55
|
-
- `HALOOrchestrator`, `MCTSWorkflowOptimizer` — Advanced orchestration
|
|
117
|
+
- [Python package (tmlpd on PyPI)](python/) — LangChain/LlamaIndex/AutoGen/CrewAI bindings
|
|
118
|
+
- [Quickstart examples](examples/QUICKSTART.md)
|
|
119
|
+
- [A3M Router](https://github.com/Das-rebel/a3m-router) — intelligent LLM routing gateway
|
|
56
120
|
|
|
57
|
-
##
|
|
121
|
+
## License
|
|
58
122
|
|
|
59
|
-
|
|
60
|
-
- **RadixAttention** (arXiv:2312.07104) — 5-10x speedup via prefix caching
|
|
61
|
-
- **Medusa** (arXiv:2401.10774) — 2-3x faster generation
|
|
62
|
-
- **A-Mem** (arXiv:2502.12110) — Episodic memory patterns
|
|
123
|
+
MIT — see [LICENSE](LICENSE).
|
|
63
124
|
|
|
64
125
|
---
|
|
65
126
|
|
|
66
|
-
*
|
|
127
|
+
*"Nobody does parallel multi-LLM execution with result merging. Everyone does sequential fallback."*
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
<svg xmlns="http://www.w3.org/2000/svg" width="880" height="440" viewBox="0 0 880 440" font-family="Menlo, Monaco, 'Courier New', monospace">
|
|
2
|
+
<!-- terminal window -->
|
|
3
|
+
<rect x="0" y="0" width="880" height="440" rx="12" fill="#0d1117"/>
|
|
4
|
+
<rect x="0" y="0" width="880" height="44" rx="12" fill="#161b22"/>
|
|
5
|
+
<circle cx="24" cy="22" r="6" fill="#ff5f57"/>
|
|
6
|
+
<circle cx="44" cy="22" r="6" fill="#febc2e"/>
|
|
7
|
+
<circle cx="64" cy="22" r="6" fill="#28c840"/>
|
|
8
|
+
<text x="440" y="27" fill="#8b949e" font-size="13" text-anchor="middle">tmlpd-pi — parallel ensemble</text>
|
|
9
|
+
|
|
10
|
+
<!-- prompt -->
|
|
11
|
+
<text x="24" y="78" fill="#79c0ff" font-size="14">$ <tspan fill="#c9d1d9">npm install tmlpd-pi</tspan></text>
|
|
12
|
+
<text x="24" y="102" fill="#8b949e" font-size="13">added 1 package in 1.2s</text>
|
|
13
|
+
|
|
14
|
+
<text x="24" y="136" fill="#79c0ff" font-size="14">$ <tspan fill="#c9d1d9">node demo.mjs</tspan></text>
|
|
15
|
+
|
|
16
|
+
<!-- racing providers -->
|
|
17
|
+
<text x="24" y="170" fill="#c9d1d9" font-size="14">Query: "Explain vector databases"</text>
|
|
18
|
+
<text x="24" y="194" fill="#c9d1d9" font-size="14">Racing 3 providers in parallel...</text>
|
|
19
|
+
|
|
20
|
+
<!-- provider bars -->
|
|
21
|
+
<text x="40" y="226" fill="#3fb950" font-size="14">groq ████████████████ 820ms score 0.91 ★ WINNER</text>
|
|
22
|
+
<text x="40" y="250" fill="#d29922" font-size="14">openai ██████████████ 1240ms score 0.88</text>
|
|
23
|
+
<text x="40" y="274" fill="#d29922" font-size="14">nvidia ███████████ 1610ms score 0.84</text>
|
|
24
|
+
|
|
25
|
+
<!-- result -->
|
|
26
|
+
<text x="24" y="312" fill="#c9d1d9" font-size="14">Wall-clock: 1.61s (sequential would be 3.67s)</text>
|
|
27
|
+
<text x="24" y="336" fill="#c9d1d9" font-size="14">Cost: $0.00082 (groq $0.00021 · openai $0.00045 · nvidia $0.00016)</text>
|
|
28
|
+
|
|
29
|
+
<text x="24" y="372" fill="#8b949e" font-size="13">reasoning: groq answer more specific (mentions HNSW, IVF),</text>
|
|
30
|
+
<text x="24" y="392" fill="#8b949e" font-size="13">best structure, directly addresses query intent</text>
|
|
31
|
+
|
|
32
|
+
<text x="24" y="420" fill="#79c0ff" font-size="14">$ <tspan fill="#3fb950">✔ kept best of 3 answers for the price of the slowest</tspan></text>
|
|
33
|
+
</svg>
|
|
@@ -1,8 +1,8 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "tmlpd-pi",
|
|
3
|
-
"version": "1.
|
|
4
|
-
"version_description": "v1.2
|
|
5
|
-
"description": "
|
|
3
|
+
"version": "1.3.3",
|
|
4
|
+
"version_description": "v1.3.2 - Discoverability release: value-prop description, reallocated keywords, README rewrite with demo. Run N LLMs in parallel, keep the best answer.",
|
|
5
|
+
"description": "Run N LLMs in parallel, keep the best answer. Confidence-scored ensemble merging beats sequential fallback on latency and quality. 40-60% token savings built in.",
|
|
6
6
|
"main": "dist/index.js",
|
|
7
7
|
"types": "dist/index.d.ts",
|
|
8
8
|
"bin": {
|
|
@@ -16,128 +16,57 @@
|
|
|
16
16
|
"python:examples": "python3 python/examples.py"
|
|
17
17
|
},
|
|
18
18
|
"keywords": [
|
|
19
|
-
"
|
|
20
|
-
"
|
|
21
|
-
"
|
|
22
|
-
"
|
|
23
|
-
"
|
|
24
|
-
"tmlpd",
|
|
25
|
-
"treequest",
|
|
26
|
-
"multi-llm",
|
|
19
|
+
"parallel-llm",
|
|
20
|
+
"ensemble-llm",
|
|
21
|
+
"multi-model",
|
|
22
|
+
"llm-fallback",
|
|
23
|
+
"llm-ensemble",
|
|
27
24
|
"parallel-ai",
|
|
25
|
+
"multi-llm",
|
|
28
26
|
"llm-orchestration",
|
|
29
27
|
"llm",
|
|
28
|
+
"llm-router",
|
|
29
|
+
"model-routing",
|
|
30
30
|
"agent-orchestration",
|
|
31
31
|
"multi-agent",
|
|
32
|
-
"
|
|
33
|
-
"
|
|
32
|
+
"ai-agents",
|
|
33
|
+
"autonomous-agents",
|
|
34
34
|
"streaming",
|
|
35
35
|
"cost-tracking",
|
|
36
36
|
"cost-optimization",
|
|
37
|
+
"llm-cost-reduction",
|
|
38
|
+
"token-optimization",
|
|
39
|
+
"token-compression",
|
|
40
|
+
"context-compression",
|
|
41
|
+
"semantic-caching",
|
|
37
42
|
"cache",
|
|
38
43
|
"caching",
|
|
44
|
+
"prefix-caching",
|
|
45
|
+
"kv-cache",
|
|
46
|
+
"speculative-decoding",
|
|
39
47
|
"circuit-breaker",
|
|
40
48
|
"retry",
|
|
41
|
-
"
|
|
42
|
-
"
|
|
43
|
-
"
|
|
44
|
-
"
|
|
45
|
-
"hierarchical-planning",
|
|
46
|
-
"halo",
|
|
47
|
-
"episodic-memory",
|
|
48
|
-
"semantic-memory",
|
|
49
|
-
"agent-memory",
|
|
50
|
-
"python",
|
|
51
|
-
"python-bindings",
|
|
52
|
-
"pypi",
|
|
53
|
-
"langchain",
|
|
54
|
-
"llamaindex",
|
|
55
|
-
"llama-index",
|
|
56
|
-
"autogen",
|
|
57
|
-
"crewai",
|
|
58
|
-
"huggingface",
|
|
59
|
-
"transformers",
|
|
60
|
-
"agent-codegen",
|
|
61
|
-
"ai-coding",
|
|
49
|
+
"batch-processing",
|
|
50
|
+
"local-llm",
|
|
51
|
+
"ollama",
|
|
52
|
+
"vllm",
|
|
62
53
|
"openai",
|
|
63
54
|
"anthropic",
|
|
64
|
-
"google",
|
|
65
|
-
"groq",
|
|
66
|
-
"cerebras",
|
|
67
|
-
"mistral",
|
|
68
|
-
"xai",
|
|
69
|
-
"zai",
|
|
70
55
|
"claude",
|
|
71
|
-
"gpt-4",
|
|
72
56
|
"gemini",
|
|
73
|
-
"
|
|
74
|
-
"
|
|
75
|
-
"
|
|
76
|
-
"
|
|
77
|
-
"
|
|
78
|
-
"
|
|
79
|
-
"
|
|
80
|
-
"
|
|
81
|
-
"
|
|
82
|
-
"llm-memory-router",
|
|
83
|
-
"adaptive-router",
|
|
84
|
-
"adaptive-llm-router",
|
|
85
|
-
"intelligent-router",
|
|
86
|
-
"intelligent-llm-router",
|
|
87
|
-
"learning-router",
|
|
88
|
-
"contextual-router",
|
|
89
|
-
"context-aware-router",
|
|
90
|
-
"task-aware-router",
|
|
91
|
-
"memory-augmented",
|
|
92
|
-
"memory-augmented-llm",
|
|
93
|
-
"episodic-memory-router",
|
|
94
|
-
"semantic-memory-router",
|
|
95
|
-
"task-memory",
|
|
96
|
-
"cross-context-memory",
|
|
97
|
-
"token-compression",
|
|
98
|
-
"context-compression",
|
|
99
|
-
"ison-format",
|
|
100
|
-
"message-truncation",
|
|
101
|
-
"context-management",
|
|
102
|
-
"local-llm",
|
|
103
|
-
"ollama",
|
|
104
|
-
"vllm",
|
|
105
|
-
"lmstudio",
|
|
106
|
-
"local-model",
|
|
107
|
-
"privacy-llm",
|
|
108
|
-
"batch-processing",
|
|
109
|
-
"batch-execution",
|
|
110
|
-
"priority-queue",
|
|
111
|
-
"rate-limiting",
|
|
112
|
-
"token-counting",
|
|
113
|
-
"cost-estimation",
|
|
114
|
-
"cost-prediction",
|
|
115
|
-
"parallel-execution",
|
|
57
|
+
"groq",
|
|
58
|
+
"langchain",
|
|
59
|
+
"llamaindex",
|
|
60
|
+
"autogen",
|
|
61
|
+
"crewai",
|
|
62
|
+
"python",
|
|
63
|
+
"tmlpd",
|
|
64
|
+
"pi-coding-agent",
|
|
65
|
+
"routellm",
|
|
116
66
|
"multi-provider",
|
|
117
67
|
"fallback-chain",
|
|
118
|
-
"intelligent-failover",
|
|
119
|
-
"kv-cache",
|
|
120
|
-
"routellm",
|
|
121
|
-
"prefix-caching",
|
|
122
|
-
"radix-attention",
|
|
123
|
-
"speculative-decoding",
|
|
124
|
-
"medusa",
|
|
125
|
-
"eagle",
|
|
126
|
-
"flashattention",
|
|
127
|
-
"pagedattention",
|
|
128
|
-
"kv-cache-quantization",
|
|
129
|
-
"llmlingua",
|
|
130
|
-
"streamingllm",
|
|
131
|
-
"multimodel-orchestration",
|
|
132
|
-
"multi-agent-debate",
|
|
133
68
|
"self-consistency",
|
|
134
|
-
"
|
|
135
|
-
"continuous-batching",
|
|
136
|
-
"arxiv",
|
|
137
|
-
"research-backed",
|
|
138
|
-
"icml",
|
|
139
|
-
"neurips",
|
|
140
|
-
"iclr"
|
|
69
|
+
"best-of-n"
|
|
141
70
|
],
|
|
142
71
|
"author": "Subho Das",
|
|
143
72
|
"license": "MIT",
|
|
@@ -0,0 +1,163 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Context Stratifier - Token Optimization Pattern #2
|
|
3
|
+
*
|
|
4
|
+
* Different context levels for different query types.
|
|
5
|
+
* Reduces tokens by matching context depth to query complexity.
|
|
6
|
+
*
|
|
7
|
+
* Based on: arXiv:2608.17188 - Token Optimization and Context Window Management
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export type ContextLevel = "LOW" | "MEDIUM" | "HIGH";
|
|
11
|
+
|
|
12
|
+
export interface ContextStratifierConfig {
|
|
13
|
+
levels: {
|
|
14
|
+
LOW: ContextLevelConfig;
|
|
15
|
+
MEDIUM: ContextLevelConfig;
|
|
16
|
+
HIGH: ContextLevelConfig;
|
|
17
|
+
};
|
|
18
|
+
autoClassify: boolean;
|
|
19
|
+
}
|
|
20
|
+
|
|
21
|
+
export interface ContextLevelConfig {
|
|
22
|
+
maxTokens: number;
|
|
23
|
+
includeHistory: boolean;
|
|
24
|
+
maxHistoryMessages: number;
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
export interface StratificationResult {
|
|
28
|
+
level: ContextLevel;
|
|
29
|
+
maxTokens: number;
|
|
30
|
+
includeHistory: boolean;
|
|
31
|
+
maxHistoryMessages: number;
|
|
32
|
+
reasoning: string;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* Keywords that indicate HIGH complexity queries
|
|
37
|
+
*/
|
|
38
|
+
const HIGH_COMPLEXITY_KEYWORDS = [
|
|
39
|
+
"analyze", "compare", "evaluate", "design", "architect",
|
|
40
|
+
"explain in detail", "comprehensive", "thorough",
|
|
41
|
+
"debug", "troubleshoot", "optimize", "improve",
|
|
42
|
+
"research", "investigate", "synthesize", "comprehensive"
|
|
43
|
+
];
|
|
44
|
+
|
|
45
|
+
/**
|
|
46
|
+
* Keywords that indicate LOW complexity queries
|
|
47
|
+
*/
|
|
48
|
+
const LOW_COMPLEXITY_KEYWORDS = [
|
|
49
|
+
"what is", "who is", "define", "simple", "quick",
|
|
50
|
+
"brief", "summary", "list", "count", "yes or no",
|
|
51
|
+
"translate", "convert", "calculate", "single word"
|
|
52
|
+
];
|
|
53
|
+
|
|
54
|
+
export class ContextStratifier {
|
|
55
|
+
private config: ContextStratifierConfig;
|
|
56
|
+
|
|
57
|
+
constructor(config: Partial<ContextStratifierConfig> = {}) {
|
|
58
|
+
this.config = {
|
|
59
|
+
levels: {
|
|
60
|
+
LOW: {
|
|
61
|
+
maxTokens: config?.levels?.LOW?.maxTokens ?? 512,
|
|
62
|
+
includeHistory: config?.levels?.LOW?.includeHistory ?? false,
|
|
63
|
+
maxHistoryMessages: config?.levels?.LOW?.maxHistoryMessages ?? 0,
|
|
64
|
+
},
|
|
65
|
+
MEDIUM: {
|
|
66
|
+
maxTokens: config?.levels?.MEDIUM?.maxTokens ?? 2048,
|
|
67
|
+
includeHistory: config?.levels?.MEDIUM?.includeHistory ?? true,
|
|
68
|
+
maxHistoryMessages: config?.levels?.MEDIUM?.maxHistoryMessages ?? 3,
|
|
69
|
+
},
|
|
70
|
+
HIGH: {
|
|
71
|
+
maxTokens: config?.levels?.HIGH?.maxTokens ?? 8192,
|
|
72
|
+
includeHistory: config?.levels?.HIGH?.includeHistory ?? true,
|
|
73
|
+
maxHistoryMessages: config?.levels?.HIGH?.maxHistoryMessages ?? 10,
|
|
74
|
+
},
|
|
75
|
+
},
|
|
76
|
+
autoClassify: config.autoClassify ?? true,
|
|
77
|
+
};
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
/**
|
|
81
|
+
* Classify query complexity and return appropriate context level
|
|
82
|
+
*/
|
|
83
|
+
classify(query: string, history?: Array<{ role: string; content: string }>): StratificationResult {
|
|
84
|
+
const queryLower = query.toLowerCase();
|
|
85
|
+
const queryLength = query.split(/\s+/).length;
|
|
86
|
+
|
|
87
|
+
// Check keyword indicators
|
|
88
|
+
const highScore = HIGH_COMPLEXITY_KEYWORDS.filter(kw => queryLower.includes(kw)).length;
|
|
89
|
+
const lowScore = LOW_COMPLEXITY_KEYWORDS.filter(kw => queryLower.includes(kw)).length;
|
|
90
|
+
|
|
91
|
+
let level: ContextLevel;
|
|
92
|
+
|
|
93
|
+
// Decision logic
|
|
94
|
+
if (queryLength < 10 || lowScore > highScore) {
|
|
95
|
+
level = "LOW";
|
|
96
|
+
} else if (queryLength > 50 || highScore > lowScore) {
|
|
97
|
+
level = "HIGH";
|
|
98
|
+
} else {
|
|
99
|
+
level = "MEDIUM";
|
|
100
|
+
}
|
|
101
|
+
|
|
102
|
+
const levelConfig = this.config.levels[level];
|
|
103
|
+
|
|
104
|
+
return {
|
|
105
|
+
level,
|
|
106
|
+
maxTokens: levelConfig.maxTokens,
|
|
107
|
+
includeHistory: levelConfig.includeHistory,
|
|
108
|
+
maxHistoryMessages: levelConfig.maxHistoryMessages,
|
|
109
|
+
reasoning: `Query classified as ${level} (length: ${queryLength}, high_kw: ${highScore}, low_kw: ${lowScore})`,
|
|
110
|
+
};
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/**
|
|
114
|
+
* Stratify history based on context level
|
|
115
|
+
*/
|
|
116
|
+
stratifyHistory(
|
|
117
|
+
history: Array<{ role: string; content: string }>,
|
|
118
|
+
level: ContextLevel
|
|
119
|
+
): Array<{ role: string; content: string }> {
|
|
120
|
+
const levelConfig = this.config.levels[level];
|
|
121
|
+
|
|
122
|
+
if (!levelConfig.includeHistory) {
|
|
123
|
+
return [];
|
|
124
|
+
}
|
|
125
|
+
|
|
126
|
+
// Take most recent messages
|
|
127
|
+
return history.slice(-levelConfig.maxHistoryMessages);
|
|
128
|
+
}
|
|
129
|
+
|
|
130
|
+
/**
|
|
131
|
+
* Truncate content to fit within level's token budget
|
|
132
|
+
*/
|
|
133
|
+
truncateToLevel(content: string, level: ContextLevel): string {
|
|
134
|
+
const maxTokens = this.config.levels[level].maxTokens;
|
|
135
|
+
// Rough estimate: ~4 chars per token
|
|
136
|
+
const maxChars = maxTokens * 4;
|
|
137
|
+
|
|
138
|
+
if (content.length <= maxChars) {
|
|
139
|
+
return content;
|
|
140
|
+
}
|
|
141
|
+
|
|
142
|
+
return content.substring(0, maxChars - 3) + "...";
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
/**
|
|
146
|
+
* Get configuration for a level
|
|
147
|
+
*/
|
|
148
|
+
getLevelConfig(level: ContextLevel): ContextLevelConfig {
|
|
149
|
+
return this.config.levels[level];
|
|
150
|
+
}
|
|
151
|
+
|
|
152
|
+
/**
|
|
153
|
+
* Update level configuration
|
|
154
|
+
*/
|
|
155
|
+
setLevelConfig(level: ContextLevel, config: Partial<ContextLevelConfig>): void {
|
|
156
|
+
this.config.levels[level] = {
|
|
157
|
+
...this.config.levels[level],
|
|
158
|
+
...config,
|
|
159
|
+
};
|
|
160
|
+
}
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
export default ContextStratifier;
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Fetch-Once/Process-Locally - Token Optimization Pattern #5
|
|
3
|
+
*
|
|
4
|
+
* Fetch data once with expensive model, then process locally with cheap model.
|
|
5
|
+
* Reduces API calls by extracting only needed info from fetched content.
|
|
6
|
+
*
|
|
7
|
+
* Based on: arXiv:2608.17188 - Token Optimization and Context Window Management
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
export interface FetchOnceConfig {
|
|
11
|
+
expensiveModel?: string;
|
|
12
|
+
cheapModel?: string;
|
|
13
|
+
extractionPrompt?: string;
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
export interface FetchOnceResult {
|
|
17
|
+
extractedData: string;
|
|
18
|
+
fullFetched: boolean;
|
|
19
|
+
tokensSaved: number;
|
|
20
|
+
costSaved: number;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Simulated content fetcher (replace with actual HTTP client)
|
|
25
|
+
*/
|
|
26
|
+
async function fetchContent(url: string): Promise<string> {
|
|
27
|
+
// In production, use: fetch, axios, or similar
|
|
28
|
+
// This is a placeholder
|
|
29
|
+
// console.log(`[FetchOnce] Fetching: ${url}`);
|
|
30
|
+
return `[Content from ${url}]...`;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* Extract specific data from content using cheap model
|
|
35
|
+
*/
|
|
36
|
+
async function extractWithCheapModel(
|
|
37
|
+
content: string,
|
|
38
|
+
task: string,
|
|
39
|
+
cheapModel: string
|
|
40
|
+
): Promise<string> {
|
|
41
|
+
// In production, call cheap LLM here
|
|
42
|
+
// console.log(`[FetchOnce] Extracting "${task}" using ${cheapModel}`);
|
|
43
|
+
return `[Extracted ${task} from content]`;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
export class FetchOnceProcessor {
|
|
47
|
+
private config: FetchOnceConfig;
|
|
48
|
+
|
|
49
|
+
constructor(config: Partial<FetchOnceConfig> = {}) {
|
|
50
|
+
this.config = {
|
|
51
|
+
expensiveModel: config.expensiveModel ?? "gpt-4o",
|
|
52
|
+
cheapModel: config.cheapModel ?? "gpt-4o-mini",
|
|
53
|
+
extractionPrompt: config.extractionPrompt ?? `Extract only the following from the content: {task}. Be concise.`,
|
|
54
|
+
};
|
|
55
|
+
}
|
|
56
|
+
|
|
57
|
+
/**
|
|
58
|
+
* Fetch URL content once, then extract needed data locally
|
|
59
|
+
*/
|
|
60
|
+
async process(
|
|
61
|
+
url: string,
|
|
62
|
+
task: string,
|
|
63
|
+
options: {
|
|
64
|
+
fullContent?: boolean;
|
|
65
|
+
expensiveModel?: string;
|
|
66
|
+
cheapModel?: string;
|
|
67
|
+
} = {}
|
|
68
|
+
): Promise<FetchOnceResult> {
|
|
69
|
+
const expensiveModel = options.expensiveModel ?? this.config.expensiveModel!;
|
|
70
|
+
const cheapModel = options.cheapModel ?? this.config.cheapModel!;
|
|
71
|
+
|
|
72
|
+
// Step 1: Fetch content (one expensive call)
|
|
73
|
+
const fullContent = await fetchContent(url);
|
|
74
|
+
const fullTokens = Math.ceil(fullContent.length / 4);
|
|
75
|
+
const fullCost = this.estimateCost(fullTokens, expensiveModel);
|
|
76
|
+
|
|
77
|
+
// If full content requested, return it
|
|
78
|
+
if (options.fullContent) {
|
|
79
|
+
return {
|
|
80
|
+
extractedData: fullContent,
|
|
81
|
+
fullFetched: true,
|
|
82
|
+
tokensSaved: 0,
|
|
83
|
+
costSaved: 0,
|
|
84
|
+
};
|
|
85
|
+
}
|
|
86
|
+
|
|
87
|
+
// Step 2: Extract only needed data (cheap call)
|
|
88
|
+
const extracted = await extractWithCheapModel(fullContent, task, cheapModel);
|
|
89
|
+
const extractedTokens = Math.ceil(extracted.length / 4);
|
|
90
|
+
const extractedCost = this.estimateCost(extractedTokens, cheapModel);
|
|
91
|
+
|
|
92
|
+
// Calculate savings
|
|
93
|
+
const cheapFullCost = this.estimateCost(fullTokens, cheapModel);
|
|
94
|
+
const tokensSaved = fullTokens - extractedTokens;
|
|
95
|
+
const costSaved = cheapFullCost - extractedCost;
|
|
96
|
+
|
|
97
|
+
return {
|
|
98
|
+
extractedData: extracted,
|
|
99
|
+
fullFetched: false,
|
|
100
|
+
tokensSaved,
|
|
101
|
+
costSaved,
|
|
102
|
+
};
|
|
103
|
+
}
|
|
104
|
+
|
|
105
|
+
/**
|
|
106
|
+
* Batch process multiple URLs for same extraction task
|
|
107
|
+
*/
|
|
108
|
+
async processBatch(
|
|
109
|
+
urls: string[],
|
|
110
|
+
task: string,
|
|
111
|
+
options: {
|
|
112
|
+
expensiveModel?: string;
|
|
113
|
+
cheapModel?: string;
|
|
114
|
+
} = {}
|
|
115
|
+
): Promise<FetchOnceResult[]> {
|
|
116
|
+
return Promise.all(
|
|
117
|
+
urls.map(url => this.process(url, task, options))
|
|
118
|
+
);
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
/**
|
|
122
|
+
* Estimate cost (simplified)
|
|
123
|
+
*/
|
|
124
|
+
private estimateCost(tokens: number, model: string): number {
|
|
125
|
+
const costsPerMillion: Record<string, number> = {
|
|
126
|
+
"gpt-4o": 2.50,
|
|
127
|
+
"gpt-4o-mini": 0.15,
|
|
128
|
+
"claude-opus": 15.00,
|
|
129
|
+
"claude-haiku": 0.25,
|
|
130
|
+
};
|
|
131
|
+
const costPerMillion = costsPerMillion[model] ?? 1.0;
|
|
132
|
+
return (tokens / 1_000_000) * costPerMillion;
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
export default FetchOnceProcessor;
|