Skip to content

Commit a81e38a

Browse files
authored
feat: add first-class Kimi provider (#824)
* feat: add first-class Kimi provider * test: parameterize provider environment coverage
1 parent e8053cf commit a81e38a

26 files changed

Lines changed: 1061 additions & 26 deletions

File tree

.env.example

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,10 @@ OPENAI_API_KEY=your_openai_api_key_here
1616
DEEPSEEK_API_KEY=your_deepseek_api_key_here
1717
# DEEPSEEK_BASE_URL=https://api.deepseek.com
1818

19+
# Kimi
20+
KIMI_API_KEY=your_kimi_api_key_here
21+
# KIMI_BASE_URL=https://api.kimi.com/coding/v1
22+
1923
# Ollama — no API key needed, but set base URL if non-default
2024
# OLLAMA_BASE_URL=http://localhost:11434
2125

docs/CLI_REFERENCE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -62,7 +62,7 @@ All three reach the indexing knobs; the LLM-only knobs appear only when docs are
6262

6363
| Flag | Description |
6464
|------|-------------|
65-
| `--provider` | LLM provider: `anthropic`, `openai`, `openrouter`, `gemini`, `deepseek`, `ollama`, `litellm`, `codex_cli`, `opencode`, `mock` |
65+
| `--provider` | LLM provider: `anthropic`, `openai`, `openrouter`, `gemini`, `deepseek`, `kimi`, `ollama`, `litellm`, `codex_cli`, `opencode`, `mock` |
6666
| `--model` | Model name override (e.g., `claude-sonnet-4-6`) |
6767
| `--embedder` | Embedder for semantic search: `gemini`, `openai`, `openrouter`, `ollama`, `mock` (default: auto-detect) |
6868
| `--index-only` | Skip LLM generation. Only parse, build graph, and index git. Free. |

docs/CONFIG.md

Lines changed: 27 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -68,7 +68,7 @@ You can edit this file directly. Changes take effect on the next `init`,
6868

6969
| Key | Default | Meaning |
7070
|-----|---------|---------|
71-
| `provider` | auto-detected | `anthropic`, `openai`, `gemini`, `openrouter`, `deepseek`, `ollama`, `litellm`, `opencode` |
71+
| `provider` | auto-detected | `anthropic`, `openai`, `gemini`, `openrouter`, `deepseek`, `kimi`, `ollama`, `litellm`, `opencode` |
7272
| `model` | provider default | Model identifier passed to the provider |
7373
| `embedder` | `mock` | `openai`, `gemini`, `ollama`, `openrouter`, `mock` |
7474
| `embedding_model` | provider default | Embedding model identifier |
@@ -345,6 +345,29 @@ export DEEPSEEK_API_KEY="..."
345345
repowise init --provider deepseek --model deepseek-chat
346346
```
347347

348+
### Kimi
349+
350+
```bash
351+
export KIMI_API_KEY="..."
352+
repowise init --provider kimi --model kimi-for-coding
353+
```
354+
355+
The default Kimi Code endpoint supports `kimi-for-coding` and
356+
`kimi-for-coding-highspeed`. Repowise automatically uses their required
357+
sampling parameters.
358+
359+
K2.x models use the separate Kimi Open Platform. Supply a key from that
360+
platform together with its OpenAI-compatible endpoint:
361+
362+
```bash
363+
export KIMI_API_KEY="..."
364+
export KIMI_BASE_URL="https://api.moonshot.ai/v1"
365+
repowise init --provider kimi --model kimi-k2.6
366+
```
367+
368+
For K2.x models, Repowise selects the required sampling parameters for
369+
thinking or instant mode.
370+
348371
### Ollama (local, no API key)
349372

350373
```bash
@@ -367,7 +390,7 @@ order:
367390

368391
1. `REPOWISE_PROVIDER` environment variable
369392
2. `provider` in `.repowise/config.yaml`
370-
3. API key env vars: `ANTHROPIC_API_KEY` → `OPENAI_API_KEY` → `OLLAMA_BASE_URL` → `GEMINI_API_KEY`
393+
3. API key env vars: `ANTHROPIC_API_KEY` → `OPENAI_API_KEY` → `OPENROUTER_API_KEY` → `OLLAMA_BASE_URL` → `GEMINI_API_KEY` → `DEEPSEEK_API_KEY` → `KIMI_API_KEY`
371394

372395
---
373396

@@ -418,6 +441,7 @@ The `.repowise/.env` file is gitignored automatically.
418441
| `GEMINI_API_KEY` / `GOOGLE_API_KEY` | Google Gemini API key |
419442
| `OPENROUTER_API_KEY` | OpenRouter API key |
420443
| `DEEPSEEK_API_KEY` | DeepSeek API key |
444+
| `KIMI_API_KEY` | Kimi API key |
421445
| `LITELLM_API_KEY` | LiteLLM proxy key |
422446
| `LITELLM_API_BASE` | LiteLLM proxy base URL |
423447

@@ -430,6 +454,7 @@ The `.repowise/.env` file is gitignored automatically.
430454
| `GEMINI_BASE_URL` | Override the Gemini API base URL |
431455
| `OLLAMA_BASE_URL` | Ollama server URL (default: `http://localhost:11434`) |
432456
| `DEEPSEEK_BASE_URL` | Override the DeepSeek API base URL |
457+
| `KIMI_BASE_URL` | Override the Kimi API base URL |
433458
| `LITELLM_BASE_URL` | Override the LiteLLM proxy base URL |
434459

435460
### Provider and model overrides

docs/USER_GUIDE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -179,7 +179,7 @@ repowise init [PATH]
179179

180180
| Flag | Description |
181181
|------|-------------|
182-
| `--provider` | LLM provider: `anthropic`, `openai`, `openrouter`, `gemini`, `deepseek`, `ollama`, `litellm`, `codex_cli`, `mock`. Auto-detected from env vars if not set. |
182+
| `--provider` | LLM provider: `anthropic`, `openai`, `openrouter`, `gemini`, `deepseek`, `kimi`, `ollama`, `litellm`, `codex_cli`, `mock`. Auto-detected from env vars if not set. |
183183
| `--model` | Model name override (e.g., `claude-sonnet-4-6`, `gpt-5.4-nano`) |
184184
| `--embedder` | Embedder for semantic search: `gemini`, `openai`, `mock`. Auto-detected from env vars. |
185185
| `--index-only` | Skip LLM generation entirely. Only parse, build graph, and index git. Free. |

docs/architecture/ARCHITECTURE.md

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1615,7 +1615,7 @@ Key files:
16151615
Full configuration with defaults (`.repowise/config.yaml`):
16161616

16171617
```yaml
1618-
provider: anthropic # anthropic | openai | openrouter | gemini | deepseek | ollama | litellm | codex_cli | opencode | mock
1618+
provider: anthropic # anthropic | openai | openrouter | gemini | deepseek | kimi | ollama | litellm | codex_cli | opencode | mock
16191619
model: claude-sonnet-4-5 # passed through to the provider
16201620
embedding_provider: anthropic
16211621
embedding_model: voyage-3

packages/cli/src/repowise/cli/commands/init_cmd/command.py

Lines changed: 4 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -108,7 +108,9 @@ def _record_init_outcome(
108108
"symbol_count_bucket": telemetry.bucket_count(getattr(result, "symbol_count", 0) or 0),
109109
}
110110

111-
lang_dist = getattr(getattr(result, "repo_structure", None), "root_language_distribution", None)
111+
lang_dist = getattr(
112+
getattr(result, "repo_structure", None), "root_language_distribution", None
113+
)
112114
if isinstance(lang_dist, dict) and lang_dist:
113115
outcome["top_language"] = max(lang_dist.items(), key=lambda kv: kv[1])[0]
114116

@@ -275,7 +277,7 @@ def _run_generation_phase(
275277
default=None,
276278
help=(
277279
"LLM provider name (anthropic, openai, openrouter, gemini, "
278-
"deepseek, ollama, litellm, codex_cli, opencode, mock)."
280+
"deepseek, kimi, ollama, litellm, codex_cli, opencode, mock)."
279281
),
280282
)
281283
@click.option("--model", default=None, help="Model identifier override.")

packages/cli/src/repowise/cli/commands/workspace_cmd.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -379,6 +379,7 @@ def _resolve_docs_flag(
379379
"GEMINI_API_KEY",
380380
"GOOGLE_API_KEY",
381381
"DEEPSEEK_API_KEY",
382+
"KIMI_API_KEY",
382383
"OLLAMA_BASE_URL",
383384
):
384385
if _os.environ.get(key):

packages/cli/src/repowise/cli/helpers.py

Lines changed: 17 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -610,6 +610,7 @@ def _resolve_base_url(name: str) -> str | None:
610610
"openai": ["OPENAI_BASE_URL"],
611611
"gemini": ["GEMINI_BASE_URL"],
612612
"deepseek": ["DEEPSEEK_BASE_URL"],
613+
"kimi": ["KIMI_BASE_URL"],
613614
"ollama": ["OLLAMA_BASE_URL"],
614615
"litellm": ["LITELLM_BASE_URL", "LITELLM_API_BASE"],
615616
}
@@ -657,6 +658,8 @@ def _resolve_base_url(name: str) -> str | None:
657658
kwargs["api_key"] = os.environ["OPENROUTER_API_KEY"]
658659
elif provider_name == "deepseek" and os.environ.get("DEEPSEEK_API_KEY"):
659660
kwargs["api_key"] = os.environ["DEEPSEEK_API_KEY"]
661+
elif provider_name == "kimi" and os.environ.get("KIMI_API_KEY"):
662+
kwargs["api_key"] = os.environ["KIMI_API_KEY"]
660663
elif provider_name == "litellm" and os.environ.get("LITELLM_API_KEY"):
661664
kwargs["api_key"] = os.environ["LITELLM_API_KEY"]
662665
elif provider_name == "ollama" and os.environ.get("OLLAMA_BASE_URL"):
@@ -718,13 +721,24 @@ def _resolve_base_url(name: str) -> str | None:
718721
if base_url:
719722
kwargs["base_url"] = base_url
720723
return get_provider("deepseek", **kwargs)
724+
if os.environ.get("KIMI_API_KEY") and os.environ["KIMI_API_KEY"].strip():
725+
kwargs = (
726+
{"model": model, "api_key": os.environ["KIMI_API_KEY"]}
727+
if model
728+
else {"api_key": os.environ["KIMI_API_KEY"]}
729+
)
730+
base_url = _resolve_base_url("kimi")
731+
if base_url:
732+
kwargs["base_url"] = base_url
733+
return get_provider("kimi", **kwargs)
721734

722735
raise click.ClickException(
723736
"No provider configured. Use --provider, set REPOWISE_PROVIDER, "
724737
"or set ANTHROPIC_API_KEY / OPENAI_API_KEY / OPENROUTER_API_KEY / "
725738
"OLLAMA_BASE_URL / GEMINI_API_KEY / GOOGLE_API_KEY / DEEPSEEK_API_KEY / "
726-
"LITELLM_API_KEY. Use REPOWISE_PROVIDER=codex_cli to use an authenticated "
727-
"Codex CLI subscription, or REPOWISE_PROVIDER=opencode to use opencode."
739+
"KIMI_API_KEY / LITELLM_API_KEY. Use REPOWISE_PROVIDER=codex_cli to use "
740+
"an authenticated Codex CLI subscription, or REPOWISE_PROVIDER=opencode "
741+
"to use opencode."
728742
)
729743

730744

@@ -760,6 +774,7 @@ def _is_env_var_exists(var_name: str) -> bool:
760774
"openai": ["OPENAI_API_KEY"],
761775
"openrouter": ["OPENROUTER_API_KEY"],
762776
"deepseek": ["DEEPSEEK_API_KEY"],
777+
"kimi": ["KIMI_API_KEY"],
763778
"gemini": ["GEMINI_API_KEY", "GOOGLE_API_KEY"], # Either one
764779
"ollama": ["OLLAMA_BASE_URL"],
765780
"litellm": ["LITELLM_API_KEY"], # May need others depending on backend

packages/cli/src/repowise/cli/ui/provider_selection.py

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,7 @@
2525
"openai": "gpt-5.4-nano",
2626
"anthropic": "claude-sonnet-4-6",
2727
"deepseek": "deepseek-v4-flash",
28+
"kimi": "kimi-for-coding",
2829
"codex_cli": "codex_cli/default",
2930
"opencode": "opencode/default",
3031
"ollama": "llama3.2",
@@ -37,6 +38,7 @@
3738
"openai": "OPENAI_API_KEY",
3839
"anthropic": "ANTHROPIC_API_KEY",
3940
"deepseek": "DEEPSEEK_API_KEY",
41+
"kimi": "KIMI_API_KEY",
4042
"codex_cli": "__CODEX_CLI__",
4143
"opencode": "__OPENCODE_CLI__",
4244
"ollama": "OLLAMA_BASE_URL",
@@ -48,6 +50,7 @@
4850
"openai": "https://platform.openai.com/api-keys",
4951
"anthropic": "https://console.anthropic.com/settings/keys",
5052
"deepseek": "https://platform.deepseek.com/api_keys",
53+
"kimi": "https://www.kimi.com/code/console",
5154
"codex_cli": "https://developers.openai.com/codex/cli",
5255
"opencode": "https://opencode.ai",
5356
"ollama": "https://ollama.com/download",

packages/core/src/repowise/core/analysis/health/refactoring/llm/enrich.py

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -167,6 +167,7 @@ def _env(name: str) -> str | None:
167167
"openai": "OPENAI_API_KEY",
168168
"openrouter": "OPENROUTER_API_KEY",
169169
"deepseek": "DEEPSEEK_API_KEY",
170+
"kimi": "KIMI_API_KEY",
170171
"gemini": "GEMINI_API_KEY",
171172
"litellm": "LITELLM_API_KEY",
172173
}

0 commit comments

Comments
 (0)