Skip to content

Commit 2f6319b

Browse files
vishalveerareddy123vishal veerareddyclaude
authored
feat: Adds intelligent tiered model routing (#47)
* chore: move dockerode to optionalDependencies, wrap require Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Added tiered System * Added docs * Fix IDE client tool filtering and Codex CLI tool mappings - Fix Codex Bash mapping: shell_command → shell (array format for command) - Add missing Codex mappings: TodoWrite → update_plan, WebSearch → web_search - Add two-layer tool filtering for IDE clients: Layer 1: IDE_SAFE_TOOLS removes AskUserQuestion (can't work through proxy) Layer 2: CLIENT_TOOL_MAPPINGS per-client filter ensures each client only sees tools it supports (e.g. Codex gets 8, Claude Code gets 14) - Add tool name mapping to chat/completions response paths (streaming + non-streaming) - Add missing Claude Code tools: MultiEdit, LS, NotebookRead - Inject filtered tools in openai-router.js before orchestrator call to prevent providers from injecting full STANDARD_TOOLS Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Demote verbose operational logs from info→debug, fix tier config crash - Demote 22 info→debug in openai-router.js (request previews, tool injection, streaming chunks, intermediate conversions) - Demote 39 info→debug in databricks.js (tool injection, request construction, response parsing across all providers) - Clean up orchestrator/index.js: consolidate Ollama conversational check (6→1 log), headroom compression (4→1), tool execution mode (4→1); remove 4 console.log artifacts and [CONTEXT_FLOW] scaffolding - Fix tier config: change hard throw to graceful warn when TIER_* env vars missing (was crashing CI) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Add pino-roll file logging and update production docs Adds optional persistent log file rotation via pino-roll (LOG_FILE_ENABLED=true) and expands the Structured Logging section in production.md with file logging config, log level philosophy, and querying examples. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Sync Dockerfile, docker-compose.yml, and .env.example with all env vars Adds missing sections to all config files: file logging (LOG_FILE_*), rate limiting, policy, agents, token optimization, smart tool selection, prompt/semantic cache, tiered routing, and provider configs (LM Studio, Z.AI, Vertex AI). Adds /app/logs volume for persistent log rotation. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * remove unused * Added gpt * Added latest ollama support * increased token limit * Fix duplicate tool calls * feat: add Moonshot AI (Kimi) provider, fix routing bugs, update docs - Add Moonshot AI as first-class provider (invokeMoonshot, config, orchestrator, provider discovery) - Fix stop_reason detection: check tool_calls presence instead of finish_reason string - Fix streaming format mismatch: force non-streaming for OpenAI-format providers - Fix reasoning content handling: use content field, fallback to reasoning_content - Fix orchestrator double-conversion for Moonshot responses - Fix force-local routing to respect TIER_SIMPLE config instead of hardcoding Ollama - Remove dead code: determineProviderSync (unused sync routing fallback) - Update routing docs: clear precedence hierarchy for TIER_* vs MODEL_PROVIDER vs PREFER_OLLAMA - Add comprehensive Moonshot documentation across all doc files - Add Moonshot to model-tiers.json (kimi-k2-thinking for REASONING tier) Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: update tests to use determineProviderSmart after removing determineProviderSync Replace all determineProviderSync() calls in tests with async determineProviderSmart() since the sync function was removed as dead code. Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * fix: resolve undefined variable errors (logger in router, clean in orchestrator) - Add missing logger require in src/api/router.js (used in streaming error handling) - Fix clean.model → cleanPayload.model in orchestrator hybrid mode response Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> * Result --------- Co-authored-by: vishal veerareddy <vishalveera.reddy@servicenow.com> Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
1 parent 4562795 commit 2f6319b

61 files changed

Lines changed: 5643 additions & 1557 deletions

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

.claude/settings.local.json

Lines changed: 12 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -77,7 +77,18 @@
7777
"Bash(kill:*)",
7878
"Bash(grep:*)",
7979
"WebFetch(domain:opencode.ai)",
80-
"Bash(find:*)"
80+
"Bash(find:*)",
81+
"WebFetch(domain:www.databricks.com)",
82+
"WebFetch(domain:docs.databricks.com)",
83+
"Bash(env:*)",
84+
"Bash(DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node --test:*)",
85+
"Bash(DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com node:*)",
86+
"Bash(gh pr list:*)",
87+
"Bash(gh pr diff:*)",
88+
"Bash(PREFER_OLLAMA=true node:*)",
89+
"Bash(DATABRICKS_API_KEY=test-key DATABRICKS_API_BASE=http://test.com MODEL_PROVIDER=azure-openai AZURE_OPENAI_ENDPOINT=https://test.openai.azure.com AZURE_OPENAI_API_KEY=test-key node:*)",
90+
"Bash(git stash:*)",
91+
"WebFetch(domain:docs.ollama.com)"
8192
],
8293
"deny": [],
8394
"ask": []

.env.example

Lines changed: 242 additions & 271 deletions
Large diffs are not rendered by default.

Dockerfile

Lines changed: 108 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -56,7 +56,7 @@ COPY --from=build --chown=node:node /app/index.js /app/package.json ./
5656
COPY --from=build --chown=node:node /app/node_modules ./node_modules
5757
COPY --from=build --chown=node:node /app/src ./src
5858

59-
VOLUME ["/app/data"]
59+
VOLUME ["/app/data", "/app/logs"]
6060

6161
EXPOSE 8081
6262

@@ -75,17 +75,27 @@ ENV MODEL_PROVIDER="databricks" \
7575
LOG_LEVEL="info" \
7676
WORKSPACE_ROOT="/workspace" \
7777
WEB_SEARCH_ENDPOINT="http://searxng:8888/search" \
78-
NODE_ENV="production"
78+
NODE_ENV="production" \
79+
REQUEST_JSON_LIMIT="1gb" \
80+
SESSION_DB_PATH="/app/data/sessions.db"
81+
82+
# File Logging (persistent logs with pino-roll rotation)
83+
ENV LOG_FILE_ENABLED="false" \
84+
LOG_FILE_PATH="/app/logs/lynkr.log" \
85+
LOG_FILE_LEVEL="debug" \
86+
LOG_FILE_FREQUENCY="daily" \
87+
LOG_FILE_MAX_FILES="14"
7988

8089
# Databricks Configuration (default provider)
8190
ENV DATABRICKS_API_BASE="https://example.cloud.databricks.com" \
8291
DATABRICKS_API_KEY="replace-with-databricks-pat"
8392

84-
# Ollama Configuration (for hybrid routing)
93+
# Ollama Configuration (for tier-based routing)
8594
# Recommended models: llama3.1:8b, llama3.2, qwen2.5:14b, mistral:7b-instruct
86-
ENV PREFER_OLLAMA="false" \
87-
OLLAMA_ENDPOINT="http://localhost:11434" \
95+
# Configure via TIER_* env vars: TIER_SIMPLE=ollama:llama3.2
96+
ENV OLLAMA_ENDPOINT="http://localhost:11434" \
8897
OLLAMA_MODEL="llama3.1:8b" \
98+
OLLAMA_TIMEOUT_MS="120000" \
8999
OLLAMA_MAX_TOOLS_FOR_ROUTING="3" \
90100
OLLAMA_EMBEDDINGS_MODEL="nomic-embed-text" \
91101
OLLAMA_EMBEDDINGS_ENDPOINT="http://localhost:11434/api/embeddings"
@@ -99,45 +109,99 @@ ENV OPENROUTER_API_KEY="" \
99109
OPENROUTER_MAX_TOOLS_FOR_ROUTING="15"
100110

101111
# Azure OpenAI Configuration (optional)
102-
# IMPORTANT: Set full endpoint URL including deployment path
103-
# Example: https://your-resource.openai.azure.com/openai/deployments/YOUR-DEPLOYMENT/chat/completions?api-version=2025-01-01-preview
104-
# Deployment options: gpt-4o, gpt-4o-mini, gpt-5-chat, o1-preview, o3-mini
105112
ENV AZURE_OPENAI_ENDPOINT="" \
106113
AZURE_OPENAI_API_KEY="" \
107-
AZURE_OPENAI_DEPLOYMENT="gpt-4o"
114+
AZURE_OPENAI_DEPLOYMENT="gpt-4o" \
115+
AZURE_OPENAI_API_VERSION="2024-08-01-preview"
108116

109117
# Hybrid Routing & Fallback Configuration
110-
# Options: databricks, azure-openai, azure-anthropic, openrouter, bedrock, openai
111-
# Note: Local providers (ollama, llamacpp, lmstudio) cannot be used as fallback
112118
ENV FALLBACK_ENABLED="true" \
113119
FALLBACK_PROVIDER="databricks"
114120

115121
# Azure Anthropic Configuration (optional)
116122
ENV AZURE_ANTHROPIC_ENDPOINT="" \
117-
AZURE_ANTHROPIC_API_KEY=""
123+
AZURE_ANTHROPIC_API_KEY="" \
124+
AZURE_ANTHROPIC_VERSION="2023-06-01"
118125

119126
# AWS Bedrock Configuration (optional)
120-
# Supports Claude, Titan, Llama, Jurassic, Cohere, Mistral models
121127
ENV AWS_BEDROCK_API_KEY="" \
122128
AWS_BEDROCK_REGION="us-east-1" \
123129
AWS_BEDROCK_MODEL_ID="anthropic.claude-3-5-sonnet-20241022-v2:0"
124130

125-
# llama.cpp Configuration (optional - for local GGUF models)
131+
# llama.cpp Configuration (optional)
126132
ENV LLAMACPP_ENDPOINT="http://localhost:8080" \
127133
LLAMACPP_MODEL="default" \
128134
LLAMACPP_EMBEDDINGS_ENDPOINT="http://localhost:8080/embeddings" \
129135
LLAMACPP_TIMEOUT_MS="120000"
130136

137+
# LM Studio Configuration (optional)
138+
ENV LMSTUDIO_ENDPOINT="http://localhost:1234" \
139+
LMSTUDIO_MODEL="default" \
140+
LMSTUDIO_TIMEOUT_MS="120000"
141+
131142
# OpenAI Configuration (optional)
132143
ENV OPENAI_API_KEY="" \
133144
OPENAI_MODEL="gpt-4o" \
134145
OPENAI_ENDPOINT="https://api.openai.com/v1/chat/completions"
135146

147+
# Z.AI Configuration (optional)
148+
ENV ZAI_API_KEY="" \
149+
ZAI_ENDPOINT="https://api.z.ai/api/anthropic/v1/messages" \
150+
ZAI_MODEL="GLM-4.7"
151+
152+
# Google Vertex AI Configuration (optional)
153+
ENV VERTEX_API_KEY="" \
154+
VERTEX_MODEL="gemini-2.0-flash"
155+
136156
# Embeddings Provider Override (optional)
137-
# Options: ollama, llamacpp, openrouter, openai
138-
# By default, uses same provider as MODEL_PROVIDER
139157
ENV EMBEDDINGS_PROVIDER=""
140158

159+
# Tool Injection & Suggestion Mode
160+
ENV INJECT_TOOLS_LLAMACPP="true" \
161+
INJECT_TOOLS_OLLAMA="true" \
162+
SUGGESTION_MODE_MODEL="default"
163+
164+
# Rate Limiting
165+
ENV RATE_LIMIT_ENABLED="true" \
166+
RATE_LIMIT_WINDOW_MS="60000" \
167+
RATE_LIMIT_MAX="100" \
168+
RATE_LIMIT_KEY_BY="session"
169+
170+
# Web Search Configuration
171+
ENV WEB_SEARCH_ALLOW_ALL="true" \
172+
WEB_SEARCH_TIMEOUT_MS="10000" \
173+
WEB_FETCH_BODY_PREVIEW_MAX="10000" \
174+
WEB_SEARCH_RETRY_ENABLED="true" \
175+
WEB_SEARCH_MAX_RETRIES="2"
176+
177+
# Policy Configuration
178+
ENV POLICY_MAX_STEPS="20" \
179+
POLICY_MAX_TOOL_CALLS="12" \
180+
POLICY_TOOL_LOOP_THRESHOLD="10" \
181+
POLICY_GIT_ALLOW_PUSH="false" \
182+
POLICY_GIT_ALLOW_PULL="true" \
183+
POLICY_GIT_ALLOW_COMMIT="true" \
184+
POLICY_GIT_REQUIRE_TESTS="false" \
185+
POLICY_GIT_AUTOSTASH="false" \
186+
POLICY_FILE_BLOCKED_PATHS="/.env,.env,/etc/passwd,/etc/shadow" \
187+
POLICY_SAFE_COMMANDS_ENABLED="true"
188+
189+
# Agents Configuration
190+
ENV AGENTS_ENABLED="true" \
191+
AGENTS_MAX_CONCURRENT="10" \
192+
AGENTS_DEFAULT_MODEL="haiku" \
193+
AGENTS_MAX_STEPS="15" \
194+
AGENTS_TIMEOUT="300000"
195+
196+
# Prompt Cache Configuration
197+
ENV PROMPT_CACHE_ENABLED="true" \
198+
PROMPT_CACHE_MAX_ENTRIES="1000" \
199+
PROMPT_CACHE_TTL_MS="300000"
200+
201+
# Semantic Response Cache
202+
ENV SEMANTIC_CACHE_ENABLED="false" \
203+
SEMANTIC_CACHE_THRESHOLD="0.95"
204+
141205
# Production Hardening Defaults
142206
ENV CIRCUIT_BREAKER_FAILURE_THRESHOLD="5" \
143207
CIRCUIT_BREAKER_SUCCESS_THRESHOLD="2" \
@@ -160,6 +224,34 @@ ENV MEMORY_ENABLED="true" \
160224
MEMORY_DEDUP_ENABLED="true" \
161225
MEMORY_DEDUP_LOOKBACK="5"
162226

227+
# Token Optimization
228+
ENV TOKEN_TRACKING_ENABLED="true" \
229+
TOOL_TRUNCATION_ENABLED="true" \
230+
SYSTEM_PROMPT_MODE="dynamic" \
231+
TOOL_DESCRIPTIONS="minimal" \
232+
HISTORY_COMPRESSION_ENABLED="true" \
233+
HISTORY_KEEP_RECENT_TURNS="10" \
234+
HISTORY_SUMMARIZE_OLDER="true" \
235+
TOKEN_BUDGET_WARNING="100000" \
236+
TOKEN_BUDGET_MAX="180000" \
237+
TOKEN_BUDGET_ENFORCEMENT="true"
238+
239+
# Smart Tool Selection
240+
ENV SMART_TOOL_SELECTION_MODE="heuristic" \
241+
SMART_TOOL_SELECTION_TOKEN_BUDGET="2500"
242+
243+
# Hot Reload
244+
ENV HOT_RELOAD_ENABLED="true" \
245+
HOT_RELOAD_DEBOUNCE_MS="1000"
246+
247+
# Tiered Model Routing (optional)
248+
# Format: TIER_<LEVEL>=provider:model
249+
# All 4 tiers must be set to enable tiered routing
250+
# ENV TIER_SIMPLE="ollama:llama3.2" \
251+
# TIER_MEDIUM="openrouter:openai/gpt-4o-mini" \
252+
# TIER_COMPLEX="azure-openai:gpt-4o" \
253+
# TIER_REASONING="azure-openai:gpt-4o"
254+
163255
# Switch to non-root user
164256
USER node
165257

README.md

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -238,7 +238,7 @@ Lynkr supports [ClawdBot](https://github.com/openclaw/openclaw) via its OpenAI-c
238238

239239
### Getting Started
240240
- 📦 **[Installation Guide](documentation/installation.md)** - Detailed installation for all methods
241-
- ⚙️ **[Provider Configuration](documentation/providers.md)** - Complete setup for all 9+ providers
241+
- ⚙️ **[Provider Configuration](documentation/providers.md)** - Complete setup for all 12+ providers
242242
- 🎯 **[Quick Start Examples](documentation/installation.md#quick-start-examples)** - Copy-paste configs
243243

244244
### IDE & CLI Integration
@@ -277,7 +277,7 @@ Lynkr supports [ClawdBot](https://github.com/openclaw/openclaw) via its OpenAI-c
277277

278278
## Key Features Highlights
279279

280-
-**Multi-Provider Support** - 9+ providers including local (Ollama, llama.cpp) and cloud (Bedrock, Databricks, OpenRouter)
280+
-**Multi-Provider Support** - 12+ providers including local (Ollama, llama.cpp) and cloud (Bedrock, Databricks, OpenRouter, Moonshot AI)
281281
-**60-80% Cost Reduction** - Token optimization with smart tool selection, prompt caching, memory deduplication
282282
-**100% Local Option** - Run completely offline with Ollama/llama.cpp (zero cloud dependencies)
283283
-**OpenAI Compatible** - Works with Cursor IDE, Continue.dev, and any OpenAI-compatible client

config/model-tiers.json

Lines changed: 89 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,89 @@
1+
{
2+
"tiers": {
3+
"SIMPLE": {
4+
"description": "Greetings, simple Q&A, confirmations, basic lookups",
5+
"range": [0, 25],
6+
"priority": 1,
7+
"preferred": {
8+
"ollama": ["llama3.2", "gemma2", "phi3", "qwen2.5:7b", "mistral"],
9+
"llamacpp": ["default"],
10+
"lmstudio": ["default"],
11+
"openai": ["gpt-4o-mini", "gpt-3.5-turbo"],
12+
"azure-openai": ["gpt-4o-mini", "gpt-35-turbo"],
13+
"anthropic": ["claude-3-haiku-20240307", "claude-3-5-haiku-20241022"],
14+
"bedrock": ["anthropic.claude-3-haiku-20240307-v1:0", "amazon.nova-lite-v1:0"],
15+
"databricks": ["databricks-claude-haiku-4-5", "databricks-gpt-5-nano"],
16+
"google": ["gemini-2.0-flash", "gemini-1.5-flash"],
17+
"openrouter": ["google/gemini-flash-1.5", "deepseek/deepseek-chat"],
18+
"zai": ["GLM-4-Flash"],
19+
"moonshot": ["kimi-k2-turbo-preview"]
20+
}
21+
},
22+
"MEDIUM": {
23+
"description": "Code reading, simple edits, research, documentation",
24+
"range": [26, 50],
25+
"priority": 2,
26+
"preferred": {
27+
"ollama": ["qwen2.5:32b", "deepseek-coder:33b", "codellama:34b"],
28+
"llamacpp": ["default"],
29+
"lmstudio": ["default"],
30+
"openai": ["gpt-4o", "gpt-4-turbo"],
31+
"azure-openai": ["gpt-4o", "gpt-4"],
32+
"anthropic": ["claude-sonnet-4-20250514", "claude-3-5-sonnet-20241022"],
33+
"bedrock": ["anthropic.claude-3-5-sonnet-20241022-v2:0", "amazon.nova-pro-v1:0"],
34+
"databricks": ["databricks-claude-sonnet-4-5", "databricks-gpt-5-1"],
35+
"google": ["gemini-1.5-pro", "gemini-2.0-pro"],
36+
"openrouter": ["anthropic/claude-3.5-sonnet", "openai/gpt-4o"],
37+
"zai": ["GLM-4.7"],
38+
"moonshot": ["kimi-k2-turbo-preview"]
39+
}
40+
},
41+
"COMPLEX": {
42+
"description": "Multi-file changes, debugging, architecture, refactoring",
43+
"range": [51, 75],
44+
"priority": 3,
45+
"preferred": {
46+
"ollama": ["qwen2.5:72b", "llama3.1:70b", "deepseek-coder-v2:236b"],
47+
"openai": ["o1-mini", "o3-mini", "gpt-4o"],
48+
"azure-openai": ["o1-mini", "gpt-4o"],
49+
"anthropic": ["claude-sonnet-4-20250514", "claude-3-5-sonnet-20241022"],
50+
"bedrock": ["anthropic.claude-3-5-sonnet-20241022-v2:0"],
51+
"databricks": ["databricks-claude-sonnet-4-5", "databricks-gpt-5-1-codex-max"],
52+
"google": ["gemini-2.5-pro", "gemini-1.5-pro"],
53+
"openrouter": ["anthropic/claude-3.5-sonnet", "meta-llama/llama-3.1-405b"],
54+
"zai": ["GLM-4.7"],
55+
"moonshot": ["kimi-k2-turbo-preview"]
56+
}
57+
},
58+
"REASONING": {
59+
"description": "Complex analysis, security audits, novel problems, deep thinking",
60+
"range": [76, 100],
61+
"priority": 4,
62+
"preferred": {
63+
"openai": ["o1", "o1-pro", "o3"],
64+
"azure-openai": ["o1", "o1-pro"],
65+
"anthropic": ["claude-opus-4-20250514", "claude-3-opus-20240229"],
66+
"bedrock": ["anthropic.claude-3-opus-20240229-v1:0"],
67+
"databricks": ["databricks-claude-opus-4-6", "databricks-claude-opus-4-5", "databricks-gpt-5-2"],
68+
"google": ["gemini-2.5-pro"],
69+
"openrouter": ["anthropic/claude-3-opus", "deepseek/deepseek-reasoner", "openai/o1"],
70+
"deepseek": ["deepseek-reasoner", "deepseek-r1"],
71+
"moonshot": ["kimi-k2-thinking", "kimi-k2-turbo-preview"]
72+
}
73+
}
74+
},
75+
"localProviders": {
76+
"ollama": { "free": true, "defaultTier": "SIMPLE" },
77+
"llamacpp": { "free": true, "defaultTier": "SIMPLE" },
78+
"lmstudio": { "free": true, "defaultTier": "SIMPLE" }
79+
},
80+
"providerAliases": {
81+
"azure": "azure-openai",
82+
"aws": "bedrock",
83+
"amazon": "bedrock",
84+
"claude": "anthropic",
85+
"gemini": "google",
86+
"vertex": "google",
87+
"kimi": "moonshot"
88+
}
89+
}

0 commit comments

Comments
 (0)