mirror of
https://github.com/MoonshotAI/kimi-code.git
synced 2026-08-20 06:05:36 +00:00
feat: log enabled experimental flags (#357)
This commit is contained in:
parent
9143fdadf6
commit
179aecf423
5 changed files with 72 additions and 11 deletions
6
.changeset/log-enabled-flags.md
Normal file
6
.changeset/log-enabled-flags.md
Normal file
|
|
@ -0,0 +1,6 @@
|
|||
---
|
||||
"@moonshot-ai/agent-core": minor
|
||||
"@moonshot-ai/kimi-code": minor
|
||||
---
|
||||
|
||||
Log enabled experimental flags at startup.
|
||||
|
|
@ -17,14 +17,12 @@ export const MASTER_ENV = 'KIMI_CODE_EXPERIMENTAL_FLAG';
|
|||
* L3 registry default
|
||||
*/
|
||||
export class FlagResolver {
|
||||
private readonly env: Readonly<Record<string, string | undefined>>;
|
||||
private readonly byId: ReadonlyMap<string, FlagDefinitionInput>;
|
||||
|
||||
constructor(
|
||||
env: Readonly<Record<string, string | undefined>> = process.env,
|
||||
definitions: readonly FlagDefinitionInput[] = FLAG_DEFINITIONS,
|
||||
private readonly env: Readonly<Record<string, string | undefined>> = process.env,
|
||||
private readonly definitions: readonly FlagDefinitionInput[] = FLAG_DEFINITIONS,
|
||||
) {
|
||||
this.env = env;
|
||||
this.byId = new Map(definitions.map((def) => [def.id, def]));
|
||||
}
|
||||
|
||||
|
|
@ -36,6 +34,18 @@ export class FlagResolver {
|
|||
if (override !== undefined) return override;
|
||||
return def.default; // L3 default
|
||||
}
|
||||
|
||||
snapshot(): Record<string, boolean> {
|
||||
return Object.fromEntries(
|
||||
this.definitions.map((def) => [def.id, this.enabled(def.id as FlagId)]),
|
||||
);
|
||||
}
|
||||
|
||||
enabledIds(): readonly FlagId[] {
|
||||
return this.definitions
|
||||
.filter((def) => this.enabled(def.id as FlagId))
|
||||
.map((def) => def.id as FlagId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
|
|
|||
|
|
@ -22,11 +22,8 @@ import {
|
|||
type MoonshotServiceConfig,
|
||||
} from '../config';
|
||||
import {
|
||||
FLAG_DEFINITIONS,
|
||||
flags,
|
||||
type ExperimentalFlagMap,
|
||||
type FlagDefinitionInput,
|
||||
type FlagId,
|
||||
} from '../flags';
|
||||
import type { Logger } from '../logging/types';
|
||||
import { resolveSessionMcpConfig, type SessionMcpConfig } from '../mcp';
|
||||
|
|
@ -169,6 +166,7 @@ export class KimiCore implements PromisableMethods<CoreAPI> {
|
|||
this.pluginsReady = this.plugins.load().catch((error: unknown) => {
|
||||
this.pluginsLoadError = error instanceof Error ? error : new Error(String(error));
|
||||
});
|
||||
log.info('experimental flags enabled', { flags: flags.enabledIds() });
|
||||
|
||||
this.sdk = rpcClient(this);
|
||||
}
|
||||
|
|
@ -259,8 +257,7 @@ export class KimiCore implements PromisableMethods<CoreAPI> {
|
|||
}
|
||||
|
||||
getExperimentalFlags(): ExperimentalFlagMap {
|
||||
const defs: readonly FlagDefinitionInput[] = FLAG_DEFINITIONS;
|
||||
return Object.fromEntries(defs.map((def) => [def.id, flags.enabled(def.id as FlagId)]));
|
||||
return flags.snapshot();
|
||||
}
|
||||
|
||||
async closeSession({ sessionId }: CloseSessionPayload): Promise<void> {
|
||||
|
|
|
|||
|
|
@ -63,7 +63,7 @@ describe('FlagResolver', () => {
|
|||
expect(enabled('b-off-default')).toBe(true);
|
||||
});
|
||||
|
||||
it('L1 master switch beats an L2 per-feature off (D2)', () => {
|
||||
it('L1 master switch beats an L2 per-feature off', () => {
|
||||
const enabled = make({ [MASTER_ENV]: '1', KIMI_CODE_EXPERIMENTAL_A: '0' });
|
||||
expect(enabled('a-on-default')).toBe(true);
|
||||
});
|
||||
|
|
@ -73,6 +73,15 @@ describe('FlagResolver', () => {
|
|||
expect(enabled('b-off-default')).toBe(false);
|
||||
});
|
||||
|
||||
it('returns a full snapshot and enabled ids in registry order', () => {
|
||||
const resolver = new FlagResolver({ KIMI_CODE_EXPERIMENTAL_B: '1' }, DEFS);
|
||||
expect(resolver.snapshot()).toEqual({
|
||||
'a-on-default': true,
|
||||
'b-off-default': true,
|
||||
});
|
||||
expect(resolver.enabledIds()).toEqual(['a-on-default', 'b-off-default']);
|
||||
});
|
||||
|
||||
it('reads the env name declared in the registry (the declared name works, others do not)', () => {
|
||||
expect(make({ KIMI_CODE_EXPERIMENTAL_B: '1' })('b-off-default')).toBe(true);
|
||||
// The name mechanically derived from the id must not take effect (env is explicitly ..._B).
|
||||
|
|
|
|||
|
|
@ -1,18 +1,32 @@
|
|||
import { mkdir, mkdtemp, rm, writeFile } from 'node:fs/promises';
|
||||
import { mkdir, mkdtemp, readFile, rm, writeFile } from 'node:fs/promises';
|
||||
import { tmpdir } from 'node:os';
|
||||
import { join } from 'pathe';
|
||||
|
||||
import { afterEach, describe, expect, it, vi } from 'vitest';
|
||||
|
||||
import {
|
||||
FLAG_DEFINITIONS,
|
||||
MASTER_ENV,
|
||||
createRPC,
|
||||
KimiCore,
|
||||
type ApprovalResponse,
|
||||
type CoreAPI,
|
||||
type SDKAPI,
|
||||
} from '../../src';
|
||||
import {
|
||||
__resetRootLoggerForTest,
|
||||
getRootLogger,
|
||||
resolveGlobalLogPath,
|
||||
} from '../../src/logging/logger';
|
||||
import { resolveLoggingConfig } from '../../src/logging/resolve-config';
|
||||
import type { OAuthTokenProviderResolver } from '../../src/session/provider-manager';
|
||||
|
||||
function requiredFlagEnv(id: string): string {
|
||||
const def = FLAG_DEFINITIONS.find((item) => item.id === id);
|
||||
if (def === undefined) throw new Error(`Missing flag definition: ${id}`);
|
||||
return def.env;
|
||||
}
|
||||
|
||||
describe('KimiCore runtime config', () => {
|
||||
let tmp: string;
|
||||
|
||||
|
|
@ -20,9 +34,34 @@ describe('KimiCore runtime config', () => {
|
|||
if (tmp !== undefined) {
|
||||
await rm(tmp, { recursive: true, force: true });
|
||||
}
|
||||
await __resetRootLoggerForTest();
|
||||
vi.unstubAllEnvs();
|
||||
vi.unstubAllGlobals();
|
||||
});
|
||||
|
||||
it('logs all enabled experimental flags once on core startup', async () => {
|
||||
tmp = await mkdtemp(join(tmpdir(), 'kimi-core-runtime-'));
|
||||
const homeDir = join(tmp, 'home');
|
||||
await mkdir(homeDir, { recursive: true });
|
||||
await getRootLogger().configure(resolveLoggingConfig({ homeDir }));
|
||||
|
||||
vi.stubEnv(MASTER_ENV, '0');
|
||||
for (const def of FLAG_DEFINITIONS) {
|
||||
vi.stubEnv(def.env, '0');
|
||||
}
|
||||
vi.stubEnv(requiredFlagEnv('goal-command'), '1');
|
||||
vi.stubEnv(requiredFlagEnv('background-ask'), '1');
|
||||
|
||||
void new KimiCore(async () => ({}) as never, { homeDir });
|
||||
await getRootLogger().flushGlobal();
|
||||
|
||||
const text = await readFile(resolveGlobalLogPath(homeDir), 'utf-8');
|
||||
expect(text).toContain('experimental flags enabled');
|
||||
expect(text).toContain('goal-command');
|
||||
expect(text).toContain('background-ask');
|
||||
expect(text.match(/experimental flags enabled/g)).toHaveLength(1);
|
||||
});
|
||||
|
||||
it('uses the shared OAuth resolver for Moonshot service tokens', async () => {
|
||||
tmp = await mkdtemp(join(tmpdir(), 'kimi-core-runtime-'));
|
||||
const homeDir = join(tmp, 'home');
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue