Skip to content

Commit be8ec04

Browse files
author
IM.codes
committed
Fix shared context backup runtime config
1 parent 393806e commit be8ec04

6 files changed

Lines changed: 73 additions & 32 deletions

File tree

shared/shared-context-runtime-config.ts

Lines changed: 9 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -90,20 +90,20 @@ export function normalizeSharedContextRuntimeConfig(
9090
? rawPrimaryContextModel
9191
: getDefaultSharedContextModelForBackend(normalizedPrimaryBackend);
9292
const normalizedBackupBackendCandidate = normalizeSharedContextRuntimeBackend(input?.backupContextBackend)
93-
?? inferSharedContextRuntimeBackend(input?.backupContextModel)
94-
?? normalizedPrimaryBackend;
93+
?? inferSharedContextRuntimeBackend(input?.backupContextModel);
9594
const rawBackupContextModel = trimModelValue(input?.backupContextModel);
96-
const backupContextModel = rawBackupContextModel
97-
? (isKnownSharedContextModelForBackend(normalizedBackupBackendCandidate, rawBackupContextModel)
98-
? rawBackupContextModel
99-
: getDefaultSharedContextModelForBackend(normalizedBackupBackendCandidate))
95+
const backupContextBackend = normalizedBackupBackendCandidate;
96+
const backupContextModel = backupContextBackend
97+
? (rawBackupContextModel
98+
? (isKnownSharedContextModelForBackend(backupContextBackend, rawBackupContextModel)
99+
? rawBackupContextModel
100+
: getDefaultSharedContextModelForBackend(backupContextBackend))
101+
: getDefaultSharedContextModelForBackend(backupContextBackend))
100102
: undefined;
101103
return {
102104
primaryContextBackend: normalizedPrimaryBackend,
103105
primaryContextModel,
104-
backupContextBackend: backupContextModel
105-
? normalizedBackupBackendCandidate
106-
: undefined,
106+
backupContextBackend,
107107
backupContextModel,
108108
};
109109
}

src/daemon/command-handler.ts

Lines changed: 13 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ import {
6060
} from '../../shared/effort-levels.js';
6161
import { getSavedP2pConfig, upsertSavedP2pConfig } from '../store/p2p-config-store.js';
6262
import {
63+
normalizeSharedContextRuntimeConfig,
6364
normalizeSharedContextRuntimeBackend,
6465
SHARED_CONTEXT_RUNTIME_CONFIG_MSG,
6566
} from '../../shared/shared-context-runtime-config.js';
@@ -3842,23 +3843,20 @@ async function handleCcPresetsSave(cmd: Record<string, unknown>, serverLink: Ser
38423843

38433844
async function handleSharedContextRuntimeConfigApply(cmd: Record<string, unknown>): Promise<void> {
38443845
const config = cmd.config as Record<string, unknown> | undefined;
3845-
const primaryContextBackend = normalizeSharedContextRuntimeBackend(
3846-
typeof config?.primaryContextBackend === 'string' ? config.primaryContextBackend : undefined,
3847-
);
3848-
const primaryContextModel = typeof config?.primaryContextModel === 'string' ? config.primaryContextModel.trim() : '';
3849-
const backupContextBackend = normalizeSharedContextRuntimeBackend(
3850-
typeof config?.backupContextBackend === 'string' ? config.backupContextBackend : undefined,
3851-
);
3852-
const backupContextModel = typeof config?.backupContextModel === 'string' ? config.backupContextModel.trim() : '';
3853-
if (!primaryContextBackend || !primaryContextModel) {
3846+
const normalized = normalizeSharedContextRuntimeConfig({
3847+
primaryContextBackend: normalizeSharedContextRuntimeBackend(
3848+
typeof config?.primaryContextBackend === 'string' ? config.primaryContextBackend : undefined,
3849+
),
3850+
primaryContextModel: typeof config?.primaryContextModel === 'string' ? config.primaryContextModel : undefined,
3851+
backupContextBackend: normalizeSharedContextRuntimeBackend(
3852+
typeof config?.backupContextBackend === 'string' ? config.backupContextBackend : undefined,
3853+
),
3854+
backupContextModel: typeof config?.backupContextModel === 'string' ? config.backupContextModel : undefined,
3855+
});
3856+
if (!normalized.primaryContextBackend || !normalized.primaryContextModel) {
38543857
logger.warn({ cmd }, 'invalid shared-context runtime config apply command');
38553858
return;
38563859
}
38573860
const { setContextModelRuntimeConfig } = await import('../context/context-model-config.js');
3858-
setContextModelRuntimeConfig({
3859-
primaryContextBackend,
3860-
primaryContextModel,
3861-
backupContextBackend: backupContextModel ? (backupContextBackend ?? primaryContextBackend) : undefined,
3862-
backupContextModel: backupContextModel || undefined,
3863-
});
3861+
setContextModelRuntimeConfig(normalized);
38643862
}

test/daemon/context-model-config.test.ts

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,18 @@ describe('context-model-config', () => {
3131
expect(getContextModelConfig().primaryContextModel).toBe('gpt-5.4-mini');
3232
expect(getContextModelConfig().primaryContextBackend).toBe('codex-sdk');
3333
});
34+
35+
it('fills the backup model from the selected backup backend when runtime config omits it', () => {
36+
setContextModelRuntimeConfig({
37+
primaryContextBackend: 'claude-code-sdk',
38+
primaryContextModel: 'sonnet',
39+
backupContextBackend: 'qwen',
40+
});
41+
expect(getContextModelConfig()).toEqual({
42+
primaryContextBackend: 'claude-code-sdk',
43+
primaryContextModel: 'sonnet',
44+
backupContextBackend: 'qwen',
45+
backupContextModel: 'qwen3-coder-plus',
46+
});
47+
});
3448
});

