lynkr 7.2.5 → 8.0.0
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/README.md +2 -2
- package/config/model-tiers.json +89 -0
- package/docs/docs.html +1 -0
- package/docs/index.md +7 -0
- package/docs/toon-integration-spec.md +130 -0
- package/documentation/README.md +3 -2
- package/documentation/claude-code-cli.md +23 -16
- package/documentation/cursor-integration.md +17 -14
- package/documentation/docker.md +11 -4
- package/documentation/embeddings.md +7 -5
- package/documentation/faq.md +66 -12
- package/documentation/features.md +22 -15
- package/documentation/installation.md +66 -14
- package/documentation/production.md +43 -8
- package/documentation/providers.md +145 -42
- package/documentation/routing.md +476 -0
- package/documentation/token-optimization.md +7 -5
- package/documentation/troubleshooting.md +81 -5
- package/install.sh +6 -1
- package/package.json +4 -2
- package/scripts/setup.js +0 -1
- package/src/agents/executor.js +14 -6
- package/src/api/middleware/session.js +15 -2
- package/src/api/openai-router.js +130 -37
- package/src/api/providers-handler.js +15 -1
- package/src/api/router.js +107 -2
- package/src/budget/index.js +4 -3
- package/src/clients/databricks.js +431 -234
- package/src/clients/gpt-utils.js +181 -0
- package/src/clients/ollama-utils.js +66 -140
- package/src/clients/routing.js +0 -1
- package/src/clients/standard-tools.js +76 -3
- package/src/config/index.js +113 -35
- package/src/context/toon.js +173 -0
- package/src/logger/index.js +23 -0
- package/src/orchestrator/index.js +686 -211
- package/src/routing/agentic-detector.js +320 -0
- package/src/routing/complexity-analyzer.js +202 -2
- package/src/routing/cost-optimizer.js +305 -0
- package/src/routing/index.js +168 -159
- package/src/routing/model-tiers.js +365 -0
- package/src/server.js +2 -2
- package/src/sessions/cleanup.js +3 -3
- package/src/sessions/record.js +10 -1
- package/src/sessions/store.js +7 -2
- package/src/tools/agent-task.js +48 -1
- package/src/tools/index.js +15 -2
- package/te +11622 -0
- package/test/README.md +1 -1
- package/test/azure-openai-config.test.js +17 -8
- package/test/azure-openai-integration.test.js +7 -1
- package/test/azure-openai-routing.test.js +41 -43
- package/test/bedrock-integration.test.js +18 -32
- package/test/hybrid-routing-integration.test.js +35 -20
- package/test/hybrid-routing-performance.test.js +74 -64
- package/test/llamacpp-integration.test.js +28 -9
- package/test/lmstudio-integration.test.js +20 -8
- package/test/openai-integration.test.js +17 -20
- package/test/performance-tests.js +1 -1
- package/test/routing.test.js +65 -59
- package/test/toon-compression.test.js +131 -0
- package/CLAWROUTER_ROUTING_PLAN.md +0 -910
- package/ROUTER_COMPARISON.md +0 -173
- package/TIER_ROUTING_PLAN.md +0 -771
package/test/README.md
CHANGED
|
@@ -9,7 +9,7 @@ All tests for the Lynkr project are consolidated in this `test/` directory.
|
|
|
9
9
|
**Purpose**: Tests the hybrid routing logic in isolation
|
|
10
10
|
**Run**: `DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test test/routing.test.js`
|
|
11
11
|
**Coverage**: 10 tests
|
|
12
|
-
- Routing with
|
|
12
|
+
- Routing with tier-based routing disabled (no TIER_* vars set)
|
|
13
13
|
- Simple requests → Ollama
|
|
14
14
|
- Complex requests → Cloud
|
|
15
15
|
- Tool capability checks
|
|
@@ -17,6 +17,12 @@ describe("Azure OpenAI Configuration Tests", () => {
|
|
|
17
17
|
process.env.AZURE_OPENAI_API_KEY = "";
|
|
18
18
|
process.env.AZURE_OPENAI_DEPLOYMENT = "";
|
|
19
19
|
process.env.AZURE_OPENAI_API_VERSION = "";
|
|
20
|
+
|
|
21
|
+
// Prevent .env TIER_* values from being picked up by dotenv
|
|
22
|
+
process.env.TIER_SIMPLE = "";
|
|
23
|
+
process.env.TIER_MEDIUM = "";
|
|
24
|
+
process.env.TIER_COMPLEX = "";
|
|
25
|
+
process.env.TIER_REASONING = "";
|
|
20
26
|
});
|
|
21
27
|
|
|
22
28
|
afterEach(() => {
|
|
@@ -119,7 +125,7 @@ describe("Azure OpenAI Configuration Tests", () => {
|
|
|
119
125
|
|
|
120
126
|
describe("Fallback Provider Validation", () => {
|
|
121
127
|
it("should accept azure-openai as fallback provider with credentials", () => {
|
|
122
|
-
process.env.
|
|
128
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
123
129
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
124
130
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
125
131
|
process.env.FALLBACK_ENABLED = "true";
|
|
@@ -134,9 +140,8 @@ describe("Azure OpenAI Configuration Tests", () => {
|
|
|
134
140
|
assert.strictEqual(config.modelProvider.fallbackProvider, "azure-openai");
|
|
135
141
|
});
|
|
136
142
|
|
|
137
|
-
it("should
|
|
138
|
-
process.env.MODEL_PROVIDER = "ollama";
|
|
139
|
-
process.env.PREFER_OLLAMA = "true";
|
|
143
|
+
it("should warn when azure-openai is fallback but credentials missing", () => {
|
|
144
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
140
145
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
141
146
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
142
147
|
process.env.FALLBACK_ENABLED = "true";
|
|
@@ -146,11 +151,15 @@ describe("Azure OpenAI Configuration Tests", () => {
|
|
|
146
151
|
process.env.AZURE_OPENAI_API_KEY = "";
|
|
147
152
|
process.env.DATABRICKS_API_KEY = "test-key";
|
|
148
153
|
process.env.DATABRICKS_API_BASE = "http://test.com";
|
|
154
|
+
// Enable tier routing so fallback validation runs
|
|
155
|
+
process.env.TIER_SIMPLE = "ollama:llama3.2";
|
|
156
|
+
process.env.TIER_MEDIUM = "ollama:llama3.2";
|
|
157
|
+
process.env.TIER_COMPLEX = "ollama:llama3.2";
|
|
158
|
+
process.env.TIER_REASONING = "ollama:llama3.2";
|
|
149
159
|
|
|
150
|
-
// Should
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}, /AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_API_KEY/);
|
|
160
|
+
// Should warn but not throw (fallback misconfigured warning)
|
|
161
|
+
const config = require("../src/config");
|
|
162
|
+
assert.strictEqual(config.modelProvider.fallbackProvider, "azure-openai");
|
|
154
163
|
});
|
|
155
164
|
});
|
|
156
165
|
|
|
@@ -17,6 +17,12 @@ describe("Azure OpenAI Integration Tests", () => {
|
|
|
17
17
|
process.env.MODEL_PROVIDER = "databricks";
|
|
18
18
|
process.env.DATABRICKS_API_KEY = "test-key";
|
|
19
19
|
process.env.DATABRICKS_API_BASE = "http://test.com";
|
|
20
|
+
|
|
21
|
+
// Prevent .env TIER_* values from being picked up by dotenv
|
|
22
|
+
process.env.TIER_SIMPLE = "";
|
|
23
|
+
process.env.TIER_MEDIUM = "";
|
|
24
|
+
process.env.TIER_COMPLEX = "";
|
|
25
|
+
process.env.TIER_REASONING = "";
|
|
20
26
|
});
|
|
21
27
|
|
|
22
28
|
afterEach(() => {
|
|
@@ -169,7 +175,7 @@ describe("Azure OpenAI Integration Tests", () => {
|
|
|
169
175
|
});
|
|
170
176
|
|
|
171
177
|
it("should select azure-openai as fallback provider", () => {
|
|
172
|
-
process.env.
|
|
178
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
173
179
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
174
180
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
175
181
|
process.env.FALLBACK_ENABLED = "true";
|
|
@@ -9,6 +9,11 @@ describe("Azure OpenAI Routing Tests", () => {
|
|
|
9
9
|
// Clear module cache
|
|
10
10
|
delete require.cache[require.resolve("../src/config")];
|
|
11
11
|
delete require.cache[require.resolve("../src/clients/routing")];
|
|
12
|
+
delete require.cache[require.resolve("../src/routing/index.js")];
|
|
13
|
+
delete require.cache[require.resolve("../src/routing/model-tiers")];
|
|
14
|
+
delete require.cache[require.resolve("../src/routing/complexity-analyzer")];
|
|
15
|
+
delete require.cache[require.resolve("../src/routing/cost-optimizer")];
|
|
16
|
+
delete require.cache[require.resolve("../src/routing/agentic-detector")];
|
|
12
17
|
|
|
13
18
|
// Store original config
|
|
14
19
|
originalConfig = { ...process.env };
|
|
@@ -20,9 +25,15 @@ describe("Azure OpenAI Routing Tests", () => {
|
|
|
20
25
|
process.env.MODEL_PROVIDER = "databricks"; // Set default to avoid validation errors
|
|
21
26
|
process.env.DATABRICKS_API_KEY = "test-key";
|
|
22
27
|
process.env.DATABRICKS_API_BASE = "http://test.com";
|
|
23
|
-
|
|
28
|
+
|
|
24
29
|
// Explicitly set valid fallback to override any local .env pollution (e.g. lmstudio)
|
|
25
30
|
process.env.FALLBACK_PROVIDER = "databricks";
|
|
31
|
+
|
|
32
|
+
// Ensure no TIER_* vars leak between tests
|
|
33
|
+
process.env.TIER_SIMPLE = "";
|
|
34
|
+
process.env.TIER_MEDIUM = "";
|
|
35
|
+
process.env.TIER_COMPLEX = "";
|
|
36
|
+
process.env.TIER_REASONING = "";
|
|
26
37
|
});
|
|
27
38
|
|
|
28
39
|
afterEach(() => {
|
|
@@ -31,32 +42,25 @@ describe("Azure OpenAI Routing Tests", () => {
|
|
|
31
42
|
});
|
|
32
43
|
|
|
33
44
|
describe("Primary Provider Routing", () => {
|
|
34
|
-
it("should route to azure-openai when set as MODEL_PROVIDER", () => {
|
|
45
|
+
it("should route to azure-openai when set as MODEL_PROVIDER", async () => {
|
|
35
46
|
process.env.MODEL_PROVIDER = "azure-openai";
|
|
36
47
|
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
37
48
|
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
38
|
-
process.env.PREFER_OLLAMA = "false";
|
|
39
49
|
|
|
40
50
|
routing = require("../src/clients/routing");
|
|
41
51
|
|
|
42
|
-
const
|
|
52
|
+
const result = await routing.determineProviderSmart({
|
|
53
|
+
messages: [{ role: "user", content: "test" }],
|
|
54
|
+
tools: []
|
|
55
|
+
});
|
|
43
56
|
|
|
44
|
-
assert.strictEqual(provider, "azure-openai");
|
|
57
|
+
assert.strictEqual(result.provider, "azure-openai");
|
|
45
58
|
});
|
|
46
59
|
});
|
|
47
60
|
|
|
48
|
-
describe("
|
|
49
|
-
it("should
|
|
50
|
-
|
|
51
|
-
// Set to empty string instead of delete to prevent dotenv from reloading it
|
|
52
|
-
process.env.OPENROUTER_API_KEY = "";
|
|
53
|
-
|
|
54
|
-
process.env.PREFER_OLLAMA = "true";
|
|
55
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
56
|
-
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
57
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
58
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "3";
|
|
59
|
-
process.env.OPENROUTER_MAX_TOOLS_FOR_ROUTING = "15";
|
|
61
|
+
describe("Static Routing with Azure OpenAI", () => {
|
|
62
|
+
it("should return primary provider regardless of tool count (tier routing disabled)", async () => {
|
|
63
|
+
process.env.MODEL_PROVIDER = "azure-openai";
|
|
60
64
|
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
61
65
|
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
62
66
|
|
|
@@ -67,24 +71,19 @@ describe("Azure OpenAI Routing Tests", () => {
|
|
|
67
71
|
|
|
68
72
|
routing = require("../src/clients/routing");
|
|
69
73
|
|
|
70
|
-
|
|
71
|
-
|
|
74
|
+
const result = await routing.determineProviderSmart({
|
|
75
|
+
messages: [{ role: "user", content: "test" }],
|
|
72
76
|
tools: [{}, {}, {}, {}, {}]
|
|
73
77
|
});
|
|
74
78
|
|
|
75
|
-
assert.strictEqual(provider, "azure-openai");
|
|
79
|
+
assert.strictEqual(result.provider, "azure-openai");
|
|
80
|
+
assert.strictEqual(result.method, "static");
|
|
76
81
|
});
|
|
77
82
|
|
|
78
|
-
it("should
|
|
79
|
-
process.env.
|
|
80
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
81
|
-
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
82
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
83
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "3";
|
|
84
|
-
process.env.OPENROUTER_MAX_TOOLS_FOR_ROUTING = "15";
|
|
85
|
-
process.env.OPENROUTER_API_KEY = "openrouter-key";
|
|
83
|
+
it("should return primary provider for simple requests", async () => {
|
|
84
|
+
process.env.MODEL_PROVIDER = "azure-openai";
|
|
86
85
|
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
87
|
-
process.env.AZURE_OPENAI_API_KEY = "
|
|
86
|
+
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
88
87
|
|
|
89
88
|
// Clear cache after env setup
|
|
90
89
|
delete require.cache[require.resolve("../src/config/index.js")];
|
|
@@ -93,20 +92,17 @@ describe("Azure OpenAI Routing Tests", () => {
|
|
|
93
92
|
|
|
94
93
|
routing = require("../src/clients/routing");
|
|
95
94
|
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
tools: [{}, {}
|
|
95
|
+
const result = await routing.determineProviderSmart({
|
|
96
|
+
messages: [{ role: "user", content: "test" }],
|
|
97
|
+
tools: [{}, {}]
|
|
99
98
|
});
|
|
100
99
|
|
|
101
|
-
assert.strictEqual(provider, "
|
|
100
|
+
assert.strictEqual(result.provider, "azure-openai");
|
|
101
|
+
assert.strictEqual(result.method, "static");
|
|
102
102
|
});
|
|
103
103
|
|
|
104
|
-
it("should
|
|
105
|
-
process.env.
|
|
106
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
107
|
-
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
108
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
109
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "3";
|
|
104
|
+
it("should return static routing from determineProviderSmart when tiers disabled", async () => {
|
|
105
|
+
process.env.MODEL_PROVIDER = "azure-openai";
|
|
110
106
|
process.env.AZURE_OPENAI_ENDPOINT = "https://test.openai.azure.com";
|
|
111
107
|
process.env.AZURE_OPENAI_API_KEY = "test-key";
|
|
112
108
|
|
|
@@ -117,18 +113,20 @@ describe("Azure OpenAI Routing Tests", () => {
|
|
|
117
113
|
|
|
118
114
|
routing = require("../src/clients/routing");
|
|
119
115
|
|
|
120
|
-
|
|
121
|
-
|
|
116
|
+
const result = await routing.determineProviderSmart({
|
|
117
|
+
messages: [{ role: "user", content: "test" }],
|
|
122
118
|
tools: [{}, {}]
|
|
123
119
|
});
|
|
124
120
|
|
|
125
|
-
assert.strictEqual(provider, "
|
|
121
|
+
assert.strictEqual(result.provider, "azure-openai");
|
|
122
|
+
assert.strictEqual(result.method, "static");
|
|
123
|
+
assert.strictEqual(result.reason, "tier_routing_disabled");
|
|
126
124
|
});
|
|
127
125
|
});
|
|
128
126
|
|
|
129
127
|
describe("Fallback Configuration", () => {
|
|
130
128
|
it("should support azure-openai as fallback provider", () => {
|
|
131
|
-
process.env.
|
|
129
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
132
130
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
133
131
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
134
132
|
process.env.FALLBACK_ENABLED = "true";
|
|
@@ -11,6 +11,12 @@ describe("AWS Bedrock Integration", () => {
|
|
|
11
11
|
delete require.cache[require.resolve("../src/config")];
|
|
12
12
|
delete require.cache[require.resolve("../src/clients/routing")];
|
|
13
13
|
delete require.cache[require.resolve("../src/clients/bedrock-utils")];
|
|
14
|
+
|
|
15
|
+
// Prevent .env TIER_* values from being picked up by dotenv
|
|
16
|
+
process.env.TIER_SIMPLE = "";
|
|
17
|
+
process.env.TIER_MEDIUM = "";
|
|
18
|
+
process.env.TIER_COMPLEX = "";
|
|
19
|
+
process.env.TIER_REASONING = "";
|
|
14
20
|
});
|
|
15
21
|
|
|
16
22
|
afterEach(() => {
|
|
@@ -374,62 +380,45 @@ describe("AWS Bedrock Integration", () => {
|
|
|
374
380
|
process.env.MODEL_PROVIDER = "bedrock";
|
|
375
381
|
process.env.AWS_ACCESS_KEY_ID = "AKIATEST123";
|
|
376
382
|
process.env.AWS_SECRET_ACCESS_KEY = "testSecretKey123";
|
|
377
|
-
process.env.PREFER_OLLAMA = "false";
|
|
378
383
|
|
|
379
384
|
const config = require("../src/config");
|
|
380
385
|
const routing = require("../src/clients/routing");
|
|
381
386
|
|
|
382
387
|
const payload = { messages: [{ role: "user", content: "test" }] };
|
|
383
|
-
const provider = routing.
|
|
388
|
+
const provider = routing.determineProviderSync(payload);
|
|
384
389
|
|
|
385
|
-
//
|
|
390
|
+
// determineProviderSync returns static MODEL_PROVIDER
|
|
386
391
|
assert.strictEqual(provider, "bedrock");
|
|
387
392
|
});
|
|
388
393
|
|
|
389
|
-
it("should
|
|
390
|
-
process.env.MODEL_PROVIDER = "
|
|
391
|
-
process.env.PREFER_OLLAMA = "true";
|
|
392
|
-
process.env.OLLAMA_MODEL = "llama3.1";
|
|
393
|
-
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
394
|
+
it("should return static routing from determineProviderSmart when tiers disabled", async () => {
|
|
395
|
+
process.env.MODEL_PROVIDER = "bedrock";
|
|
394
396
|
process.env.AWS_ACCESS_KEY_ID = "AKIATEST123";
|
|
395
397
|
process.env.AWS_SECRET_ACCESS_KEY = "testSecretKey123";
|
|
396
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "2";
|
|
397
|
-
process.env.FALLBACK_ENABLED = "true";
|
|
398
|
-
process.env.FALLBACK_PROVIDER = "bedrock";
|
|
399
|
-
|
|
400
|
-
// Clear other providers to ensure bedrock is chosen
|
|
401
|
-
delete process.env.OPENROUTER_API_KEY;
|
|
402
|
-
delete process.env.OPENAI_API_KEY;
|
|
403
|
-
delete process.env.AZURE_OPENAI_API_KEY;
|
|
404
|
-
delete process.env.AZURE_OPENAI_ENDPOINT;
|
|
405
|
-
delete process.env.LLAMACPP_ENDPOINT;
|
|
406
|
-
delete process.env.LMSTUDIO_ENDPOINT;
|
|
407
|
-
delete process.env.DATABRICKS_API_KEY;
|
|
408
|
-
delete process.env.DATABRICKS_API_BASE;
|
|
409
398
|
|
|
410
399
|
const config = require("../src/config");
|
|
411
400
|
const routing = require("../src/clients/routing");
|
|
412
401
|
|
|
413
|
-
//
|
|
402
|
+
// Many tools -- but without TIER_* vars, determineProviderSmart returns static routing
|
|
414
403
|
const payload = {
|
|
415
404
|
messages: [{ role: "user", content: "test" }],
|
|
416
405
|
tools: Array(20).fill({ name: "tool" }),
|
|
417
406
|
};
|
|
418
|
-
const
|
|
407
|
+
const result = await routing.determineProviderSmart(payload);
|
|
419
408
|
|
|
420
|
-
assert.strictEqual(provider, "bedrock");
|
|
409
|
+
assert.strictEqual(result.provider, "bedrock");
|
|
410
|
+
assert.strictEqual(result.method, "static");
|
|
411
|
+
assert.strictEqual(result.reason, "tier_routing_disabled");
|
|
421
412
|
});
|
|
422
413
|
});
|
|
423
414
|
|
|
424
415
|
describe("Fallback Provider", () => {
|
|
425
416
|
it("should allow bedrock as fallback provider", () => {
|
|
426
417
|
process.env.MODEL_PROVIDER = "ollama";
|
|
427
|
-
process.env.PREFER_OLLAMA = "true";
|
|
428
418
|
process.env.OLLAMA_MODEL = "llama3.1";
|
|
429
419
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
430
420
|
process.env.FALLBACK_PROVIDER = "bedrock";
|
|
431
|
-
process.env.
|
|
432
|
-
process.env.AWS_SECRET_ACCESS_KEY = "testSecretKey123";
|
|
421
|
+
process.env.AWS_BEDROCK_API_KEY = "test-bedrock-key";
|
|
433
422
|
process.env.FALLBACK_ENABLED = "true";
|
|
434
423
|
|
|
435
424
|
// Should not throw
|
|
@@ -439,24 +428,21 @@ describe("AWS Bedrock Integration", () => {
|
|
|
439
428
|
|
|
440
429
|
it("should validate bedrock credentials when used as fallback", () => {
|
|
441
430
|
process.env.MODEL_PROVIDER = "ollama";
|
|
442
|
-
process.env.PREFER_OLLAMA = "true";
|
|
443
431
|
process.env.OLLAMA_MODEL = "llama3.1";
|
|
444
432
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
445
433
|
process.env.FALLBACK_PROVIDER = "bedrock";
|
|
446
434
|
process.env.FALLBACK_ENABLED = "true";
|
|
447
435
|
// Set to empty string to override .env file values
|
|
448
|
-
process.env.
|
|
449
|
-
process.env.AWS_SECRET_ACCESS_KEY = "";
|
|
436
|
+
process.env.AWS_BEDROCK_API_KEY = "";
|
|
450
437
|
|
|
451
438
|
assert.throws(
|
|
452
439
|
() => require("../src/config"),
|
|
453
|
-
/FALLBACK_PROVIDER is set to 'bedrock' but
|
|
440
|
+
/FALLBACK_PROVIDER is set to 'bedrock' but AWS_BEDROCK_API_KEY is not configured/
|
|
454
441
|
);
|
|
455
442
|
});
|
|
456
443
|
|
|
457
444
|
it("should not allow local providers as fallback", () => {
|
|
458
445
|
process.env.MODEL_PROVIDER = "ollama";
|
|
459
|
-
process.env.PREFER_OLLAMA = "true";
|
|
460
446
|
process.env.OLLAMA_MODEL = "llama3.1";
|
|
461
447
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
462
448
|
process.env.FALLBACK_PROVIDER = "llamacpp";
|
|
@@ -21,6 +21,12 @@ describe("Hybrid Routing Integration Tests", () => {
|
|
|
21
21
|
process.env.DATABRICKS_API_KEY = "test-key";
|
|
22
22
|
process.env.DATABRICKS_API_BASE = "http://test.databricks.com";
|
|
23
23
|
process.env.MODEL_PROVIDER = "databricks";
|
|
24
|
+
|
|
25
|
+
// Set TIER_* to empty to prevent .env file values from being picked up by dotenv
|
|
26
|
+
process.env.TIER_SIMPLE = "";
|
|
27
|
+
process.env.TIER_MEDIUM = "";
|
|
28
|
+
process.env.TIER_COMPLEX = "";
|
|
29
|
+
process.env.TIER_REASONING = "";
|
|
24
30
|
});
|
|
25
31
|
|
|
26
32
|
afterEach(() => {
|
|
@@ -30,7 +36,7 @@ describe("Hybrid Routing Integration Tests", () => {
|
|
|
30
36
|
|
|
31
37
|
describe("Configuration Validation", () => {
|
|
32
38
|
it("should use default OLLAMA_ENDPOINT when not specified", () => {
|
|
33
|
-
process.env.
|
|
39
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
34
40
|
delete process.env.OLLAMA_ENDPOINT;
|
|
35
41
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
36
42
|
process.env.DATABRICKS_API_KEY = "test-key";
|
|
@@ -43,7 +49,7 @@ describe("Hybrid Routing Integration Tests", () => {
|
|
|
43
49
|
});
|
|
44
50
|
|
|
45
51
|
it("should reject invalid FALLBACK_PROVIDER", () => {
|
|
46
|
-
process.env.
|
|
52
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
47
53
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
48
54
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
49
55
|
process.env.FALLBACK_ENABLED = "true";
|
|
@@ -55,20 +61,24 @@ describe("Hybrid Routing Integration Tests", () => {
|
|
|
55
61
|
});
|
|
56
62
|
|
|
57
63
|
it("should reject circular fallback (ollama -> ollama)", () => {
|
|
58
|
-
process.env.
|
|
64
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
59
65
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
60
66
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
61
67
|
process.env.FALLBACK_ENABLED = "true";
|
|
62
68
|
process.env.FALLBACK_PROVIDER = "ollama";
|
|
69
|
+
// Enable tier routing so fallback validation runs
|
|
70
|
+
process.env.TIER_SIMPLE = "ollama:llama3.2";
|
|
71
|
+
process.env.TIER_MEDIUM = "ollama:llama3.2";
|
|
72
|
+
process.env.TIER_COMPLEX = "ollama:llama3.2";
|
|
73
|
+
process.env.TIER_REASONING = "ollama:llama3.2";
|
|
63
74
|
|
|
64
75
|
assert.throws(() => {
|
|
65
76
|
require("../src/config");
|
|
66
77
|
}, /FALLBACK_PROVIDER cannot be 'ollama'/);
|
|
67
78
|
});
|
|
68
79
|
|
|
69
|
-
it("should
|
|
70
|
-
process.env.MODEL_PROVIDER = "ollama";
|
|
71
|
-
process.env.PREFER_OLLAMA = "true";
|
|
80
|
+
it("should warn when databricks fallback has no credentials", () => {
|
|
81
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
72
82
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
73
83
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
74
84
|
process.env.FALLBACK_ENABLED = "true";
|
|
@@ -76,39 +86,44 @@ describe("Hybrid Routing Integration Tests", () => {
|
|
|
76
86
|
// Set to empty strings instead of deleting (dotenv.config() in config module would reload from .env)
|
|
77
87
|
process.env.DATABRICKS_API_KEY = "";
|
|
78
88
|
process.env.DATABRICKS_API_BASE = "";
|
|
89
|
+
// Enable tier routing so fallback validation runs
|
|
90
|
+
process.env.TIER_SIMPLE = "ollama:llama3.2";
|
|
91
|
+
process.env.TIER_MEDIUM = "ollama:llama3.2";
|
|
92
|
+
process.env.TIER_COMPLEX = "ollama:llama3.2";
|
|
93
|
+
process.env.TIER_REASONING = "ollama:llama3.2";
|
|
79
94
|
|
|
80
|
-
// Should
|
|
81
|
-
|
|
82
|
-
assert.
|
|
83
|
-
require("../src/config");
|
|
84
|
-
}, /DATABRICKS_API_BASE and DATABRICKS_API_KEY/);
|
|
95
|
+
// Should warn but not throw (fallback misconfigured)
|
|
96
|
+
const config = require("../src/config");
|
|
97
|
+
assert.strictEqual(config.modelProvider.fallbackProvider, "databricks");
|
|
85
98
|
});
|
|
86
99
|
|
|
87
|
-
it("should accept valid
|
|
88
|
-
process.env.
|
|
100
|
+
it("should accept valid tier routing configuration", () => {
|
|
101
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
89
102
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
90
103
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
91
104
|
process.env.FALLBACK_ENABLED = "true";
|
|
92
105
|
process.env.FALLBACK_PROVIDER = "databricks";
|
|
93
|
-
process.env.OLLAMA_MAX_TOOLS_FOR_ROUTING = "3"; // Override .env which sets it to 2
|
|
94
106
|
process.env.DATABRICKS_API_KEY = "test-key";
|
|
95
107
|
process.env.DATABRICKS_API_BASE = "http://test.com";
|
|
108
|
+
process.env.TIER_SIMPLE = "ollama:llama3.2";
|
|
109
|
+
process.env.TIER_MEDIUM = "ollama:llama3.2";
|
|
110
|
+
process.env.TIER_COMPLEX = "databricks:claude-sonnet";
|
|
111
|
+
process.env.TIER_REASONING = "databricks:claude-sonnet";
|
|
96
112
|
|
|
97
113
|
const config = require("../src/config");
|
|
98
114
|
|
|
99
|
-
assert.strictEqual(config.modelProvider.
|
|
115
|
+
assert.strictEqual(config.modelProvider.type, "ollama");
|
|
100
116
|
assert.strictEqual(config.modelProvider.fallbackEnabled, true);
|
|
101
|
-
assert.strictEqual(config.modelProvider.ollamaMaxToolsForRouting, 3);
|
|
102
117
|
assert.strictEqual(config.modelProvider.fallbackProvider, "databricks");
|
|
118
|
+
assert.strictEqual(config.modelTiers.enabled, true);
|
|
103
119
|
});
|
|
104
120
|
});
|
|
105
121
|
|
|
106
122
|
describe("Metrics Recording", () => {
|
|
107
123
|
beforeEach(() => {
|
|
108
|
-
process.env.
|
|
124
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
109
125
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
110
126
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
111
|
-
process.env.OLLAMA_FALLBACK_PROVIDER = "databricks";
|
|
112
127
|
|
|
113
128
|
config = require("../src/config");
|
|
114
129
|
const metricsModule = require("../src/observability/metrics");
|
|
@@ -207,7 +222,7 @@ describe("Hybrid Routing Integration Tests", () => {
|
|
|
207
222
|
it("should categorize circuit breaker errors", () => {
|
|
208
223
|
// This would need to be tested by importing the function if exported
|
|
209
224
|
// For now, we test via the integrated behavior
|
|
210
|
-
process.env.
|
|
225
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
211
226
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
212
227
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
213
228
|
|
|
@@ -231,7 +246,7 @@ describe("Hybrid Routing Integration Tests", () => {
|
|
|
231
246
|
});
|
|
232
247
|
|
|
233
248
|
it("should estimate cost savings correctly", () => {
|
|
234
|
-
process.env.
|
|
249
|
+
process.env.MODEL_PROVIDER = "ollama";
|
|
235
250
|
process.env.OLLAMA_ENDPOINT = "http://localhost:11434";
|
|
236
251
|
process.env.OLLAMA_MODEL = "qwen2.5-coder:latest";
|
|
237
252
|
|