feat(kimi): declare the 256K context window for Kimi For Coding

Claude Code caps unknown model ids at 200K, so the preset's standalone
CLAUDE_CODE_AUTO_COMPACT_WINDOW=262144 was clamped by min(window, value)
and never took effect. Pair it with CLAUDE_CODE_MAX_CONTEXT_TOKENS and
route the endpoint's kimi-for-coding alias on every tier, since both
context envs are ignored for claude-* prefixed model ids.

Saved providers get the same context defaults injected into effective
live settings at switch time, with a mirror-inverse strip on backfill so
injected values never harden into provider config. Explicit user values
always win over the injected defaults.
This commit is contained in:
Jason
2026-07-12 23:46:23 +08:00
parent 31ee42854a
commit 940ddd332b
3 changed files with 203 additions and 8 deletions
+153
View File
@@ -29,6 +29,7 @@ use super::normalize_claude_models_in_value;
/// usable context.
const CODEX_OAUTH_CLAUDE_MAX_CONTEXT_TOKENS: &str = "372000";
const CODEX_OAUTH_CLAUDE_AUTO_COMPACT_WINDOW: &str = "372000";
const KIMI_FOR_CODING_CONTEXT_TOKENS: &str = "262144";
/// Model env keys Claude Code may route requests through. The defaults above
/// are calibrated against gpt-5.6's Codex catalog, so every configured model
@@ -67,6 +68,16 @@ fn provider_env_targets_gpt56(provider_env: Option<&serde_json::Map<String, Valu
saw_model
}
fn is_kimi_for_coding_provider(provider: &Provider) -> bool {
provider
.settings_config
.pointer("/env/ANTHROPIC_BASE_URL")
.and_then(Value::as_str)
.map(str::trim)
.map(|url| url.trim_end_matches('/'))
== Some("https://api.kimi.com/coding")
}
/// Claude Code assigns unknown non-Claude model ids a 200K context window.
/// Codex OAuth deliberately exposes GPT ids through Claude Code, so enrich the
/// effective live settings for both newly-created and already-saved providers.
@@ -122,6 +133,36 @@ fn apply_codex_oauth_claude_context_defaults(settings: &mut Value, provider: &Pr
}
}
/// Kimi For Coding serves a 256K window, but Claude Code caps unknown models at
/// 200K unless `CLAUDE_CODE_MAX_CONTEXT_TOKENS` is set — and that env is ignored
/// for `claude-`-prefixed ids, so these defaults only bite when the provider also
/// routes the endpoint's `kimi-for-coding` alias (the preset does). Keep the
/// defaults provider-owned so an old shared snippet cannot override them.
fn apply_kimi_for_coding_context_defaults(settings: &mut Value, provider: &Provider) {
if !is_kimi_for_coding_provider(provider) {
return;
}
let provider_env = provider
.settings_config
.get("env")
.and_then(Value::as_object);
let Some(env) = settings.get_mut("env").and_then(Value::as_object_mut) else {
return;
};
for key in [
"CLAUDE_CODE_MAX_CONTEXT_TOKENS",
"CLAUDE_CODE_AUTO_COMPACT_WINDOW",
] {
let value = provider_env
.and_then(|provider_env| provider_env.get(key))
.cloned()
.unwrap_or_else(|| Value::String(KIMI_FOR_CODING_CONTEXT_TOKENS.to_string()));
env.insert(key.to_string(), value);
}
}
pub(crate) fn sanitize_claude_settings_for_live(settings: &Value) -> Value {
let mut v = settings.clone();
if let Some(obj) = v.as_object_mut() {
@@ -640,6 +681,7 @@ pub(crate) fn build_effective_settings_with_common_config(
if matches!(app_type, AppType::Claude) {
apply_codex_oauth_claude_context_defaults(&mut effective_settings, provider);
apply_kimi_for_coding_context_defaults(&mut effective_settings, provider);
}
Ok(effective_settings)
@@ -748,6 +790,30 @@ fn strip_injected_codex_oauth_context_defaults(settings: &mut Value, provider: &
}
}
fn strip_injected_kimi_for_coding_context_defaults(settings: &mut Value, provider: &Provider) {
if !is_kimi_for_coding_provider(provider) {
return;
}
let provider_env = provider
.settings_config
.get("env")
.and_then(Value::as_object);
let Some(env) = settings.get_mut("env").and_then(Value::as_object_mut) else {
return;
};
for key in [
"CLAUDE_CODE_MAX_CONTEXT_TOKENS",
"CLAUDE_CODE_AUTO_COMPACT_WINDOW",
] {
if provider_env.is_some_and(|provider_env| provider_env.contains_key(key)) {
continue;
}
if env.get(key).and_then(Value::as_str) == Some(KIMI_FOR_CODING_CONTEXT_TOKENS) {
env.remove(key);
}
}
}
fn restore_live_settings_for_provider_backfill(
app_type: &AppType,
provider: &Provider,
@@ -756,6 +822,7 @@ fn restore_live_settings_for_provider_backfill(
if matches!(app_type, AppType::Claude) {
let mut settings = live_settings;
strip_injected_codex_oauth_context_defaults(&mut settings, provider);
strip_injected_kimi_for_coding_context_defaults(&mut settings, provider);
return settings;
}
if !matches!(app_type, AppType::Codex) {
@@ -1860,6 +1927,92 @@ mod tests {
use super::*;
use serde_json::json;
#[test]
fn kimi_for_coding_effective_settings_backfill_256k_context() {
let db = Database::memory().expect("create memory db");
let provider = Provider::with_id(
"kimi-for-coding".to_string(),
"Kimi For Coding".to_string(),
json!({
"env": {
"ANTHROPIC_BASE_URL": "https://api.kimi.com/coding/",
"ANTHROPIC_MODEL": "kimi-for-coding",
"CLAUDE_CODE_AUTO_COMPACT_WINDOW": "262144"
}
}),
None,
);
let effective =
build_effective_settings_with_common_config(&db, &AppType::Claude, &provider)
.expect("build effective settings");
assert_eq!(
effective["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"],
json!("262144")
);
assert_eq!(
effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"],
json!("262144")
);
}
#[test]
fn kimi_for_coding_context_defaults_preserve_user_overrides() {
let db = Database::memory().expect("create memory db");
let provider = Provider::with_id(
"kimi-for-coding".to_string(),
"Kimi For Coding".to_string(),
json!({
"env": {
"ANTHROPIC_BASE_URL": "https://api.kimi.com/coding",
"CLAUDE_CODE_MAX_CONTEXT_TOKENS": "300000",
"CLAUDE_CODE_AUTO_COMPACT_WINDOW": "250000"
}
}),
None,
);
let effective =
build_effective_settings_with_common_config(&db, &AppType::Claude, &provider)
.expect("build effective settings");
assert_eq!(
effective["env"]["CLAUDE_CODE_MAX_CONTEXT_TOKENS"],
json!("300000")
);
assert_eq!(
effective["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"],
json!("250000")
);
}
#[test]
fn kimi_for_coding_backfill_strips_only_injected_context_default() {
let db = Database::memory().expect("create memory db");
let provider = Provider::with_id(
"kimi-for-coding".to_string(),
"Kimi For Coding".to_string(),
json!({
"env": {
"ANTHROPIC_BASE_URL": "https://api.kimi.com/coding/",
"CLAUDE_CODE_AUTO_COMPACT_WINDOW": "262144"
}
}),
None,
);
let live = build_effective_settings_with_common_config(&db, &AppType::Claude, &provider)
.expect("build effective settings");
let backfilled =
strip_common_config_from_live_settings(&db, &AppType::Claude, &provider, live);
assert!(backfilled["env"]
.get("CLAUDE_CODE_MAX_CONTEXT_TOKENS")
.is_none());
assert_eq!(
backfilled["env"]["CLAUDE_CODE_AUTO_COMPACT_WINDOW"],
json!("262144")
);
}
#[test]
fn codex_oauth_effective_settings_backfill_gpt_context_defaults() {
let db = Database::memory().expect("create memory db");
+13
View File
@@ -486,10 +486,23 @@ export const providerPresets: ProviderPreset[] = [
env: {
ANTHROPIC_BASE_URL: "https://api.kimi.com/coding/",
ANTHROPIC_AUTH_TOKEN: "",
// CLAUDE_CODE_MAX_CONTEXT_TOKENS 只对非 claude- 前缀模型 id 生效,
// 必须显式路由端点别名 kimi-for-coding(与 codex/hermes/opencode 预设一致)
ANTHROPIC_MODEL: "kimi-for-coding",
ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding",
ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding",
ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding",
CLAUDE_CODE_MAX_CONTEXT_TOKENS: "${CLAUDE_CODE_MAX_CONTEXT_TOKENS}",
CLAUDE_CODE_AUTO_COMPACT_WINDOW: "${CLAUDE_CODE_AUTO_COMPACT_WINDOW}",
},
},
templateValues: {
CLAUDE_CODE_MAX_CONTEXT_TOKENS: {
label: "Max Context Tokens",
placeholder: "262144",
defaultValue: "262144",
editorValue: "262144",
},
CLAUDE_CODE_AUTO_COMPACT_WINDOW: {
label: "Auto Compact Window",
placeholder: "262144",
+37 -8
View File
@@ -11,21 +11,50 @@ describe("Kimi For Coding Provider Preset", () => {
expect(kimiForCoding).toBeDefined();
});
it("should use template placeholder for Claude Code auto-compact window", () => {
// CLAUDE_CODE_MAX_CONTEXT_TOKENS is ignored for claude-* model ids, so the
// preset must route the endpoint's own alias for the context envs to bite
it("should route the kimi-for-coding model id on every tier", () => {
const env = (kimiForCoding!.settingsConfig as any).env;
expect(env).toMatchObject({
ANTHROPIC_MODEL: "kimi-for-coding",
ANTHROPIC_DEFAULT_HAIKU_MODEL: "kimi-for-coding",
ANTHROPIC_DEFAULT_SONNET_MODEL: "kimi-for-coding",
ANTHROPIC_DEFAULT_OPUS_MODEL: "kimi-for-coding",
});
});
it("should use matching context and auto-compact placeholders", () => {
const env = (kimiForCoding!.settingsConfig as any).env;
expect(env).toHaveProperty(
"CLAUDE_CODE_MAX_CONTEXT_TOKENS",
"${CLAUDE_CODE_MAX_CONTEXT_TOKENS}",
);
expect(env).toHaveProperty(
"CLAUDE_CODE_AUTO_COMPACT_WINDOW",
"${CLAUDE_CODE_AUTO_COMPACT_WINDOW}",
);
});
it("should expose auto-compact window as editable template value with Kimi default", () => {
const values = (kimiForCoding!.templateValues as any)
?.CLAUDE_CODE_AUTO_COMPACT_WINDOW;
expect(values).toBeDefined();
expect(values.defaultValue).toBe("262144");
expect(values.editorValue).toBe("262144");
expect(values.label).toBe("Auto Compact Window");
it("should expose matching 256K context and auto-compact values", () => {
const values = kimiForCoding!.templateValues as any;
expect(values?.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toMatchObject({
defaultValue: "262144",
editorValue: "262144",
});
expect(values?.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toMatchObject({
defaultValue: "262144",
editorValue: "262144",
label: "Auto Compact Window",
});
});
it("should resolve both Kimi context placeholders", () => {
const config = applyTemplateValues(
kimiForCoding!.settingsConfig,
kimiForCoding!.templateValues,
) as any;
expect(config.env.CLAUDE_CODE_MAX_CONTEXT_TOKENS).toBe("262144");
expect(config.env.CLAUDE_CODE_AUTO_COMPACT_WINDOW).toBe("262144");
});
});