lynkr 9.7.2 → 9.9.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.
Files changed (80) hide show
  1. package/README.md +29 -19
  2. package/bin/cli.js +11 -0
  3. package/bin/lynkr-init.js +14 -1
  4. package/bin/lynkr-usage.js +78 -0
  5. package/bin/wrap.js +60 -35
  6. package/config/difficulty-anchors.json +22 -0
  7. package/package.json +24 -3
  8. package/scripts/audit-log-reader.js +399 -0
  9. package/scripts/calibrate-thresholds.js +38 -157
  10. package/scripts/compact-dictionary.js +204 -0
  11. package/scripts/test-deduplication.js +448 -0
  12. package/scripts/ws7-anchor-replay.js +108 -0
  13. package/skills/lynkr/SKILL.md +195 -0
  14. package/src/agents/context-manager.js +18 -2
  15. package/src/agents/definitions/loader.js +90 -0
  16. package/src/agents/executor.js +24 -2
  17. package/src/agents/index.js +9 -1
  18. package/src/agents/parallel-coordinator.js +2 -2
  19. package/src/agents/reflector.js +11 -1
  20. package/src/api/middleware/loop-guard.js +87 -0
  21. package/src/api/middleware/request-logging.js +5 -64
  22. package/src/api/middleware/session.js +0 -0
  23. package/src/api/openai-router.js +120 -101
  24. package/src/api/providers-handler.js +27 -2
  25. package/src/api/router.js +450 -125
  26. package/src/budget/index.js +2 -19
  27. package/src/cache/semantic.js +9 -0
  28. package/src/clients/databricks.js +459 -146
  29. package/src/clients/gpt-utils.js +11 -105
  30. package/src/clients/openai-format.js +10 -3
  31. package/src/clients/openrouter-utils.js +49 -24
  32. package/src/clients/prompt-cache-injection.js +1 -0
  33. package/src/clients/provider-capabilities.js +1 -1
  34. package/src/clients/responses-format.js +34 -3
  35. package/src/clients/routing.js +15 -0
  36. package/src/config/index.js +36 -2
  37. package/src/context/gcf.js +275 -0
  38. package/src/context/tool-result-compressor.js +51 -9
  39. package/src/dashboard/api.js +1 -0
  40. package/src/logger/index.js +14 -1
  41. package/src/memory/search.js +12 -40
  42. package/src/memory/tools.js +3 -24
  43. package/src/orchestrator/bypass.js +4 -2
  44. package/src/orchestrator/index.js +144 -88
  45. package/src/routing/affinity-store.js +194 -0
  46. package/src/routing/agentic-detector.js +36 -6
  47. package/src/routing/bandit.js +25 -6
  48. package/src/routing/calibration.js +212 -0
  49. package/src/routing/client-profiles.js +292 -0
  50. package/src/routing/complexity-analyzer.js +48 -11
  51. package/src/routing/deescalator.js +148 -0
  52. package/src/routing/degradation.js +109 -0
  53. package/src/routing/feedback.js +157 -0
  54. package/src/routing/index.js +897 -87
  55. package/src/routing/intent-score.js +339 -0
  56. package/src/routing/interaction.js +3 -0
  57. package/src/routing/knn-router.js +70 -21
  58. package/src/routing/model-registry.js +28 -7
  59. package/src/routing/model-tiers.js +25 -2
  60. package/src/routing/reward-pipeline.js +68 -2
  61. package/src/routing/risk-analyzer.js +30 -1
  62. package/src/routing/risk-classifier.js +6 -2
  63. package/src/routing/session-affinity.js +162 -34
  64. package/src/routing/telemetry.js +298 -10
  65. package/src/routing/verifier.js +267 -0
  66. package/src/server.js +66 -21
  67. package/src/sessions/cleanup.js +17 -0
  68. package/src/tools/index.js +1 -15
  69. package/src/tools/smart-selection.js +10 -0
  70. package/src/tools/web-client.js +3 -3
  71. package/.eslintrc.cjs +0 -12
  72. package/benchmark-configs/litellm_config.yaml +0 -86
  73. package/benchmark-configs/lynkr.env +0 -48
  74. package/benchmark-configs/portkey-config.json +0 -60
  75. package/benchmark-configs/portkey-docker.sh +0 -23
  76. package/benchmark-tier-routing.js +0 -449
  77. package/funding.json +0 -110
  78. package/src/api/middleware/validation.js +0 -261
  79. package/src/routing/drift-monitor.js +0 -113
  80. package/src/workers/helpers.js +0 -185
