# ADR-0026: Kimi (Moonshot AI) built-in provider **Status:** Accepted — 2026-07-18 ## Context Moonshot AI's Kimi models (Kimi K2, `moonshot-v1-*`, and the vision variants) are served over an OpenAI-compatible Chat Completions API at `https://api.moonshot.ai/v1` (`https://api.moonshot.cn/v1` for China), authenticated with a bearer key. mort wants Kimi as a first-class failover tier, so `kimi/kimi-k2-...` should parse, chain, and alias out of the box with a dedicated `KIMI_API_KEY` env var — the same ergonomics as `openai`, `anthropic`, and `google`. Two tensions: - The wire protocol is byte-for-byte OpenAI Chat Completions, so a hand-rolled client would duplicate `provider/openai` for zero gain (ADR-0007 forbids it), exactly as ADR-0015 found for llama-swap. - The README's current stance is that arbitrary OpenAI-compatible endpoints (Groq, Together, …) are *consumer-registered*, not baked in. Blessing Kimi as a built-in is a deliberate, narrow exception justified by the north star: mort names Kimi directly in its tiers, and a built-in with `KIMI_API_KEY` keeps mort's config free of boilerplate `openai.New(WithName/WithBaseURL)` wiring. ## Decision - **No new package.** The `kimi` built-in and `kimi://` DSN scheme both construct `provider/openai` pointed at the Moonshot base URL — the chat path inherits every openai feature/fix automatically (like llama-swap's chat). - The built-in reads its key through the registry's injected `envLookup` (`KIMI_API_KEY` only — no `MOONSHOT_API_KEY` alias, per the project owner) so it stays hermetically testable via `WithEnvLookup`. - **`WithAPIKey` is passed unconditionally, even when empty.** `openai.New` defaults its key to `OPENAI_API_KEY`; without an explicit override an unset `KIMI_API_KEY` would silently authenticate Kimi with the OpenAI key. Passing the (possibly empty) lookup result severs that fallthrough. - New `openai.WithAPIKeyName("KIMI_API_KEY")` option customizes only the synthetic-401 missing-key hint (default `OPENAI_API_KEY`), so a keyless kimi call tells the operator the *right* variable to set. - The default endpoint is the international host (`kimiBaseURL`). The China endpoint (or any other host) is reachable with a `kimi://` DSN, e.g. `LLM_KCN=kimi://token@api.moonshot.cn/v1`. The `kimi://` scheme is an OpenAI-compatible target labeled `kimi` with the same key-name hint; it is intentionally near-identical to `openai://` — its value is a clear name in specs and error reporting. ## Consequences - `kimi/` is first-class in Parse, chains, aliases, and health/failover with no consumer wiring; model ids pass through verbatim (no catalog). - Chat, streaming, tools, and structured output ride the openai client. Image *inputs* work at the client level but only the Moonshot vision models accept them (matrix footnote ³). - `WithAPIKeyName` is a small, generally useful addition to `provider/openai`; the default preserves existing behavior for every other openai-compat target. - Blessing one third-party endpoint as a built-in sets a precedent; future ones should clear the same bar (a named consumer needs it in-config), not be added reflexively — `RegisterProvider`/`LLM_*` remain the path for the rest.