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,160 @@
|
|
|
1
|
+
#!/usr/bin/env node
|
|
2
|
+
/**
|
|
3
|
+
* ensemble.js — Query multiple providers in parallel and merge results.
|
|
4
|
+
*
|
|
5
|
+
* A3M's signature capability: parallel multi-LLM execution with voting.
|
|
6
|
+
* Unlike every other router (which does sequential fallback A -> B -> C),
|
|
7
|
+
* A3M queries all selected providers simultaneously and compares responses.
|
|
8
|
+
*
|
|
9
|
+
* Usage:
|
|
10
|
+
* node examples/ensemble.js
|
|
11
|
+
* QUERY="What is the capital of France?" PROVIDERS="openai,groq,anthropic" node examples/ensemble.js
|
|
12
|
+
*/
|
|
13
|
+
|
|
14
|
+
const { routeQuery, getAvailableProviders } = require('../dist/index.js');
|
|
15
|
+
|
|
16
|
+
const query = process.env.QUERY || 'Explain the concept of recursion with a real-world analogy';
|
|
17
|
+
const providerList = (process.env.PROVIDERS || 'openai,groq,gemini')
|
|
18
|
+
.split(',')
|
|
19
|
+
.map(s => s.trim());
|
|
20
|
+
|
|
21
|
+
/**
|
|
22
|
+
* Simulate calling each provider. In production, replace with real API calls.
|
|
23
|
+
* A3M Router selects the model — you call its API endpoint.
|
|
24
|
+
*/
|
|
25
|
+
async function callProvider(provider, model, prompt) {
|
|
26
|
+
const apiKey = process.env[provider.toUpperCase() + '_API_KEY'];
|
|
27
|
+
|
|
28
|
+
if (!apiKey) {
|
|
29
|
+
return {
|
|
30
|
+
provider,
|
|
31
|
+
model,
|
|
32
|
+
text: `[SKIP — set ${provider.toUpperCase()}_API_KEY]`,
|
|
33
|
+
cost: 0,
|
|
34
|
+
latency: 0,
|
|
35
|
+
skipped: true,
|
|
36
|
+
};
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
const start = Date.now();
|
|
40
|
+
|
|
41
|
+
// Each provider has a different base URL and format
|
|
42
|
+
const endpoints = {
|
|
43
|
+
openai: { url: 'https://api.openai.com/v1/chat/completions', model: model || 'gpt-4o-mini' },
|
|
44
|
+
groq: { url: 'https://api.groq.com/openai/v1/chat/completions', model: model || 'llama-3.3-70b-versatile' },
|
|
45
|
+
gemini: { url: 'https://generativelanguage.googleapis.com/v1beta/openai/chat/completions', model: model || 'gemini-2.0-flash' },
|
|
46
|
+
anthropic: { url: 'https://api.anthropic.com/v1/messages', model: model || 'claude-3-5-haiku-latest' },
|
|
47
|
+
};
|
|
48
|
+
|
|
49
|
+
const ep = endpoints[provider];
|
|
50
|
+
if (!ep) {
|
|
51
|
+
return { provider, model, text: `[UNSUPPORTED PROVIDER: ${provider}]`, cost: 0, latency: 0, skipped: true };
|
|
52
|
+
}
|
|
53
|
+
|
|
54
|
+
const headers = { 'Content-Type': 'application/json' };
|
|
55
|
+
if (provider === 'anthropic') {
|
|
56
|
+
headers['x-api-key'] = apiKey;
|
|
57
|
+
headers['anthropic-version'] = '2023-06-01';
|
|
58
|
+
} else {
|
|
59
|
+
headers['Authorization'] = `Bearer ${apiKey}`;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
const body = provider === 'anthropic'
|
|
63
|
+
? { model: ep.model, max_tokens: 512, messages: [{ role: 'user', content: prompt }] }
|
|
64
|
+
: { model: ep.model, messages: [{ role: 'user', content: prompt }], max_tokens: 512 };
|
|
65
|
+
|
|
66
|
+
try {
|
|
67
|
+
const res = await fetch(ep.url, {
|
|
68
|
+
method: 'POST',
|
|
69
|
+
headers,
|
|
70
|
+
body: JSON.stringify(body),
|
|
71
|
+
});
|
|
72
|
+
|
|
73
|
+
if (!res.ok) {
|
|
74
|
+
const errText = await res.text().catch(() => '');
|
|
75
|
+
return { provider, model: ep.model, text: `[ERROR ${res.status}: ${errText}]`, cost: 0, latency: Date.now() - start, skipped: true };
|
|
76
|
+
}
|
|
77
|
+
|
|
78
|
+
const data = await res.json();
|
|
79
|
+
const text = provider === 'anthropic'
|
|
80
|
+
? data.content?.[0]?.text || JSON.stringify(data)
|
|
81
|
+
: data.choices?.[0]?.message?.content || JSON.stringify(data);
|
|
82
|
+
|
|
83
|
+
return {
|
|
84
|
+
provider,
|
|
85
|
+
model: ep.model,
|
|
86
|
+
text: text.trim(),
|
|
87
|
+
latency: Date.now() - start,
|
|
88
|
+
skipped: false,
|
|
89
|
+
usage: data.usage,
|
|
90
|
+
};
|
|
91
|
+
} catch (err) {
|
|
92
|
+
return { provider, model: ep.model, text: `[FETCH ERROR: ${err.message}]`, cost: 0, latency: Date.now() - start, skipped: true };
|
|
93
|
+
}
|
|
94
|
+
}
|
|
95
|
+
|
|
96
|
+
/**
|
|
97
|
+
* Simple text voting: find common key phrases across responses.
|
|
98
|
+
*/
|
|
99
|
+
function voteOnResults(results) {
|
|
100
|
+
const answered = results.filter(r => !r.skipped);
|
|
101
|
+
if (answered.length === 0) return { winner: null, consensus: false, details: 'No providers returned results' };
|
|
102
|
+
|
|
103
|
+
const texts = answered.map(r => r.text.toLowerCase());
|
|
104
|
+
const wordSets = texts.map(t => new Set(t.split(/\s+/).filter(w => w.length > 3)));
|
|
105
|
+
const intersection = wordSets.reduce((a, b) => new Set([...a].filter(x => b.has(x))));
|
|
106
|
+
|
|
107
|
+
const consensusScore = intersection.size > 5 ? 0.85 : 0.3;
|
|
108
|
+
return {
|
|
109
|
+
winner: answered[0],
|
|
110
|
+
consensus: consensusScore > 0.5,
|
|
111
|
+
consensusScore,
|
|
112
|
+
sharedTerms: [...intersection].slice(0, 10),
|
|
113
|
+
totalAnswered: answered.length,
|
|
114
|
+
totalSkipped: results.length - answered.length,
|
|
115
|
+
};
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
async function main() {
|
|
119
|
+
console.log('A3M Router — Parallel Ensemble');
|
|
120
|
+
console.log('=' .repeat(50));
|
|
121
|
+
console.log('Query:', query);
|
|
122
|
+
console.log('Providers:', providerList.join(', '));
|
|
123
|
+
console.log('');
|
|
124
|
+
|
|
125
|
+
// Get route decisions from A3M for insight
|
|
126
|
+
console.log('-- A3M Route Recommendations --');
|
|
127
|
+
for (const provider of providerList) {
|
|
128
|
+
const decision = routeQuery(query);
|
|
129
|
+
console.log(` ${provider}: ${decision.primary_model} (conf: ${(decision.confidence * 100).toFixed(0)}%, $${decision.estimated_cost.toFixed(6)})`);
|
|
130
|
+
}
|
|
131
|
+
console.log('');
|
|
132
|
+
|
|
133
|
+
// Execute all providers in parallel
|
|
134
|
+
console.log('-- Parallel Execution --');
|
|
135
|
+
const promises = providerList.map(provider => callProvider(provider, null, query));
|
|
136
|
+
const results = await Promise.all(promises);
|
|
137
|
+
|
|
138
|
+
for (const r of results) {
|
|
139
|
+
const icon = r.skipped ? ' [SKIP]' : ' [OK] ';
|
|
140
|
+
console.log(`${icon} ${r.provider.padEnd(12)} ${r.model}`);
|
|
141
|
+
console.log(` latency: ${r.latency}ms`);
|
|
142
|
+
if (!r.skipped) {
|
|
143
|
+
const preview = r.text.slice(0, 120).replace(/\n/g, ' ');
|
|
144
|
+
console.log(` response: ${preview}...`);
|
|
145
|
+
}
|
|
146
|
+
console.log('');
|
|
147
|
+
}
|
|
148
|
+
|
|
149
|
+
// Vote on results
|
|
150
|
+
const vote = voteOnResults(results);
|
|
151
|
+
console.log('-- Consensus Vote --');
|
|
152
|
+
console.log(' Consensus:', vote.consensus ? 'YES' : 'NO');
|
|
153
|
+
console.log(' Score:', vote.consensusScore.toFixed(2));
|
|
154
|
+
console.log(' Answered:', vote.totalAnswered, '/', vote.totalSkipped + vote.totalAnswered);
|
|
155
|
+
if (vote.sharedTerms?.length) {
|
|
156
|
+
console.log(' Shared key terms:', vote.sharedTerms.join(', '));
|
|
157
|
+
}
|
|
158
|
+
}
|
|
159
|
+
|
|
160
|
+
main().catch(console.error);
|
|
@@ -0,0 +1,216 @@
|
|
|
1
|
+
# A3M Router — LangChain Integration
|
|
2
|
+
|
|
3
|
+
Use **A3M Router** as a drop-in LLM provider inside LangChain chains and agents. Route every query to the cheapest capable provider with automatic fallback and optional parallel ensemble execution.
|
|
4
|
+
|
|
5
|
+
> **This is a community integration. PRs welcome!**
|
|
6
|
+
|
|
7
|
+
## Installation
|
|
8
|
+
|
|
9
|
+
```bash
|
|
10
|
+
npm install @langchain/core
|
|
11
|
+
```
|
|
12
|
+
|
|
13
|
+
The integration itself is a single TypeScript file. Copy it into your project:
|
|
14
|
+
|
|
15
|
+
```bash
|
|
16
|
+
cp integrations/langchain/a3m_langchain.ts ./src/
|
|
17
|
+
```
|
|
18
|
+
|
|
19
|
+
Or import directly from the package (when published):
|
|
20
|
+
|
|
21
|
+
```bash
|
|
22
|
+
npm install adaptive-memory-multi-model-router
|
|
23
|
+
```
|
|
24
|
+
|
|
25
|
+
```typescript
|
|
26
|
+
import { A3MLLM } from 'adaptive-memory-multi-model-router/integrations/langchain';
|
|
27
|
+
```
|
|
28
|
+
|
|
29
|
+
## Quick Start
|
|
30
|
+
|
|
31
|
+
```typescript
|
|
32
|
+
import { A3MLLM } from './a3m_langchain';
|
|
33
|
+
|
|
34
|
+
const llm = new A3MLLM({
|
|
35
|
+
providers: {
|
|
36
|
+
groq: {
|
|
37
|
+
name: 'Groq',
|
|
38
|
+
baseUrl: 'https://api.groq.com/openai/v1/chat/completions',
|
|
39
|
+
apiKey: process.env.GROQ_API_KEY,
|
|
40
|
+
models: ['llama-3.3-70b-versatile'],
|
|
41
|
+
tier: 'cheap',
|
|
42
|
+
},
|
|
43
|
+
openai: {
|
|
44
|
+
name: 'OpenAI',
|
|
45
|
+
baseUrl: 'https://api.openai.com/v1/chat/completions',
|
|
46
|
+
apiKey: process.env.OPENAI_API_KEY,
|
|
47
|
+
models: ['gpt-4o-mini'],
|
|
48
|
+
tier: 'premium',
|
|
49
|
+
},
|
|
50
|
+
},
|
|
51
|
+
routingStrategy: 'cheapest', // auto-pick cheapest
|
|
52
|
+
fallbackEnabled: true, // fall back on failure
|
|
53
|
+
});
|
|
54
|
+
|
|
55
|
+
const response = await llm.invoke('What is the capital of France?');
|
|
56
|
+
console.log(response);
|
|
57
|
+
```
|
|
58
|
+
|
|
59
|
+
## Configuration Options
|
|
60
|
+
|
|
61
|
+
| Option | Type | Default | Description |
|
|
62
|
+
|--------|------|---------|-------------|
|
|
63
|
+
| `providers` | `Record<string, A3MProviderConfig>` | (required) | Provider configurations |
|
|
64
|
+
| `routingStrategy` | `'cheapest' \| 'fastest' \| 'priority' \| 'random'` | `'cheapest'` | Provider selection strategy |
|
|
65
|
+
| `defaultModel` | `string` | `''` | Fallback model name |
|
|
66
|
+
| `temperature` | `number` | `0.7` | LLM temperature |
|
|
67
|
+
| `maxTokens` | `number` | `4096` | Max output tokens |
|
|
68
|
+
| `timeout` | `number` | `60000` | Request timeout (ms) |
|
|
69
|
+
| `fallbackEnabled` | `boolean` | `true` | Auto-fallback on failure |
|
|
70
|
+
| `priorityOrder` | `string[]` | `[]` | Provider priority order |
|
|
71
|
+
| `onRoute` | `function` | — | Route decision callback |
|
|
72
|
+
| `onError` | `function` | — | Error callback |
|
|
73
|
+
|
|
74
|
+
## Provider Config Format
|
|
75
|
+
|
|
76
|
+
```typescript
|
|
77
|
+
interface A3MProviderConfig {
|
|
78
|
+
name: string; // Human-readable name
|
|
79
|
+
baseUrl: string; // API endpoint URL
|
|
80
|
+
apiKey?: string; // API key
|
|
81
|
+
models: string[]; // Available models
|
|
82
|
+
tier: 'free' | 'cheap' | 'mid' | 'premium' | 'enterprise';
|
|
83
|
+
cost?: { input: number; output: number }; // $ per 1M tokens
|
|
84
|
+
format?: 'openai' | 'anthropic' | 'google' | 'cohere';
|
|
85
|
+
headers?: Record<string, string>; // Extra HTTP headers
|
|
86
|
+
maxTokens?: number;
|
|
87
|
+
}
|
|
88
|
+
```
|
|
89
|
+
|
|
90
|
+
## Routing Strategies
|
|
91
|
+
|
|
92
|
+
- **`cheapest`** — Picks the provider with the lowest combined input+output cost per 1M tokens.
|
|
93
|
+
- **`fastest`** — Picks providers in registration order (register fast ones first).
|
|
94
|
+
- **`priority`** — Uses explicit `priorityOrder` array.
|
|
95
|
+
- **`random`** — Random provider selection (load balancing).
|
|
96
|
+
|
|
97
|
+
## Routing Metadata
|
|
98
|
+
|
|
99
|
+
Every `invokeWithMetadata()` call returns A3M routing details:
|
|
100
|
+
|
|
101
|
+
```typescript
|
|
102
|
+
{
|
|
103
|
+
provider: 'groq',
|
|
104
|
+
model: 'llama-3.3-70b-versatile',
|
|
105
|
+
latencyMs: 342,
|
|
106
|
+
costUsd: 0.00012,
|
|
107
|
+
tier: 'cheap',
|
|
108
|
+
tokensUsed: { input: 45, output: 120, total: 165 },
|
|
109
|
+
ensemble: false
|
|
110
|
+
}
|
|
111
|
+
```
|
|
112
|
+
|
|
113
|
+
## Ensemble Mode
|
|
114
|
+
|
|
115
|
+
Run multiple providers in parallel and merge results:
|
|
116
|
+
|
|
117
|
+
```typescript
|
|
118
|
+
const result = await llm.ensembleInvoke('Explain quantum computing', {
|
|
119
|
+
ensemble: 'longest', // 'first' | 'longest' | 'concat'
|
|
120
|
+
});
|
|
121
|
+
|
|
122
|
+
console.log(result.text);
|
|
123
|
+
console.log('Used providers:', result.metadata.ensembleProviders);
|
|
124
|
+
```
|
|
125
|
+
|
|
126
|
+
**Ensemble strategies:**
|
|
127
|
+
|
|
128
|
+
| Strategy | Behavior |
|
|
129
|
+
|----------|----------|
|
|
130
|
+
| `first` | Return first response received (lowest latency) |
|
|
131
|
+
| `longest` | Return the most verbose response |
|
|
132
|
+
| `concat` | Concatenate all responses with provider headers |
|
|
133
|
+
|
|
134
|
+
## LangChain Chain Integration
|
|
135
|
+
|
|
136
|
+
```typescript
|
|
137
|
+
import { PromptTemplate } from '@langchain/core/prompts';
|
|
138
|
+
import { StringOutputParser } from '@langchain/core/output_parsers';
|
|
139
|
+
|
|
140
|
+
const prompt = PromptTemplate.fromTemplate(
|
|
141
|
+
'Answer as a {role}: {question}',
|
|
142
|
+
);
|
|
143
|
+
|
|
144
|
+
const chain = prompt
|
|
145
|
+
.pipe(llm as any) // A3MLLM works as a runnable
|
|
146
|
+
.pipe(new StringOutputParser());
|
|
147
|
+
|
|
148
|
+
const response = await chain.invoke({
|
|
149
|
+
role: 'physicist',
|
|
150
|
+
question: 'Why is the sky blue?',
|
|
151
|
+
});
|
|
152
|
+
```
|
|
153
|
+
|
|
154
|
+
## Factory Functions
|
|
155
|
+
|
|
156
|
+
```typescript
|
|
157
|
+
// Single provider
|
|
158
|
+
const groq = createA3MProvider('groq', {
|
|
159
|
+
apiKey: process.env.GROQ_API_KEY,
|
|
160
|
+
});
|
|
161
|
+
|
|
162
|
+
// Auto-router across providers
|
|
163
|
+
const router = createA3MRouter({
|
|
164
|
+
groq: { apiKey: process.env.GROQ_API_KEY },
|
|
165
|
+
openai: { apiKey: process.env.OPENAI_API_KEY },
|
|
166
|
+
nvidia: { apiKey: process.env.NVIDIA_API_KEY },
|
|
167
|
+
});
|
|
168
|
+
|
|
169
|
+
const { text, metadata } = await router.invokeWithMetadata('Hello!');
|
|
170
|
+
console.log('Routed to:', metadata.provider, '| Cost: $' + metadata.costUsd);
|
|
171
|
+
```
|
|
172
|
+
|
|
173
|
+
## Cost Comparison
|
|
174
|
+
|
|
175
|
+
| Scenario | Cost per 1K queries | vs All-Premium |
|
|
176
|
+
|----------|-------------------|----------------|
|
|
177
|
+
| **A3M Router** (cheapest routing) | **~$0.30** | **~82% less** |
|
|
178
|
+
| All-GPT-4o | $2.50 | — |
|
|
179
|
+
| All-Claude-3 | $3.00 | — |
|
|
180
|
+
| All-Mistral-Large | $0.60 | — |
|
|
181
|
+
|
|
182
|
+
Routing strategy routes simple queries (summaries, facts) to free/cheap providers and only uses premium providers for complex reasoning. Result: typical cost savings of **60-82%** with negligible quality difference on most queries.
|
|
183
|
+
|
|
184
|
+
## Built-in Default Providers
|
|
185
|
+
|
|
186
|
+
The integration includes 12 built-in provider configs — just add your API keys:
|
|
187
|
+
|
|
188
|
+
`groq`, `openai`, `anthropic`, `deepseek`, `google`, `cerebras`, `nvidia`, `deepinfra`, `together`, `mistral`
|
|
189
|
+
|
|
190
|
+
```typescript
|
|
191
|
+
import { A3MLLM, A3M_DEFAULT_PROVIDERS } from './a3m_langchain';
|
|
192
|
+
|
|
193
|
+
const llm = new A3MLLM({
|
|
194
|
+
providers: {
|
|
195
|
+
groq: { ...A3M_DEFAULT_PROVIDERS.groq, apiKey: process.env.GROQ_API_KEY },
|
|
196
|
+
},
|
|
197
|
+
});
|
|
198
|
+
```
|
|
199
|
+
|
|
200
|
+
## Files
|
|
201
|
+
|
|
202
|
+
| File | Purpose |
|
|
203
|
+
|------|---------|
|
|
204
|
+
| `a3m_langchain.ts` | Core integration — `A3MLLM` class, providers, routing, ensemble |
|
|
205
|
+
| `example.ts` | Runnable examples for all features |
|
|
206
|
+
| `README.md` | This file |
|
|
207
|
+
|
|
208
|
+
## Requirements
|
|
209
|
+
|
|
210
|
+
- Node.js 18+
|
|
211
|
+
- `@langchain/core` ≥ 0.1.0 (peer dependency)
|
|
212
|
+
- At least one LLM provider API key
|
|
213
|
+
|
|
214
|
+
## License
|
|
215
|
+
|
|
216
|
+
MIT — Same as A3M Router.
|