package/src/server.js CHANGED
@@ -37,13 +37,10 @@ const lazyLoader = require("./tools/lazy-loader");
37
37
  const { setLazyLoader } = require("./tools");
38
38
  const { waitForOllama } = require("./clients/ollama-startup");
39
39
 
40
- // Initialize MCP
41
40
  initialiseMcp();
42
41
 
43
- // Set up lazy tool loading
44
42
  setLazyLoader(lazyLoader);
45
43
 
46
- // Check if lazy loading is enabled (default: true)
47
44
  const LAZY_TOOLS_ENABLED = process.env.LAZY_TOOLS_ENABLED !== "false";
48
45
 
49
46
  if (LAZY_TOOLS_ENABLED) {
@@ -88,16 +85,13 @@ function createApp() {
88
85
  app.get('/dashboard/api/routing', require('./dashboard/api').routing);
89
86
  app.get('/dashboard/api/logs', require('./dashboard/api').logs);
90
87
 
91
- // Initialize load shedder (log configuration)
92
88
  initializeLoadShedder();
93
89
 
94
- // Load shedding (protect against overload)
95
90
  app.use(loadSheddingMiddleware);
96
91
 
97
92
  // Request logging (add request IDs, structured logs)
98
93
  app.use(requestLoggingMiddleware);
99
94
 
100
- // Metrics collection
101
95
  app.use(metricsMiddleware);
102
96
 
103
97
  // Note: If using a tunnel (ngrok, Cloudflare Tunnel) and seeing BrotliDecompressionError,
@@ -107,9 +101,21 @@ function createApp() {
107
101
  app.use(sessionMiddleware);
108
102
  app.use(loggingMiddleware);
109
103
 
104
+ // Agent-framework endpoints: LangGraph/CrewAI/AutoGen talk to the
105
+ // OpenAI-compat surface, Claude Code to /v1/messages. Guards that protect
106
+ // against runaway agents must cover BOTH — mounting on /v1/messages only
107
+ // silently exempts every OpenAI-compat client (this bit us with client
108
+ // profiles already).
109
+ const AGENT_ENDPOINTS = ['/v1/messages', '/v1/chat/completions', '/v1/responses'];
110
+
111
+ // Loop guard — stateless runaway-agent circuit breaker
112
+ // (LYNKR_MAX_SESSION_TURNS / LYNKR_MAX_TOOL_TURNS; off unless set).
113
+ const { loopGuard } = require('./api/middleware/loop-guard');
114
+ for (const p of AGENT_ENDPOINTS) app.use(p, loopGuard);
115
+
110
116
  // Budget and rate limiting (can be disabled via config)
111
117
  if (config.budget?.enabled !== false) {
112
- app.use('/v1/messages', budgetMiddleware);
118
+ for (const p of AGENT_ENDPOINTS) app.use(p, budgetMiddleware);
113
119
  }
114
120
 
115
121
  // Phase 6.1 — per-tenant routing policies (LYNKR-Tenant-Id header).
@@ -117,13 +123,11 @@ function createApp() {
117
123
  app.use('/v1/messages', tenantMiddleware);
118
124
 
119
125
  // Phase 6.2 — hierarchical budget enforcement (LYNKR_BUDGET_ENFORCER=false to disable).
120
- app.use('/v1/messages', budgetEnforcer);
126
+ for (const p of AGENT_ENDPOINTS) app.use(p, budgetEnforcer);
121
127
 
122
- // Health check endpoints
123
128
  app.get("/health/live", livenessCheck);
124
129
  app.get("/health/ready", readinessCheck);
125
130
 
126
- // Metrics endpoints
127
131
  app.get("/metrics", (req, res) => {
128
132
  res.json(metrics.snapshot());
129
133
  });
@@ -176,10 +180,8 @@ function createApp() {
176
180
 
177
181
  app.use(router);
178
182
 
179
- // Dashboard UI
180
183
  app.use('/dashboard', require('./dashboard/router'));
181
184
 
182
- // Files API
183
185
  const filesRouter = require("./api/files-router");
184
186
  app.use("/v1", filesRouter);
185
187
 
@@ -193,8 +195,7 @@ function createApp() {
193
195
  }
194
196
 
195
197
  async function start() {
196
- // Initialize Worker Thread Pool (if enabled)
197
- // This pre-warms worker threads for CPU-intensive tasks
198
+ // Pre-warms worker threads for CPU-intensive tasks
198
199
  if (config.workerPool?.enabled !== false) {
199
200
  try {
200
201
  const poolOptions = {
@@ -245,17 +246,65 @@ async function start() {
245
246
  console.log(`Claude→Databricks proxy listening on http://localhost:${config.port}`);
246
247
  });
247
248
 
248
- // Start session cleanup manager
249
+ // Start session cleanup manager. It also drives routing-side maintenance
250
+ // (telemetry retention + session-pin TTL) via its runCleanup tick — see
251
+ // src/sessions/cleanup.js.
249
252
  const { getSessionCleanupManager } = require("./sessions/cleanup");
250
253
  const sessionCleanup = getSessionCleanupManager();
251
254
  sessionCleanup.start();
252
255
 
253
- // Setup graceful shutdown
256
+ // WS5.7 auto-calibration scheduler.
257
+ //
258
+ // Recomputes `data/calibrated-thresholds.json` from live telemetry every
259
+ // 24 h and hot-reloads the model-tier selector so the update takes
260
+ // effect without a restart. First run is jittered 30–90 min into the
261
+ // process lifetime so a cluster of proxies won't all recalibrate at the
262
+ // same moment. When telemetry is sparse (<100 rows in-window), the
263
+ // calibration step no-ops itself via `runCalibration`'s
264
+ // `insufficient_samples` skip path. Unconditionally armed — the pre-B
265
+ // `LYNKR_AUTO_CALIBRATE` env flag was removed because the "off" state
266
+ // was never useful in prod (calibration is idempotent and self-gating).
267
+ {
268
+ const { runCalibration } = require('./routing/calibration');
269
+ const { reloadCalibratedThresholds } = require('./routing/model-tiers');
270
+ const degradation = require('./routing/degradation');
271
+ const DAY_MS = 24 * 60 * 60 * 1000;
272
+ const FIRST_RUN_MIN_MS = 30 * 60 * 1000;
273
+ const FIRST_RUN_JITTER_MS = 60 * 60 * 1000;
274
+ const firstDelay = FIRST_RUN_MIN_MS + Math.floor(Math.random() * FIRST_RUN_JITTER_MS);
275
+
276
+ const runOnce = () => {
277
+ try {
278
+ const result = runCalibration({ days: 7 });
279
+ if (result.skipped) {
280
+ logger.info({ reason: result.reason, count: result.count }, '[Calibration] Skipped');
281
+ return;
282
+ }
283
+ const before = reloadCalibratedThresholds();
284
+ logger.info({
285
+ calibratedAt: result.calibratedAt,
286
+ sampleCount: result.sampleCount,
287
+ ranges: result.ranges,
288
+ previousRanges: before,
289
+ }, '[Calibration] Ranges refreshed');
290
+ } catch (err) {
291
+ degradation.record('calibration', err);
292
+ }
293
+ };
294
+
295
+ const firstTimer = setTimeout(() => {
296
+ runOnce();
297
+ const interval = setInterval(runOnce, DAY_MS);
298
+ interval.unref();
299
+ }, firstDelay);
300
+ firstTimer.unref();
301
+ logger.info({ firstDelayMs: firstDelay, intervalMs: DAY_MS }, '[Calibration] Scheduler armed');
302
+ }
303
+
254
304
  const shutdownManager = getShutdownManager();
255
305
  shutdownManager.registerServer(server);
256
306
  shutdownManager.setupSignalHandlers();
257
307
 
258
- // Register Headroom shutdown callback
259
308
  if (config.headroom?.enabled) {
260
309
  shutdownManager.onShutdown(async () => {
261
310
  logger.info("Stopping Headroom sidecar on shutdown");
@@ -263,7 +312,6 @@ async function start() {
263
312
  });
264
313
  }
265
314
 
266
- // Register Worker Pool shutdown callback
267
315
  if (config.workerPool?.enabled !== false && isWorkerPoolReady()) {
268
316
  shutdownManager.onShutdown(async () => {
269
317
  logger.info("Stopping worker thread pool on shutdown");
@@ -272,7 +320,6 @@ async function start() {
272
320
  });
273
321
  }
274
322
 
275
- // Register Codex process shutdown callback
276
323
  shutdownManager.onShutdown(async () => {
277
324
  try {
278
325
  const { getCodexProcess } = require("./clients/codex-process");
@@ -283,7 +330,6 @@ async function start() {
283
330
  } catch { /* ignore if codex never started */ }
284
331
  });
285
332
 
286
- // Initialize hot reload config watcher
287
333
  if (config.hotReload?.enabled !== false) {
288
334
  const watcher = initConfigWatcher({
289
335
  paths: [".env"],
@@ -300,7 +346,6 @@ async function start() {
300
346
  }
301
347
  });
302
348
 
303
- // Stop watcher on shutdown
304
349
  shutdownManager.onShutdown(() => {
305
350
  const w = getConfigWatcher();
306
351
  if (w) w.stop();
@@ -32,6 +32,23 @@ class SessionCleanupManager {
32
32
  } catch (error) {
33
33
  logger.error({ error }, "Session cleanup failed");
34
34
  }
35
+
36
+ // Routing-side maintenance piggybacks on the same tick — both live in
37
+ // .lynkr/telemetry.db, both are unbounded without a scheduled prune,
38
+ // and neither had a caller before this hook. WS5 auto-calibration will
39
+ // hang off this same manager.
40
+ try {
41
+ const telemetry = require("../routing/telemetry");
42
+ const affinityStore = require("../routing/affinity-store");
43
+ const pinTtlMs = Number(process.env.LYNKR_STICKY_TTL_MS) || 6 * 60 * 60 * 1000;
44
+ const telemetryDeleted = telemetry.cleanup();
45
+ const pinsDeleted = affinityStore.cleanup(pinTtlMs);
46
+ if (telemetryDeleted || pinsDeleted) {
47
+ logger.debug({ telemetryDeleted, pinsDeleted }, "Routing telemetry/pin cleanup completed");
48
+ }
49
+ } catch (err) {
50
+ logger.debug({ err: err.message }, "Routing cleanup tick failed (non-fatal)");
51
+ }
35
52
  }
36
53
 
37
54
  stop() {
@@ -1,6 +1,5 @@
1
1
  const logger = require("../logger");
2
2
  const { truncateToolOutput } = require("./truncate");
3
- const { isGPTProvider, formatToolResultForGPT } = require("../clients/gpt-utils");
4
3
 
5
4
  const registry = new Map();
6
5
  const registryLowercase = new Map();
@@ -258,19 +257,7 @@ async function executeToolCall(call, context = {}) {
258
257
  );
259
258
  const formatted = normalizeHandlerResult(result);
260
259
 
261
- // Apply tool output truncation for token efficiency
262
- let truncatedContent = truncateToolOutput(normalisedCall.name, formatted.content);
263
-
264
- // GPT-specific formatting temporarily disabled for testing
265
- // const isGPT = context?.provider && isGPTProvider(context.provider);
266
- // if (isGPT) {
267
- // truncatedContent = formatToolResultForGPT(
268
- // normalisedCall.name,
269
- // truncatedContent,
270
- // normalisedCall.arguments
271
- // );
272
- // }
273
- const isGPT = false; // Disabled for testing
260
+ const truncatedContent = truncateToolOutput(normalisedCall.name, formatted.content);
274
261
 
275
262
  return {
276
263
  id: normalisedCall.id,
@@ -284,7 +271,6 @@ async function executeToolCall(call, context = {}) {
284
271
  truncated: truncatedContent !== formatted.content,
285
272
  originalLength: formatted.content?.length,
286
273
  truncatedLength: truncatedContent?.length,
287
- gptFormatted: isGPT,
288
274
  },
289
275
  };
290
276
  } catch (err) {
@@ -122,6 +122,14 @@ function estimateToolTokens(tools) {
122
122
  /**
123
123
  * Apply conservative stripping to the tool list.
124
124
  */
125
+ function recordStrippingSavings(before, after) {
126
+ if (after >= before) return;
127
+ try {
128
+ const telemetry = require('../routing/telemetry');
129
+ telemetry.recordSavings('tool_stripping', (before - after) * 175);
130
+ } catch { /* telemetry is best-effort */ }
131
+ }
132
+
125
133
  function selectToolsSmartly(tools, classification, options = {}) {
126
134
  if (!Array.isArray(tools) || tools.length === 0) return tools;
127
135
 
@@ -130,6 +138,7 @@ function selectToolsSmartly(tools, classification, options = {}) {
130
138
 
131
139
  // Greeting: strip everything
132
140
  if (classification.type === 'conversational') {
141
+ recordStrippingSavings(tools.length, 0);
133
142
  return [];
134
143
  }
135
144
 
@@ -159,6 +168,7 @@ function selectToolsSmartly(tools, classification, options = {}) {
159
168
  selected = selected.slice(0, 10);
160
169
  }
161
170
 
171
+ recordStrippingSavings(tools.length, selected.length);
162
172
  return selected;
163
173
  }
164
174
 
@@ -1,4 +1,4 @@
1
- const { Agent, setGlobalDispatcher } = require("undici");
1
+ const { Agent, fetch: undiciFetch, setGlobalDispatcher } = require("undici");
2
2
  const logger = require("../logger");
3
3
 
4
4
  /**
@@ -42,10 +42,10 @@ function createWebAgent() {
42
42
  const webAgent = createWebAgent();
43
43
 
44
44
  /**
45
- * Fetch with the optimized agent
45
+ * Fetch with the optimized agent (uses undici.fetch for dispatcher support)
46
46
  */
47
47
  async function fetchWithAgent(url, options = {}) {
48
- return fetch(url, {
48
+ return undiciFetch(url, {
49
49
  ...options,
50
50
  dispatcher: webAgent,
51
51
  });
package/.eslintrc.cjs DELETED
@@ -1,12 +0,0 @@
1
- module.exports = {
2
- env: {
3
- node: true,
4
- es2021: true,
5
- },
6
- extends: ["eslint:recommended"],
7
- parserOptions: {
8
- ecmaVersion: "latest",
9
- sourceType: "script",
10
- },
11
- rules: {},
12
- };
@@ -1,86 +0,0 @@
1
- # ─── LiteLLM Benchmark Config ─────────────────────────────────────────────────
2
- # Multi-provider tier routing via LiteLLM Complexity Router.
3
- #
4
- # Start: litellm --config benchmark-configs/litellm_config.yaml --port 8082
5
- #
6
- # Required env vars:
7
- # AZURE_OPENAI_API_KEY
8
- # AZURE_OPENAI_ENDPOINT (https://YOUR-RESOURCE.openai.azure.com)
9
- # MOONSHOT_API_KEY
10
- # (Ollama needs no key — running locally on :11434)
11
- #
12
- # Tier mapping (matches Lynkr benchmark config):
13
- # SIMPLE → ollama:minimax-m2.5:cloud
14
- # MEDIUM → ollama:minimax-m2.5:cloud
15
- # COMPLEX → moonshot:moonshot-v1-auto
16
- # REASONING → azure-openai:gpt-5.2-chat
17
-
18
- model_list:
19
-
20
- # ── SIMPLE + MEDIUM → Ollama minimax-m2.5:cloud ───────────────────────────
21
- # Note: the model tag is "minimax-m2.5:cloud" — the colon is part of the
22
- # Ollama model name, NOT a provider separator here.
23
- - model_name: smart-router
24
- litellm_params:
25
- model: "ollama/minimax-m2.5:cloud"
26
- api_base: http://localhost:11434
27
- - model_name: smart-router
28
- litellm_params:
29
- model: "ollama/minimax-m2.5:cloud"
30
- api_base: http://localhost:11434
31
-
32
- # ── COMPLEX → Moonshot moonshot-v1-auto (matches Lynkr TIER_COMPLEX) ────────
33
- - model_name: smart-router
34
- litellm_params:
35
- model: openai/moonshot-v1-auto
36
- api_base: https://api.moonshot.ai/v1
37
- api_key: os.environ/MOONSHOT_API_KEY
38
-
39
- # ── REASONING → Azure OpenAI gpt-5.2-chat ─────────────────────────────────
40
- - model_name: smart-router
41
- litellm_params:
42
- model: azure/gpt-5.2-chat
43
- api_base: os.environ/AZURE_OPENAI_ENDPOINT
44
- api_key: os.environ/AZURE_OPENAI_API_KEY
45
- api_version: "2024-12-01-preview"
46
-
47
- # ── Direct aliases (for targeted calls outside the benchmark) ─────────────
48
- - model_name: ollama-minimax
49
- litellm_params:
50
- model: "ollama/minimax-m2.5:cloud"
51
- api_base: http://localhost:11434
52
-
53
- - model_name: moonshot-kimi-k2
54
- litellm_params:
55
- model: openai/moonshot-v1-auto
56
- api_base: https://api.moonshot.ai/v1
57
- api_key: os.environ/MOONSHOT_API_KEY
58
-
59
- - model_name: azure-gpt5
60
- litellm_params:
61
- model: azure/gpt-5.2-chat
62
- api_base: os.environ/AZURE_OPENAI_ENDPOINT
63
- api_key: os.environ/AZURE_OPENAI_API_KEY
64
- api_version: "2024-12-01-preview"
65
-
66
- router_settings:
67
- routing_strategy: cost-based-routing
68
- # Fallback: if smart-router fails on one deployment, try the next
69
- fallbacks:
70
- - smart-router:
71
- - ollama-minimax
72
- - moonshot-kimi-k2
73
- - azure-gpt5
74
- num_retries: 2
75
- timeout: 90
76
-
77
- litellm_settings:
78
- drop_params: true
79
- use_responses_api: false
80
- return_response_headers: true
81
- success_callback: []
82
- failure_callback: []
83
-
84
- general_settings:
85
- master_key: sk-1234 # change this
86
- port: 8082
@@ -1,48 +0,0 @@
1
- # ─── Lynkr Benchmark Config ───────────────────────────────────────────────────
2
- # Multi-provider tier routing: Ollama → Moonshot → Azure OpenAI
3
- # Copy to .env and fill in your credentials.
4
-
5
- PORT=8081
6
-
7
- # ── Ollama (local, free) ───────────────────────────────────────────────────────
8
- OLLAMA_ENDPOINT=http://localhost:11434
9
- OLLAMA_MODEL=qwen2.5-coder:7b
10
- OLLAMA_TIMEOUT_MS=120000
11
- OLLAMA_EMBEDDINGS_MODEL=nomic-embed-text
12
- OLLAMA_EMBEDDINGS_ENDPOINT=http://localhost:11434/api/embeddings
13
-
14
- # ── Azure OpenAI ───────────────────────────────────────────────────────────────
15
- AZURE_OPENAI_ENDPOINT=https://YOUR-RESOURCE.openai.azure.com
16
- AZURE_OPENAI_API_KEY=your-azure-openai-key
17
- AZURE_OPENAI_DEPLOYMENT=gpt-4o
18
- AZURE_OPENAI_API_VERSION=2024-08-01-preview
19
-
20
- # ── Moonshot (Kimi) ────────────────────────────────────────────────────────────
21
- MOONSHOT_API_KEY=your-moonshot-api-key
22
- MOONSHOT_ENDPOINT=https://api.moonshot.ai/v1/chat/completions
23
- MOONSHOT_MODEL=kimi-k2-turbo-preview
24
-
25
- # ── Primary provider (Lynkr uses this when no tier matches) ───────────────────
26
- # Set to whichever you want as the default fallback
27
- MODEL_PROVIDER=azure-openai
28
-
29
- # ── Tier Routing ───────────────────────────────────────────────────────────────
30
- # SIMPLE → Ollama local (free)
31
- # MEDIUM → Moonshot Kimi (cheap, fast)
32
- # COMPLEX → Azure OpenAI GPT-4o (powerful)
33
- # REASONING→ Azure OpenAI o3-mini (best reasoning)
34
- TIER_SIMPLE=ollama:minimax-m2.5:cloud
35
- TIER_MEDIUM=ollama:minimax-m2.5:cloud
36
- TIER_COMPLEX=moonshot:kimi-k2.6
37
- TIER_REASONING=azure-openai:gpt-5.2-chat
38
-
39
- # ── Token Optimisations (these are what LiteLLM/Portkey don't have) ────────────
40
- SMART_TOOL_SELECTION=true
41
- PROMPT_CACHE_ENABLED=true
42
- SEMANTIC_CACHE_ENABLED=true
43
- SEMANTIC_CACHE_THRESHOLD=0.95
44
- HISTORY_COMPRESSION_ENABLED=true
45
- TOOL_INJECTION_ENABLED=false
46
-
47
- # ── Optional: make routing decisions visible in responses ──────────────────────
48
- LYNKR_VISIBLE_ROUTING=true
@@ -1,60 +0,0 @@
1
- {
2
- "_comment": "Portkey Gateway Config — multi-provider conditional routing",
3
- "_note": "Portkey has NO automatic complexity detection. This config uses max_tokens as a proxy for complexity. For real tier routing pass x-portkey-metadata: { 'tier': 'simple|medium|complex|reasoning' } from your client.",
4
-
5
- "strategy": {
6
- "mode": "conditional"
7
- },
8
-
9
- "conditions": [
10
- {
11
- "_comment": "SIMPLE — short requests (max_tokens <= 256) → Ollama",
12
- "condition": {
13
- "query.max_tokens": { "$lte": 256 }
14
- },
15
- "target": {
16
- "provider": "ollama",
17
- "customHost": "http://localhost:11434",
18
- "override_params": {
19
- "model": "qwen2.5-coder:7b"
20
- }
21
- }
22
- },
23
- {
24
- "_comment": "MEDIUM — metadata tier=medium → Moonshot",
25
- "condition": {
26
- "metadata.tier": { "$eq": "medium" }
27
- },
28
- "target": {
29
- "provider": "openai",
30
- "apiKey": "{{MOONSHOT_API_KEY}}",
31
- "baseURL": "https://api.moonshot.ai/v1",
32
- "override_params": {
33
- "model": "moonshot-v1-8k"
34
- }
35
- }
36
- },
37
- {
38
- "_comment": "REASONING — metadata tier=reasoning → Azure OpenAI o3-mini",
39
- "condition": {
40
- "metadata.tier": { "$eq": "reasoning" }
41
- },
42
- "target": {
43
- "provider": "azure-openai",
44
- "apiKey": "{{AZURE_OPENAI_API_KEY}}",
45
- "resourceName": "YOUR-RESOURCE",
46
- "deploymentId": "o3-mini",
47
- "apiVersion": "2024-12-01-preview"
48
- }
49
- }
50
- ],
51
-
52
- "default": {
53
- "_comment": "COMPLEX — everything else → Azure OpenAI GPT-4o",
54
- "provider": "azure-openai",
55
- "apiKey": "{{AZURE_OPENAI_API_KEY}}",
56
- "resourceName": "YOUR-RESOURCE",
57
- "deploymentId": "gpt-4o",
58
- "apiVersion": "2024-08-01-preview"
59
- }
60
- }
@@ -1,23 +0,0 @@
1
- #!/bin/bash
2
- # Run Portkey local AI Gateway with provider credentials injected
3
-
4
- docker run -d \
5
- --name portkey-gateway \
6
- -p 8083:8787 \
7
- -e AZURE_OPENAI_API_KEY="${AZURE_OPENAI_API_KEY}" \
8
- -e MOONSHOT_API_KEY="${MOONSHOT_API_KEY}" \
9
- portkeyai/gateway:latest
10
-
11
- echo "Portkey gateway running on http://localhost:8083"
12
- echo ""
13
- echo "To use Azure OpenAI directly (no tier routing):"
14
- echo " curl http://localhost:8083/v1/chat/completions \\"
15
- echo " -H 'x-portkey-provider: azure-openai' \\"
16
- echo " -H 'x-portkey-api-key: \$PORTKEY_API_KEY' \\"
17
- echo " -H 'x-portkey-azure-resource-name: YOUR-RESOURCE' \\"
18
- echo " -H 'x-portkey-azure-deployment-id: gpt-4o' \\"
19
- echo " -H 'x-portkey-azure-api-version: 2024-08-01-preview' \\"
20
- echo " -d '{\"model\": \"gpt-4o\", \"messages\": [...]}'"
21
- echo ""
22
- echo "To use conditional routing config, pass:"
23
- echo " -H 'x-portkey-config: <base64-encoded portkey-config.json>'"