Skip to content

Commit 4c594e2

Browse files
authored
Merge pull request #2455 from Sakuranda/fix/2454-model-settings-preservation
fix(cli): preserve runtime-added models when saving settings
2 parents 8b15e2a + 2c0fec1 commit 4c594e2

2 files changed

Lines changed: 98 additions & 6 deletions

File tree

packages/cli/src/config/settings.test.ts

Lines changed: 80 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@ import {
4848
USER_SETTINGS_PATH, // This IS the mocked path.
4949
getSystemSettingsPath,
5050
getSystemDefaultsPath,
51+
SettingScope,
5152
SETTINGS_DIRECTORY_NAME, // This is from the original module, but used by the mock.
5253
type Settings,
5354
loadEnvironment,
@@ -2334,6 +2335,85 @@ describe('Settings Loading and Merging', () => {
23342335
});
23352336
});
23362337

2338+
describe('setValue persistence', () => {
2339+
it('preserves models added to settings.json after startup when updating model.name', () => {
2340+
(mockFsExistsSync as Mock).mockReturnValue(true);
2341+
2342+
const initialUserSettingsContent = {
2343+
[SETTINGS_VERSION_KEY]: SETTINGS_VERSION,
2344+
modelProviders: {
2345+
openai: [
2346+
{
2347+
id: 'existing-model',
2348+
name: 'Existing Model',
2349+
baseUrl: 'https://example.com/v1',
2350+
envKey: 'OPENAI_API_KEY',
2351+
},
2352+
],
2353+
},
2354+
model: {
2355+
name: 'existing-model',
2356+
},
2357+
};
2358+
2359+
const externallyModifiedUserSettingsContent = {
2360+
[SETTINGS_VERSION_KEY]: SETTINGS_VERSION,
2361+
modelProviders: {
2362+
openai: [
2363+
{
2364+
id: 'existing-model',
2365+
name: 'Existing Model',
2366+
baseUrl: 'https://example.com/v1',
2367+
envKey: 'OPENAI_API_KEY',
2368+
},
2369+
{
2370+
id: 'manually-added-model',
2371+
name: 'Manually Added Model',
2372+
baseUrl: 'https://example.com/v1',
2373+
envKey: 'OPENAI_API_KEY',
2374+
},
2375+
],
2376+
},
2377+
model: {
2378+
name: 'existing-model',
2379+
},
2380+
};
2381+
2382+
let currentUserSettingsContent = JSON.stringify(
2383+
initialUserSettingsContent,
2384+
);
2385+
2386+
(fs.readFileSync as Mock).mockImplementation(
2387+
(p: fs.PathOrFileDescriptor) => {
2388+
if (p === USER_SETTINGS_PATH) {
2389+
return currentUserSettingsContent;
2390+
}
2391+
return '{}';
2392+
},
2393+
);
2394+
2395+
const settings = loadSettings(MOCK_WORKSPACE_DIR);
2396+
currentUserSettingsContent = JSON.stringify(
2397+
externallyModifiedUserSettingsContent,
2398+
);
2399+
2400+
settings.setValue(
2401+
SettingScope.User,
2402+
'model.name',
2403+
'manually-added-model',
2404+
);
2405+
2406+
const writeCall = (fs.writeFileSync as Mock).mock.calls.at(-1);
2407+
expect(writeCall).toBeDefined();
2408+
2409+
const writtenContent = JSON.parse(String(writeCall?.[1]));
2410+
expect(writtenContent.model.name).toBe('manually-added-model');
2411+
expect(writtenContent.modelProviders.openai).toEqual(
2412+
externallyModifiedUserSettingsContent.modelProviders.openai,
2413+
);
2414+
});
2415+
});
2416+
23372417
describe('loadEnvironment', () => {
23382418
function setup({
23392419
isFolderTrustEnabled = true,

packages/cli/src/config/settings.ts

Lines changed: 18 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -375,7 +375,7 @@ export class LoadedSettings {
375375
setNestedPropertySafe(settingsFile.settings, key, value);
376376
setNestedPropertySafe(settingsFile.originalSettings, key, value);
377377
this._merged = this.computeMergedSettings();
378-
saveSettings(settingsFile);
378+
saveSettings(settingsFile, createSettingsUpdate(key, value));
379379
}
380380
}
381381

@@ -771,7 +771,22 @@ export function loadSettings(
771771
);
772772
}
773773

774-
export function saveSettings(settingsFile: SettingsFile): void {
774+
function createSettingsUpdate(
775+
key: string,
776+
value: unknown,
777+
): Record<string, unknown> {
778+
const root: Record<string, unknown> = {};
779+
setNestedPropertySafe(root, key, value);
780+
return root;
781+
}
782+
783+
export function saveSettings(
784+
settingsFile: SettingsFile,
785+
updates: Record<string, unknown> = settingsFile.originalSettings as Record<
786+
string,
787+
unknown
788+
>,
789+
): void {
775790
try {
776791
// Ensure the directory exists
777792
const dirPath = path.dirname(settingsFile.path);
@@ -780,10 +795,7 @@ export function saveSettings(settingsFile: SettingsFile): void {
780795
}
781796

782797
// Use the format-preserving update function
783-
updateSettingsFilePreservingFormat(
784-
settingsFile.path,
785-
settingsFile.originalSettings as Record<string, unknown>,
786-
);
798+
updateSettingsFilePreservingFormat(settingsFile.path, updates);
787799
} catch (error) {
788800
debugLogger.error('Error saving user settings file.');
789801
debugLogger.error(error instanceof Error ? error.message : String(error));

0 commit comments

Comments
 (0)