test/shared-context-runtime-config.test.ts

Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -29,4 +29,17 @@ describe('shared-context-runtime-config', () => {
2929
backupContextModel: getDefaultSharedContextModelForBackend('codex-sdk'),
3030
});
3131
});
32+
33+
it('keeps a configured backup backend by filling its default model when the model is omitted', () => {
34+
expect(normalizeSharedContextRuntimeConfig({
35+
primaryContextBackend: 'claude-code-sdk',
36+
primaryContextModel: 'sonnet',
37+
backupContextBackend: 'qwen',
38+
})).toEqual({
39+
primaryContextBackend: 'claude-code-sdk',
40+
primaryContextModel: 'sonnet',
41+
backupContextBackend: 'qwen',
42+
backupContextModel: getDefaultSharedContextModelForBackend('qwen'),
43+
});
44+
});
3245
});

web/src/components/SharedContextManagementPanel.tsx

Lines changed: 1 addition & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -688,10 +688,7 @@ export function SharedContextManagementPanel({ enterpriseId: initialEnterpriseId
688688

689689
const handleProcessingBackupBackendChange = useCallback((nextBackend: SharedContextRuntimeBackend) => {
690690
setProcessingBackupBackend((prevBackend) => {
691-
setProcessingBackupModel((prevModel) => {
692-
if (!prevModel.trim()) return '';
693-
return resolveProcessingModelForBackend(nextBackend, prevModel, prevBackend);
694-
});
691+
setProcessingBackupModel((prevModel) => resolveProcessingModelForBackend(nextBackend, prevModel, prevBackend));
695692
return nextBackend;
696693
});
697694
}, []);

web/test/components/SharedContextManagementPanel.test.tsx

Lines changed: 23 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -313,23 +313,24 @@ describe('SharedContextManagementPanel', () => {
313313

314314
const primaryBackend = screen.getByLabelText('sharedContext.management.processingPrimaryBackend: codex-sdk');
315315
const primaryInput = screen.getByLabelText('sharedContext.management.processingPrimaryModel') as HTMLInputElement;
316-
const backupBackend = screen.getByLabelText('sharedContext.management.processingBackupBackend: claude-code-sdk');
316+
const backupBackend = screen.getByLabelText('sharedContext.management.processingBackupBackend: qwen');
317317
const backupInput = screen.getByLabelText('sharedContext.management.processingBackupModel') as HTMLInputElement;
318318
fireEvent.click(primaryBackend);
319319
fireEvent.input(primaryInput, { target: { value: 'gpt-5.4' } });
320320
fireEvent.click(backupBackend);
321-
fireEvent.input(backupInput, { target: { value: 'haiku' } });
322321
await flush();
323322

323+
expect(backupInput.value).toBe('qwen3-coder-plus');
324+
324325
await act(async () => {
325326
fireEvent.click(screen.getByText('sharedContext.management.processingSave'));
326327
});
327328

328329
await waitFor(() => expect(updateSharedContextRuntimeConfigMock).toHaveBeenCalledWith('srv-1', {
329330
primaryContextBackend: 'codex-sdk',
330331
primaryContextModel: 'gpt-5.4',
331-
backupContextBackend: 'claude-code-sdk',
332-
backupContextModel: 'haiku',
332+
backupContextBackend: 'qwen',
333+
backupContextModel: 'qwen3-coder-plus',
333334
}));
334335
expect((screen.getByLabelText('sharedContext.management.processingPrimaryModel') as HTMLInputElement).value).toBe('gpt-5.4');
335336
expect(await screen.findByText('sharedContext.management.processingSavedPrimaryBackend')).toBeDefined();
@@ -386,4 +387,22 @@ describe('SharedContextManagementPanel', () => {
386387

387388
expect(backupInput.value).toBe('qwen3-coder-plus');
388389
});
390+
391+
it('preloads a backend-appropriate backup model as soon as the backup backend changes', async () => {
392+
render(<SharedContextManagementPanel serverId="srv-1" />);
393+
await flush();
394+
395+
await act(async () => {
396+
fireEvent.click(screen.getByText('sharedContext.management.tabs.processing'));
397+
});
398+
399+
const backupInput = await screen.findByLabelText('sharedContext.management.processingBackupModel') as HTMLInputElement;
400+
expect(backupInput.value).toBe('');
401+
402+
await act(async () => {
403+
fireEvent.click(screen.getByLabelText('sharedContext.management.processingBackupBackend: qwen'));
404+
});
405+
406+
expect(backupInput.value).toBe('qwen3-coder-plus');
407+
});
389408
});

0 commit comments

Comments
 (0)