|
| 1 | +# Adding a model when the vendor runs two platforms: split, don't overload |
| 2 | + |
| 3 | +## The problem, in one line |
| 4 | + |
| 5 | +"新增 kimi-k3" came with an official config snippet pointing at `api.moonshot.cn`, but |
| 6 | +ccmr's existing kimi provider (and the user's live config) point at `api.moonshot.ai` — |
| 7 | +and the two platforms' API keys do not interoperate. |
| 8 | + |
| 9 | +## The approach |
| 10 | + |
| 11 | +1. **Source the specs before touching config.** The pasted snippet gave only model_id and |
| 12 | + base_url. Context window and max output came from the vendor's own docs, quoted |
| 13 | + verbatim: `platform.kimi.com/docs/pricing/chat-k3` (1M context) and |
| 14 | + `docs/guide/kimi-k3-quickstart` ("`max_completion_tokens` 默认 131072,最大可设置为 |
| 15 | + 1048576"). A summarizing fetch had garbled this once — re-fetch asking for the exact |
| 16 | + sentence before trusting a number. |
| 17 | +2. **Check what `max_tokens` actually does before choosing its value.** In ccmr it is a |
| 18 | + clamp on outgoing requests (`router.ts`: cap body.max_tokens at the model limit), so |
| 19 | + the right value is the vendor's *hard maximum* (1048576), not its default (131072). |
| 20 | + The same number can be wrong for one semantic and right for another. |
| 21 | +3. **Detect the platform fork early and make it the user's decision.** Grepped the repo: |
| 22 | + `.cn` had never been used; the user's live config uses `.ai`; the two platforms' |
| 23 | + keys don't interoperate. That is a public-behavior fork (which endpoint the default |
| 24 | + config ships), so it went to the user as three concrete options instead of a guess. |
| 25 | + They chose the dual-provider route. |
| 26 | +4. **Copy the repo's own precedent.** minimax/minimax-global and glm/glm-global already |
| 27 | + encode the CN/international split: separate provider key, separate `*_API_KEY` env, |
| 28 | + display names suffixed, model names `<provider>-<variant>`. `kimi-cn` reused that |
| 29 | + shape wholesale — zero new design. |
| 30 | +5. **TDD at the public seam, then walk the Five Copies.** Failing ConfigManager tests |
| 31 | + first (model keys, aliases, base_url, api_key_env), then DEFAULT_CONFIG → YAML |
| 32 | + template → env template/.env.example → README tables/changelog → VSCode extension |
| 33 | + secrets.ts, verified by grepping `KIMI_CN_API_KEY` across both repos. |
| 34 | +6. **Doctor with a control.** `doctor kimi-k3` returned `[401] Invalid Authentication`; |
| 35 | + before blaming the new entry, `doctor kimi-k2.6` with the same key returned the |
| 36 | + identical 401 — the credential is dead, the routing is fine. One extra request turned |
| 37 | + an ambiguous failure into a named layer. |
| 38 | + |
| 39 | +## The judgment calls |
| 40 | + |
| 41 | +- **Did NOT change the existing kimi provider's base_url to `.cn`** even though the |
| 42 | + user's snippet used it — that would silently break every existing international-key |
| 43 | + user. New endpoint = new provider, never a mutation of a shipped one. |
| 44 | +- **Did NOT trust the third-party K3 coverage** (blogs claiming specs before the vendor |
| 45 | + published) — only numbers quoted from platform.kimi.com pages went into config. |
| 46 | +- **Did NOT mark the task done on the 401** — reported the upstream error verbatim with |
| 47 | + the control-test evidence instead of retrying or paraphrasing it away. |
| 48 | + |
| 49 | +## The reusable rule |
| 50 | + |
| 51 | +When a vendor runs separate CN/international platforms, a new model request that names |
| 52 | +one endpoint is a fork, not an instruction: check which platform the existing provider |
| 53 | +and the user's live config use, and if keys don't interoperate, model the other platform |
| 54 | +as a new provider with its own key env (copy the repo's existing split precedent) — |
| 55 | +asking the user only the one question the code can't answer: which platform they want. |
0 commit comments