mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-07-10 01:39:25 +00:00
fix: restore env-injected fields to on-disk values in stripEnvModelConfig; update tests for thinking behavior
This commit is contained in:
parent
18a86297a2
commit
3719ec031d
2 changed files with 70 additions and 19 deletions
|
|
@ -171,9 +171,10 @@ export function applyEnvModelConfig(config: KimiConfig, env: Env = process.env):
|
|||
* into the in-memory runtime config; this guarantees they never reach
|
||||
* config.toml — including via a `getConfig` -> `setConfig` patch round-trip,
|
||||
* where the runtime config (carrying the env provider and its shell API key)
|
||||
* would otherwise be merged back and written out. A `default_model` pointing at
|
||||
* the env alias is restored to the on-disk value (from `config.raw`) rather than
|
||||
* erased, so a real default_model already in config.toml survives the round-trip.
|
||||
* would otherwise be merged back and written out. Every env-injected top-level
|
||||
* field (default_model, thinking, default_thinking) is restored to its on-disk
|
||||
* value from `config.raw` rather than erased, so real values already in
|
||||
* config.toml survive the round-trip.
|
||||
*/
|
||||
export function stripEnvModelConfig(config: KimiConfig): KimiConfig {
|
||||
const hasProvider = ENV_MODEL_PROVIDER_KEY in config.providers;
|
||||
|
|
@ -194,9 +195,14 @@ export function stripEnvModelConfig(config: KimiConfig): KimiConfig {
|
|||
...config,
|
||||
providers,
|
||||
...(models !== undefined ? { models } : {}),
|
||||
// Restore the on-disk default (from raw) instead of erasing it when the
|
||||
// runtime default points at the env alias.
|
||||
// Restore env-injected top-level fields from raw instead of persisting the
|
||||
// shell overrides: the env default_model (when it points at the env alias),
|
||||
// and the env thinking / default_thinking. Reaching here means env-model
|
||||
// mode is active (the synthetic provider/model exist), so these may be env
|
||||
// values; an unset raw field restores to undefined (i.e. drops it).
|
||||
...(defaultIsEnv ? { defaultModel: rawDefaultModel(config) } : {}),
|
||||
thinking: rawThinking(config),
|
||||
defaultThinking: rawDefaultThinking(config),
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -204,3 +210,15 @@ function rawDefaultModel(config: KimiConfig): string | undefined {
|
|||
const raw = config.raw?.['default_model'];
|
||||
return typeof raw === 'string' ? raw : undefined;
|
||||
}
|
||||
|
||||
function rawDefaultThinking(config: KimiConfig): boolean | undefined {
|
||||
const raw = config.raw?.['default_thinking'];
|
||||
return typeof raw === 'boolean' ? raw : undefined;
|
||||
}
|
||||
|
||||
function rawThinking(config: KimiConfig): ThinkingConfig | undefined {
|
||||
const raw = config.raw?.['thinking'];
|
||||
return typeof raw === 'object' && raw !== null && !Array.isArray(raw)
|
||||
? (raw as ThinkingConfig)
|
||||
: undefined;
|
||||
}
|
||||
|
|
|
|||
|
|
@ -1,4 +1,4 @@
|
|||
import { mkdtempSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { mkdtempSync, readFileSync, rmSync, writeFileSync } from 'node:fs';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import { join } from 'pathe';
|
||||
|
|
@ -213,27 +213,60 @@ describe('stripEnvModelConfig (write-back guard)', () => {
|
|||
});
|
||||
|
||||
describe('writeConfigFile never persists the env model', () => {
|
||||
it('strips env entries even when a runtime config is written back', async () => {
|
||||
it('strips env entries (incl. thinking) when a runtime config is written back', async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'kimi-env-write-'));
|
||||
const path = join(dir, 'config.toml');
|
||||
writeFileSync(
|
||||
path,
|
||||
'default_model = "x"\ndefault_thinking = false\n[thinking]\nmode = "auto"\n[providers.x]\ntype = "kimi"\napi_key = "k"\n[models.x]\nprovider = "x"\nmodel = "x"\nmax_context_size = 1000\n',
|
||||
);
|
||||
try {
|
||||
// Reproduces the /login round-trip: a runtime config carrying the env
|
||||
// model AND env thinking overrides is written back and must persist none.
|
||||
const runtime = loadRuntimeConfig(path, {
|
||||
...MIN,
|
||||
KIMI_MODEL_THINKING_MODE: 'on',
|
||||
KIMI_MODEL_DEFAULT_THINKING: 'true',
|
||||
});
|
||||
// Sanity: env overrides are active at runtime.
|
||||
expect(runtime.providers[ENV_MODEL_PROVIDER_KEY]).toBeDefined();
|
||||
expect(runtime.thinking?.mode).toBe('on');
|
||||
expect(runtime.defaultThinking).toBe(true);
|
||||
|
||||
await writeConfigFile(path, runtime);
|
||||
const onDisk = readConfigFile(path);
|
||||
// Env provider/model are gone; the user's stay; default_model is restored.
|
||||
expect(onDisk.providers[ENV_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(onDisk.models?.[ENV_MODEL_ALIAS_KEY]).toBeUndefined();
|
||||
expect(onDisk.providers['x']).toBeDefined();
|
||||
expect(onDisk.models?.['x']).toBeDefined();
|
||||
expect(onDisk.defaultModel).toBe('x');
|
||||
// Thinking is restored to the on-disk original, not the env override.
|
||||
expect(onDisk.thinking?.mode).toBe('auto');
|
||||
expect(onDisk.defaultThinking).toBe(false);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
});
|
||||
|
||||
it('output never contains env-injected identifiers', async () => {
|
||||
const dir = mkdtempSync(join(tmpdir(), 'kimi-env-write2-'));
|
||||
const path = join(dir, 'config.toml');
|
||||
writeFileSync(
|
||||
path,
|
||||
'default_model = "x"\n[providers.x]\ntype = "kimi"\napi_key = "k"\n[models.x]\nprovider = "x"\nmodel = "x"\nmax_context_size = 1000\n',
|
||||
);
|
||||
try {
|
||||
// Reproduces the /login round-trip: a runtime config carrying the env
|
||||
// model is written back through writeConfigFile and must not persist it.
|
||||
const runtime = loadRuntimeConfig(path, { ...MIN });
|
||||
expect(runtime.providers[ENV_MODEL_PROVIDER_KEY]).toBeDefined();
|
||||
const runtime = loadRuntimeConfig(path, {
|
||||
...MIN,
|
||||
KIMI_MODEL_THINKING_EFFORT: 'low',
|
||||
KIMI_MODEL_DEFAULT_THINKING: 'true',
|
||||
});
|
||||
await writeConfigFile(path, runtime);
|
||||
const onDisk = readConfigFile(path);
|
||||
expect(onDisk.providers[ENV_MODEL_PROVIDER_KEY]).toBeUndefined();
|
||||
expect(onDisk.models?.[ENV_MODEL_ALIAS_KEY]).toBeUndefined();
|
||||
expect(onDisk.providers['x']).toBeDefined();
|
||||
expect(onDisk.models?.['x']).toBeDefined();
|
||||
// The env default_model is stripped, but the on-disk original is restored
|
||||
// (not erased), so a real default_model in config.toml survives.
|
||||
expect(onDisk.defaultModel).toBe('x');
|
||||
const text = readFileSync(path, 'utf-8');
|
||||
// Hard invariant: no env-synthesized identifiers ever reach config.toml.
|
||||
expect(text).not.toContain(ENV_MODEL_PROVIDER_KEY);
|
||||
expect(text).not.toContain(ENV_MODEL_ALIAS_KEY);
|
||||
} finally {
|
||||
rmSync(dir, { recursive: true, force: true });
|
||||
}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue