fix: preserve exploration tool history and cap context budget

This commit is contained in:
chenmingxuan committed 2026-10-16 19:05:35 +08:00
1 parent ee256ad7ce
commit 621f30e437
17 files changed
+211 -23

No files matched your search

@@ -1,5 +1,6 @@
import {
contextPercent,
runMode,
formatCompactTokens,
formatElapsed,
parseRunTuning,
@@ -35,13 +36,14 @@ describe('run-info.util', () => {
describe('contextPercent', () => {
it('reports one-decimal share of the window and clamps at 100', () => {
expect(contextPercent(150_000, 1_000_000)).toBe(15);
expect(contextPercent(123_456, 1_000_000)).toBe(12.3);
expect(contextPercent(150_000, 300_000)).toBe(50);
expect(contextPercent(123_456, 300_000)).toBe(41.2);
expect(contextPercent(2_000_000, 1_000_000)).toBe(100);
});
it('falls back to the 1M window and is null when usage is unknown', () => {
expect(contextPercent(250_000, undefined)).toBe(25);
it('falls back to the 300k window and is null when usage is unknown', () => {
expect(contextPercent(150_000, undefined)).toBe(50);
expect(contextPercent(150_000, 1_000_000)).toBe(50);
expect(contextPercent(null, 1_000_000)).toBeNull();
expect(contextPercent(undefined, 1_000_000)).toBeNull();
});
@@ -90,3 +92,23 @@ describe('run-info.util', () => {
});
});
});
describe('runMode', () => {
const model = { name: 'mimo-v2.6-pro', id: 'mimo-v2.6-pro', provider: 'openai', architecture: 'flash' };
it('uses recorded execution mode, never the API model name', () => {
expect(runMode({ profile: 'pro' }, model)).toBe('pro');
expect(runMode(null, model)).toBe('flash');
expect(runMode(null, { ...model, name: 'mimo-v2.6-pro', architecture: undefined })).toBe('unknown');
});
it('distinguishes exploration from a Flash or Pro validation rerun', () => {
expect(runMode({ profile: 'pro', exploration_kind: 'exploration' }, model)).toBe('explore');
expect(runMode({ profile: 'pro', exploration_kind: 'validation' }, model)).toBe('pro');
expect(runMode({ profile: 'flash', exploration_kind: 'validation' }, model)).toBe('flash');
});
it('supports older saved mode names without defaulting missing data to Flash', () => {
expect(runMode(null, { ...model, name: 'ARTEMIS Pro', architecture: undefined })).toBe('pro');
expect(runMode(null, { ...model, name: '探索', architecture: undefined })).toBe('explore');
expect(runMode(null, null)).toBe('unknown');
});
});
@@ -27,10 +27,24 @@ import {
DEFAULT_EXPLORER_MODE,
DEFAULT_VERIFICATION_LEVEL
} from '../core/models/pro-tuning.model';
import { SessionRunTuning } from '../core/models/session.model';
import { ModelInfo, Session, SessionRunTuning } from '../core/models/session.model';
/** Denominator for "context used" when the backend does not report one. */
export const OPERATOR_CONTEXT_WINDOW_TOKENS = 1_000_000;
export const OPERATOR_CONTEXT_WINDOW_TOKENS = 300_000;
/** Execution mode is independent of the API model name (e.g. MiMo Pro). */
export function runMode(session?: Partial<Session> | null, model?: ModelInfo | null): 'flash' | 'pro' | 'explore' | 'unknown' {
if (session?.exploration_kind === 'exploration') return 'explore';
const recorded = session?.profile || session?.model_info?.architecture || model?.architecture;
const mode = (recorded || session?.model_info?.name || model?.name || '').toLowerCase().replace(/^artemis\s+/, '');
if (mode === '探索' || mode === 'explore') return 'explore';
return mode === 'flash' || mode === 'pro' ? mode : 'unknown';
}
export function contextWindow(windowTokens?: number | null): number {
const value = Number(windowTokens);
return value > 0 && Number.isFinite(value) ? Math.min(value, OPERATOR_CONTEXT_WINDOW_TOKENS) : OPERATOR_CONTEXT_WINDOW_TOKENS;
}
/** `42s`, `12m 05s`, `1h 02m 05s`. Negative or invalid input reads as `0s`. */
export function formatElapsed(totalSeconds: number | null | undefined): string {
@@ -58,7 +72,7 @@ export function contextPercent(
windowTokens: number | null | undefined
): number | null {
const used = Number(usedTokens);
const window = Number(windowTokens) || OPERATOR_CONTEXT_WINDOW_TOKENS;
const window = contextWindow(windowTokens);
if (!Number.isFinite(used) || used < 0 || usedTokens === null || usedTokens === undefined) {
return null;
}