feat(providers): add DeepSeek and Kimi support#208
Conversation
Both providers use OpenAI-compatible APIs and route through OpenAIProvider with custom base URLs. No new code needed beyond registry wiring. Closes #207 Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
📝 WalkthroughWalkthroughAdds DeepSeek and Kimi/Moonshot as runtime-supported providers: registers them in the provider registry, exposes their models in the model list, wires optional provider config and env vars, and updates documentation to mention availability via configurable Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes Possibly related PRs
Poem
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. ✨ Finishing Touches
🧪 Generate unit tests (beta)
Comment |
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/config/types.rs`:
- Around line 1134-1137: The ProvidersConfig struct was extended with deepseek
and kimi fields but apply_provider_env_overrides() in src/config/mod.rs lacks
handling for their environment overrides; update apply_provider_env_overrides()
to mirror the existing provider pattern (as used for anthropic/openai/groq) by
checking ProvidersConfig.deepseek and ProvidersConfig.kimi Option values and,
when present, overwrite their ProviderConfig.api_key, api_base, and model from
env vars ZEPTOCLAW_PROVIDERS_DEEPSEEK_API_KEY,
ZEPTOCLAW_PROVIDERS_DEEPSEEK_API_BASE, ZEPTOCLAW_PROVIDERS_DEEPSEEK_MODEL and
ZEPTOCLAW_PROVIDERS_KIMI_API_KEY, ZEPTOCLAW_PROVIDERS_KIMI_API_BASE,
ZEPTOCLAW_PROVIDERS_KIMI_MODEL respectively (only set if the env var is
defined), using the same helper/get-env logic used for the other providers.
ℹ️ Review info
Configuration used: defaults
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (5)
CLAUDE.mdsrc/channels/model_switch.rssrc/config/types.rssrc/providers/mod.rssrc/providers/registry.rs
Add ZEPTOCLAW_PROVIDERS_DEEPSEEK_{API_KEY,API_BASE,MODEL} and
ZEPTOCLAW_PROVIDERS_KIMI_{API_KEY,API_BASE,MODEL} environment variable
overrides in apply_provider_env_overrides(), following the existing
pattern for other providers. Updates the test to cover both new
providers.
Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@src/config/mod.rs`:
- Around line 427-457: The helper functions get_api_key() and get_api_base() are
missing DeepSeek and Kimi entries and their docstrings are out of date; update
the helper implementations and their docstrings to include DeepSeek and Kimi
(matching the provider names used in provider_config_by_name()), or
alternatively add a clear comment in the helpers stating they are only used for
unit tests and not for runtime provider selection; ensure you reference the
exact helper function names get_api_key() and get_api_base() and align their
returned/handled provider names with the registry implementation
(provider_config_by_name()) so tests and docs are consistent.
| // DeepSeek | ||
| if let Ok(val) = std::env::var("ZEPTOCLAW_PROVIDERS_DEEPSEEK_API_KEY") { | ||
| let provider = self | ||
| .providers | ||
| .deepseek | ||
| .get_or_insert_with(ProviderConfig::default); | ||
| provider.api_key = Some(val); | ||
| } | ||
| if let Ok(val) = std::env::var("ZEPTOCLAW_PROVIDERS_DEEPSEEK_API_BASE") { | ||
| let provider = self | ||
| .providers | ||
| .deepseek | ||
| .get_or_insert_with(ProviderConfig::default); | ||
| provider.api_base = Some(val); | ||
| } | ||
|
|
||
| // Kimi (Moonshot AI) | ||
| if let Ok(val) = std::env::var("ZEPTOCLAW_PROVIDERS_KIMI_API_KEY") { | ||
| let provider = self | ||
| .providers | ||
| .kimi | ||
| .get_or_insert_with(ProviderConfig::default); | ||
| provider.api_key = Some(val); | ||
| } | ||
| if let Ok(val) = std::env::var("ZEPTOCLAW_PROVIDERS_KIMI_API_BASE") { | ||
| let provider = self | ||
| .providers | ||
| .kimi | ||
| .get_or_insert_with(ProviderConfig::default); | ||
| provider.api_base = Some(val); | ||
| } |
There was a problem hiding this comment.
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Helper definitions =="
rg -n --type=rust -C5 'fn get_api_key|fn get_api_base' src/config/mod.rs
echo
echo "== Call sites relying on helpers =="
rg -nP --type=rust -C3 '\bget_api_key\s*\(|\bget_api_base\s*\(' src
echo
echo "== DeepSeek/Kimi references in config module =="
rg -n --type=rust -C2 'providers\.(deepseek|kimi)' src/config/mod.rsRepository: qhkm/zeptoclaw
Length of output: 3368
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Full get_api_key implementation =="
rg -n --type=rust -A20 'pub fn get_api_key' src/config/mod.rs | head -40
echo
echo "== Full get_api_base implementation =="
rg -n --type=rust -A30 'pub fn get_api_base' src/config/mod.rs | head -50
echo
echo "== Calls to get_api_key outside tests =="
rg -n --type=rust 'get_api_key' src --glob='!**/*test*' --glob='!**/tests/**'
echo
echo "== Calls to get_api_base outside tests =="
rg -n --type=rust 'get_api_base' src --glob='!**/*test*' --glob='!**/tests/**'
echo
echo "== Providers struct definition =="
rg -n --type=rust -A50 'struct Providers' src/config/mod.rs | head -70Repository: qhkm/zeptoclaw
Length of output: 2484
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Providers struct definition =="
rg -n --type=rust -B2 -A30 'pub struct Providers' src/config/mod.rs
echo
echo "== Check actual usage of helpers in codebase =="
git grep -n 'get_api_key\|get_api_base' src/ || echo "Not found in source (only in tests)"
echo
echo "== Check if deepseek/kimi wiring is complete =="
echo "Checking environment variable env overrides..."
rg -n --type=rust 'ZEPTOCLAW_PROVIDERS_DEEPSEEK|ZEPTOCLAW_PROVIDERS_KIMI' src/config/mod.rs
echo
echo "=== Provider Config model field support ==="
rg -n --type=rust -B2 -A2 'pub model:' src/config/mod.rsRepository: qhkm/zeptoclaw
Length of output: 91
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Find Providers struct =="
rg -n 'struct Providers' src/config/mod.rs -A 40
echo
echo "== Check all provider fields =="
rg -n 'pub (openrouter|anthropic|openai|gemini|zhipu|groq|deepseek|kimi|vllm):' src/config/mod.rs
echo
echo "== Search for actual usage patterns outside tests =="
rg -n 'get_api_key|get_api_base' src --type=rust | grep -v test | grep -v '#\['
echo
echo "== Check if these helpers are used in onboarding/cli =="
rg -rn 'get_api_key\|get_api_base' src/cli/ src/onboard/
echo
echo "== Verify the complete get_api_base to see all providers =="
sed -n '1158,1220p' src/config/mod.rsRepository: qhkm/zeptoclaw
Length of output: 85
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Direct file read: Providers struct and fields =="
sed -n '1,150p' src/config/mod.rs | grep -A 50 'struct Providers'
echo
echo "== Complete get_api_base function =="
sed -n '1158,1230p' src/config/mod.rs
echo
echo "== Line count and structure =="
wc -l src/config/mod.rsRepository: qhkm/zeptoclaw
Length of output: 109
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Check file exists and size =="
ls -lh src/config/mod.rs
echo
echo "== Read full file to understand structure =="
cat src/config/mod.rs | head -100
echo
echo "=== Search for Providers struct ==="
cat src/config/mod.rs | grep -n "struct Providers" -A 30 || trueRepository: qhkm/zeptoclaw
Length of output: 3754
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Search for all provider fields =="
grep -n "deepseek\|kimi\|openrouter\|anthropic" src/config/mod.rs | head -30
echo
echo "== Context around line 1136 (get_api_key) =="
sed -n '1130,1160p' src/config/mod.rs
echo
echo "== Context around line 1158 (get_api_base) =="
sed -n '1158,1210p' src/config/mod.rs
echo
echo "== Check what uses these helper functions =="
grep -n "\.get_api_key()\|\.get_api_base()" src/config/mod.rs src/**/*.rs 2>/dev/null || trueRepository: qhkm/zeptoclaw
Length of output: 4894
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Verify: ANY usage of get_api_key or get_api_base across entire codebase =="
git grep -n 'get_api_key\|get_api_base' 2>/dev/null || echo "None found outside src/config/mod.rs tests"
echo
echo "== Check if DeepSeek/Kimi wiring is otherwise complete =="
echo "Model override env vars:"
grep -n 'ZEPTOCLAW_PROVIDERS_DEEPSEEK_MODEL\|ZEPTOCLAW_PROVIDERS_KIMI_MODEL' src/config/mod.rs
echo
echo "== Confirm test coverage for model overrides =="
sed -n '1745,1760p' src/config/mod.rs
echo
echo "== Check comment/docstring accuracy =="
sed -n '1133,1136p' src/config/mod.rs
sed -n '1157,1158p' src/config/mod.rsRepository: qhkm/zeptoclaw
Length of output: 1883
🏁 Script executed:
#!/bin/bash
set -euo pipefemporal
echo "== Check if these helpers are exported/part of public API =="
rg -n 'pub fn get_api_key|pub fn get_api_base' src/config/mod.rs
echo
echo "== Check if they might be used transitively through imports =="
grep -n 'use.*Config' src/**/*.rs | head -20
echo
echo "== Verify no runtime usage by checking provider selection logic =="
rg -n 'get_or_insert_with.*ProviderConfig' src/config/mod.rs | head -10
echo
echo "== Look for actual runtime provider selection logic =="
rg -n 'providers\.(deepseek|kimi)' src --type=rust | grep -v test | grep -v 'get_or_insert'Repository: qhkm/zeptoclaw
Length of output: 115
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Check if these helpers are exported/part of public API =="
rg -n 'pub fn get_api_key|pub fn get_api_base' src/config/mod.rs
echo
echo "== Check if they might be used transitively through imports =="
grep -n 'use.*Config' src/**/*.rs 2>/dev/null | head -20 || true
echo
echo "== Verify no runtime usage by checking provider selection logic =="
rg -n 'get_or_insert_with.*ProviderConfig' src/config/mod.rs | head -10
echo
echo "== Look for actual runtime provider selection logic =="
rg -n 'providers\.(deepseek|kimi)' src --type=rust | grep -v test | grep -v 'get_or_insert'Repository: qhkm/zeptoclaw
Length of output: 2857
🏁 Script executed:
#!/bin/bash
set -euo pipefail
echo "== Check src/providers/registry.rs to understand actual provider selection =="
sed -n '130,145p' src/providers/registry.rs
echo
echo "== Context around provider registry =="
sed -n '120,150p' src/providers/registry.rs
echo
echo "== Verify complete docstring for get_api_key =="
sed -n '1133,1156p' src/config/mod.rsRepository: qhkm/zeptoclaw
Length of output: 2740
Helpers get_api_key() and get_api_base() are outdated, but this does not impact runtime provider selection.
The docstring at line 1135 and the helper implementation omit DeepSeek and Kimi, but these functions are only used in unit tests and never called in production code. Actual provider selection at runtime uses provider_config_by_name() in src/providers/registry.rs, which correctly includes both new providers. Consider updating the helpers' docstrings and implementation to include DeepSeek and Kimi for consistency with the registered provider list, or clarify their intended scope in comments.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@src/config/mod.rs` around lines 427 - 457, The helper functions get_api_key()
and get_api_base() are missing DeepSeek and Kimi entries and their docstrings
are out of date; update the helper implementations and their docstrings to
include DeepSeek and Kimi (matching the provider names used in
provider_config_by_name()), or alternatively add a clear comment in the helpers
stating they are only used for unit tests and not for runtime provider
selection; ensure you reference the exact helper function names get_api_key()
and get_api_base() and align their returned/handled provider names with the
registry implementation (provider_config_by_name()) so tests and docs are
consistent.
Summary
https://api.deepseek.com/v1) and Kimi/Moonshot AI (https://api.moonshot.cn/v1) as OpenAI-compatible providersOpenAIProviderwith custom base URLs — no new provider code needed/model list: DeepSeek V3, DeepSeek R1, Kimi 128K, Kimi 32KChanges
src/providers/registry.rs— AddProviderSpecentries +provider_config_by_namearmssrc/providers/mod.rs— Add toRUNTIME_SUPPORTED_PROVIDERSsrc/config/types.rs— Adddeepseekandkimifields toProvidersConfigsrc/channels/model_switch.rs— Add known models + update valid providers list in testsCLAUDE.md— Update provider docsConfiguration
{ "providers": { "deepseek": { "api_key": "sk-xxx", "model": "deepseek-chat" }, "kimi": { "api_key": "sk-xxx", "model": "moonshot-v1-128k" } } }Or via env vars:
Test plan
cargo fmt -- --checkpassescargo clippy -- -D warningspassescargo test --lib— 2670 tests pass (including registry sync test)Closes #207
🤖 Generated with Claude Code
Summary by CodeRabbit
Documentation
New Features