Push model-switching logging into loadCliConfig (#815)
This commit is contained in:
parent
680f4cdd61
commit
6ea4479064
|
@ -83,29 +83,29 @@ describe('loadCliConfig', () => {
|
||||||
it('should set showMemoryUsage to true when --memory flag is present', async () => {
|
it('should set showMemoryUsage to true when --memory flag is present', async () => {
|
||||||
process.argv = ['node', 'script.js', '--show_memory_usage'];
|
process.argv = ['node', 'script.js', '--show_memory_usage'];
|
||||||
const settings: Settings = {};
|
const settings: Settings = {};
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getShowMemoryUsage()).toBe(true);
|
expect(config.getShowMemoryUsage()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set showMemoryUsage to false when --memory flag is not present', async () => {
|
it('should set showMemoryUsage to false when --memory flag is not present', async () => {
|
||||||
process.argv = ['node', 'script.js'];
|
process.argv = ['node', 'script.js'];
|
||||||
const settings: Settings = {};
|
const settings: Settings = {};
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getShowMemoryUsage()).toBe(false);
|
expect(config.getShowMemoryUsage()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set showMemoryUsage to false by default from settings if CLI flag is not present', async () => {
|
it('should set showMemoryUsage to false by default from settings if CLI flag is not present', async () => {
|
||||||
process.argv = ['node', 'script.js'];
|
process.argv = ['node', 'script.js'];
|
||||||
const settings: Settings = { showMemoryUsage: false };
|
const settings: Settings = { showMemoryUsage: false };
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getShowMemoryUsage()).toBe(false);
|
expect(config.getShowMemoryUsage()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prioritize CLI flag over settings for showMemoryUsage (CLI true, settings false)', async () => {
|
it('should prioritize CLI flag over settings for showMemoryUsage (CLI true, settings false)', async () => {
|
||||||
process.argv = ['node', 'script.js', '--show_memory_usage'];
|
process.argv = ['node', 'script.js', '--show_memory_usage'];
|
||||||
const settings: Settings = { showMemoryUsage: false };
|
const settings: Settings = { showMemoryUsage: false };
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getShowMemoryUsage()).toBe(true);
|
expect(config.getShowMemoryUsage()).toBe(true);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -128,50 +128,50 @@ describe('loadCliConfig telemetry', () => {
|
||||||
it('should set telemetry to false by default when no flag or setting is present', async () => {
|
it('should set telemetry to false by default when no flag or setting is present', async () => {
|
||||||
process.argv = ['node', 'script.js'];
|
process.argv = ['node', 'script.js'];
|
||||||
const settings: Settings = {};
|
const settings: Settings = {};
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getTelemetry()).toBe(false);
|
expect(config.getTelemetry()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set telemetry to true when --telemetry flag is present', async () => {
|
it('should set telemetry to true when --telemetry flag is present', async () => {
|
||||||
process.argv = ['node', 'script.js', '--telemetry'];
|
process.argv = ['node', 'script.js', '--telemetry'];
|
||||||
const settings: Settings = {};
|
const settings: Settings = {};
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getTelemetry()).toBe(true);
|
expect(config.getTelemetry()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should set telemetry to false when --no-telemetry flag is present', async () => {
|
it('should set telemetry to false when --no-telemetry flag is present', async () => {
|
||||||
process.argv = ['node', 'script.js', '--no-telemetry'];
|
process.argv = ['node', 'script.js', '--no-telemetry'];
|
||||||
const settings: Settings = {};
|
const settings: Settings = {};
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getTelemetry()).toBe(false);
|
expect(config.getTelemetry()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use telemetry value from settings if CLI flag is not present (settings true)', async () => {
|
it('should use telemetry value from settings if CLI flag is not present (settings true)', async () => {
|
||||||
process.argv = ['node', 'script.js'];
|
process.argv = ['node', 'script.js'];
|
||||||
const settings: Settings = { telemetry: true };
|
const settings: Settings = { telemetry: true };
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getTelemetry()).toBe(true);
|
expect(config.getTelemetry()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should use telemetry value from settings if CLI flag is not present (settings false)', async () => {
|
it('should use telemetry value from settings if CLI flag is not present (settings false)', async () => {
|
||||||
process.argv = ['node', 'script.js'];
|
process.argv = ['node', 'script.js'];
|
||||||
const settings: Settings = { telemetry: false };
|
const settings: Settings = { telemetry: false };
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getTelemetry()).toBe(false);
|
expect(config.getTelemetry()).toBe(false);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prioritize --telemetry CLI flag (true) over settings (false)', async () => {
|
it('should prioritize --telemetry CLI flag (true) over settings (false)', async () => {
|
||||||
process.argv = ['node', 'script.js', '--telemetry'];
|
process.argv = ['node', 'script.js', '--telemetry'];
|
||||||
const settings: Settings = { telemetry: false };
|
const settings: Settings = { telemetry: false };
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getTelemetry()).toBe(true);
|
expect(config.getTelemetry()).toBe(true);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should prioritize --no-telemetry CLI flag (false) over settings (true)', async () => {
|
it('should prioritize --no-telemetry CLI flag (false) over settings (true)', async () => {
|
||||||
process.argv = ['node', 'script.js', '--no-telemetry'];
|
process.argv = ['node', 'script.js', '--no-telemetry'];
|
||||||
const settings: Settings = { telemetry: true };
|
const settings: Settings = { telemetry: true };
|
||||||
const result = await loadCliConfig(settings);
|
const config = await loadCliConfig(settings);
|
||||||
expect(result.config.getTelemetry()).toBe(false);
|
expect(config.getTelemetry()).toBe(false);
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -18,10 +18,7 @@ import {
|
||||||
ApprovalMode,
|
ApprovalMode,
|
||||||
} from '@gemini-code/core';
|
} from '@gemini-code/core';
|
||||||
import { Settings } from './settings.js';
|
import { Settings } from './settings.js';
|
||||||
import {
|
import { getEffectiveModel } from '../utils/modelCheck.js';
|
||||||
getEffectiveModel,
|
|
||||||
type EffectiveModelCheckResult,
|
|
||||||
} from '../utils/modelCheck.js';
|
|
||||||
import { getCliVersion } from '../utils/version.js';
|
import { getCliVersion } from '../utils/version.js';
|
||||||
|
|
||||||
// Simple console logger for now - replace with actual logger if available
|
// Simple console logger for now - replace with actual logger if available
|
||||||
|
@ -119,17 +116,10 @@ export async function loadHierarchicalGeminiMemory(
|
||||||
return loadServerHierarchicalMemory(currentWorkingDirectory, debugMode);
|
return loadServerHierarchicalMemory(currentWorkingDirectory, debugMode);
|
||||||
}
|
}
|
||||||
|
|
||||||
export interface LoadCliConfigResult {
|
|
||||||
config: Config;
|
|
||||||
modelWasSwitched: boolean;
|
|
||||||
originalModelBeforeSwitch?: string;
|
|
||||||
finalModel: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadCliConfig(
|
export async function loadCliConfig(
|
||||||
settings: Settings,
|
settings: Settings,
|
||||||
geminiIgnorePatterns: string[],
|
geminiIgnorePatterns: string[],
|
||||||
): Promise<LoadCliConfigResult> {
|
): Promise<Config> {
|
||||||
loadEnvironment();
|
loadEnvironment();
|
||||||
|
|
||||||
const geminiApiKey = process.env.GEMINI_API_KEY;
|
const geminiApiKey = process.env.GEMINI_API_KEY;
|
||||||
|
@ -180,19 +170,8 @@ export async function loadCliConfig(
|
||||||
const useVertexAI = hasGeminiApiKey ? false : undefined;
|
const useVertexAI = hasGeminiApiKey ? false : undefined;
|
||||||
|
|
||||||
let modelToUse = argv.model || DEFAULT_GEMINI_MODEL;
|
let modelToUse = argv.model || DEFAULT_GEMINI_MODEL;
|
||||||
let modelSwitched = false;
|
|
||||||
let originalModel: string | undefined = undefined;
|
|
||||||
|
|
||||||
if (apiKeyForServer) {
|
if (apiKeyForServer) {
|
||||||
const checkResult: EffectiveModelCheckResult = await getEffectiveModel(
|
modelToUse = await getEffectiveModel(apiKeyForServer, modelToUse);
|
||||||
apiKeyForServer,
|
|
||||||
modelToUse,
|
|
||||||
);
|
|
||||||
if (checkResult.switched) {
|
|
||||||
modelSwitched = true;
|
|
||||||
originalModel = checkResult.originalModelIfSwitched;
|
|
||||||
modelToUse = checkResult.effectiveModel;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
const configParams: ConfigParameters = {
|
const configParams: ConfigParameters = {
|
||||||
|
@ -227,11 +206,5 @@ export async function loadCliConfig(
|
||||||
settings.fileFiltering?.allowBuildArtifacts,
|
settings.fileFiltering?.allowBuildArtifacts,
|
||||||
};
|
};
|
||||||
|
|
||||||
const config = createServerConfig(configParams);
|
return createServerConfig(configParams);
|
||||||
return {
|
|
||||||
config,
|
|
||||||
modelWasSwitched: modelSwitched,
|
|
||||||
originalModelBeforeSwitch: originalModel,
|
|
||||||
finalModel: modelToUse,
|
|
||||||
};
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -66,18 +66,11 @@ export async function main() {
|
||||||
process.exit(1);
|
process.exit(1);
|
||||||
}
|
}
|
||||||
|
|
||||||
const { config, modelWasSwitched, originalModelBeforeSwitch, finalModel } =
|
const config = await loadCliConfig(settings.merged, geminiIgnorePatterns);
|
||||||
await loadCliConfig(settings.merged, geminiIgnorePatterns);
|
|
||||||
|
|
||||||
// Initialize centralized FileDiscoveryService
|
// Initialize centralized FileDiscoveryService
|
||||||
await config.getFileService();
|
await config.getFileService();
|
||||||
|
|
||||||
if (modelWasSwitched && originalModelBeforeSwitch) {
|
|
||||||
console.log(
|
|
||||||
`[INFO] Your configured model (${originalModelBeforeSwitch}) was temporarily unavailable. Switched to ${finalModel} for this session.`,
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
if (settings.merged.theme) {
|
if (settings.merged.theme) {
|
||||||
if (!themeManager.setActiveTheme(settings.merged.theme)) {
|
if (!themeManager.setActiveTheme(settings.merged.theme)) {
|
||||||
// If the theme is not found during initial load, log a warning and continue.
|
// If the theme is not found during initial load, log a warning and continue.
|
||||||
|
@ -176,9 +169,8 @@ async function loadNonInteractiveConfig(
|
||||||
...settings.merged,
|
...settings.merged,
|
||||||
coreTools: nonInteractiveTools,
|
coreTools: nonInteractiveTools,
|
||||||
};
|
};
|
||||||
const nonInteractiveConfigResult = await loadCliConfig(
|
return await loadCliConfig(
|
||||||
nonInteractiveSettings,
|
nonInteractiveSettings,
|
||||||
config.getGeminiIgnorePatterns(),
|
config.getGeminiIgnorePatterns(),
|
||||||
);
|
);
|
||||||
return nonInteractiveConfigResult.config;
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -5,10 +5,7 @@
|
||||||
*/
|
*/
|
||||||
|
|
||||||
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
import { describe, it, expect, vi, beforeEach, afterEach } from 'vitest';
|
||||||
import {
|
import { getEffectiveModel } from './modelCheck.js';
|
||||||
getEffectiveModel,
|
|
||||||
type EffectiveModelCheckResult,
|
|
||||||
} from './modelCheck.js';
|
|
||||||
import {
|
import {
|
||||||
DEFAULT_GEMINI_MODEL,
|
DEFAULT_GEMINI_MODEL,
|
||||||
DEFAULT_GEMINI_FLASH_MODEL,
|
DEFAULT_GEMINI_FLASH_MODEL,
|
||||||
|
@ -45,13 +42,10 @@ describe('getEffectiveModel', () => {
|
||||||
});
|
});
|
||||||
|
|
||||||
describe('when currentConfiguredModel is not DEFAULT_GEMINI_MODEL', () => {
|
describe('when currentConfiguredModel is not DEFAULT_GEMINI_MODEL', () => {
|
||||||
it('should return the currentConfiguredModel and switched: false without fetching', async () => {
|
it('should return the currentConfiguredModel without fetching', async () => {
|
||||||
const customModel = 'custom-model-name';
|
const customModel = 'custom-model-name';
|
||||||
const result = await getEffectiveModel(apiKey, customModel);
|
const result = await getEffectiveModel(apiKey, customModel);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual(customModel);
|
||||||
effectiveModel: customModel,
|
|
||||||
switched: false,
|
|
||||||
});
|
|
||||||
expect(fetch).not.toHaveBeenCalled();
|
expect(fetch).not.toHaveBeenCalled();
|
||||||
});
|
});
|
||||||
});
|
});
|
||||||
|
@ -62,15 +56,8 @@ describe('getEffectiveModel', () => {
|
||||||
ok: false,
|
ok: false,
|
||||||
status: 429,
|
status: 429,
|
||||||
});
|
});
|
||||||
const result: EffectiveModelCheckResult = await getEffectiveModel(
|
const result = await getEffectiveModel(apiKey, DEFAULT_GEMINI_MODEL);
|
||||||
apiKey,
|
expect(result).toEqual(DEFAULT_GEMINI_FLASH_MODEL);
|
||||||
DEFAULT_GEMINI_MODEL,
|
|
||||||
);
|
|
||||||
expect(result).toEqual({
|
|
||||||
effectiveModel: DEFAULT_GEMINI_FLASH_MODEL,
|
|
||||||
switched: true,
|
|
||||||
originalModelIfSwitched: DEFAULT_GEMINI_MODEL,
|
|
||||||
});
|
|
||||||
expect(fetch).toHaveBeenCalledTimes(1);
|
expect(fetch).toHaveBeenCalledTimes(1);
|
||||||
expect(fetch).toHaveBeenCalledWith(
|
expect(fetch).toHaveBeenCalledWith(
|
||||||
`https://generativelanguage.googleapis.com/v1beta/models/${DEFAULT_GEMINI_MODEL}:generateContent?key=${apiKey}`,
|
`https://generativelanguage.googleapis.com/v1beta/models/${DEFAULT_GEMINI_MODEL}:generateContent?key=${apiKey}`,
|
||||||
|
@ -84,10 +71,7 @@ describe('getEffectiveModel', () => {
|
||||||
status: 200,
|
status: 200,
|
||||||
});
|
});
|
||||||
const result = await getEffectiveModel(apiKey, DEFAULT_GEMINI_MODEL);
|
const result = await getEffectiveModel(apiKey, DEFAULT_GEMINI_MODEL);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual(DEFAULT_GEMINI_MODEL);
|
||||||
effectiveModel: DEFAULT_GEMINI_MODEL,
|
|
||||||
switched: false,
|
|
||||||
});
|
|
||||||
expect(fetch).toHaveBeenCalledTimes(1);
|
expect(fetch).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -97,20 +81,14 @@ describe('getEffectiveModel', () => {
|
||||||
status: 500,
|
status: 500,
|
||||||
});
|
});
|
||||||
const result = await getEffectiveModel(apiKey, DEFAULT_GEMINI_MODEL);
|
const result = await getEffectiveModel(apiKey, DEFAULT_GEMINI_MODEL);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual(DEFAULT_GEMINI_MODEL);
|
||||||
effectiveModel: DEFAULT_GEMINI_MODEL,
|
|
||||||
switched: false,
|
|
||||||
});
|
|
||||||
expect(fetch).toHaveBeenCalledTimes(1);
|
expect(fetch).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
it('should return DEFAULT_GEMINI_MODEL if fetch throws a network error', async () => {
|
it('should return DEFAULT_GEMINI_MODEL if fetch throws a network error', async () => {
|
||||||
(fetch as vi.Mock).mockRejectedValueOnce(new Error('Network error'));
|
(fetch as vi.Mock).mockRejectedValueOnce(new Error('Network error'));
|
||||||
const result = await getEffectiveModel(apiKey, DEFAULT_GEMINI_MODEL);
|
const result = await getEffectiveModel(apiKey, DEFAULT_GEMINI_MODEL);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual(DEFAULT_GEMINI_MODEL);
|
||||||
effectiveModel: DEFAULT_GEMINI_MODEL,
|
|
||||||
switched: false,
|
|
||||||
});
|
|
||||||
expect(fetch).toHaveBeenCalledTimes(1);
|
expect(fetch).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
@ -146,10 +124,7 @@ describe('getEffectiveModel', () => {
|
||||||
|
|
||||||
expect(mockAbort).toHaveBeenCalledTimes(0); // setTimeout calls controller.abort(), not our direct mockAbort
|
expect(mockAbort).toHaveBeenCalledTimes(0); // setTimeout calls controller.abort(), not our direct mockAbort
|
||||||
expect(abortControllerInstance.abort).toHaveBeenCalledTimes(1);
|
expect(abortControllerInstance.abort).toHaveBeenCalledTimes(1);
|
||||||
expect(result).toEqual({
|
expect(result).toEqual(DEFAULT_GEMINI_MODEL);
|
||||||
effectiveModel: DEFAULT_GEMINI_MODEL,
|
|
||||||
switched: false,
|
|
||||||
});
|
|
||||||
expect(fetch).toHaveBeenCalledTimes(1);
|
expect(fetch).toHaveBeenCalledTimes(1);
|
||||||
});
|
});
|
||||||
|
|
||||||
|
|
|
@ -9,12 +9,6 @@ import {
|
||||||
DEFAULT_GEMINI_FLASH_MODEL,
|
DEFAULT_GEMINI_FLASH_MODEL,
|
||||||
} from '../config/config.js';
|
} from '../config/config.js';
|
||||||
|
|
||||||
export interface EffectiveModelCheckResult {
|
|
||||||
effectiveModel: string;
|
|
||||||
switched: boolean;
|
|
||||||
originalModelIfSwitched?: string;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks if the default "pro" model is rate-limited and returns a fallback "flash"
|
* Checks if the default "pro" model is rate-limited and returns a fallback "flash"
|
||||||
* model if necessary. This function is designed to be silent.
|
* model if necessary. This function is designed to be silent.
|
||||||
|
@ -26,10 +20,10 @@ export interface EffectiveModelCheckResult {
|
||||||
export async function getEffectiveModel(
|
export async function getEffectiveModel(
|
||||||
apiKey: string,
|
apiKey: string,
|
||||||
currentConfiguredModel: string,
|
currentConfiguredModel: string,
|
||||||
): Promise<EffectiveModelCheckResult> {
|
): Promise<string> {
|
||||||
if (currentConfiguredModel !== DEFAULT_GEMINI_MODEL) {
|
if (currentConfiguredModel !== DEFAULT_GEMINI_MODEL) {
|
||||||
// Only check if the user is trying to use the specific pro model we want to fallback from.
|
// Only check if the user is trying to use the specific pro model we want to fallback from.
|
||||||
return { effectiveModel: currentConfiguredModel, switched: false };
|
return currentConfiguredModel;
|
||||||
}
|
}
|
||||||
|
|
||||||
const modelToTest = DEFAULT_GEMINI_MODEL;
|
const modelToTest = DEFAULT_GEMINI_MODEL;
|
||||||
|
@ -59,17 +53,16 @@ export async function getEffectiveModel(
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
|
|
||||||
if (response.status === 429) {
|
if (response.status === 429) {
|
||||||
return {
|
console.log(
|
||||||
effectiveModel: fallbackModel,
|
`[INFO] Your configured model (${modelToTest}) was temporarily unavailable. Switched to ${fallbackModel} for this session.`,
|
||||||
switched: true,
|
);
|
||||||
originalModelIfSwitched: modelToTest,
|
return fallbackModel;
|
||||||
};
|
|
||||||
}
|
}
|
||||||
// For any other case (success, other error codes), we stick to the original model.
|
// For any other case (success, other error codes), we stick to the original model.
|
||||||
return { effectiveModel: currentConfiguredModel, switched: false };
|
return currentConfiguredModel;
|
||||||
} catch (_error) {
|
} catch (_error) {
|
||||||
clearTimeout(timeoutId);
|
clearTimeout(timeoutId);
|
||||||
// On timeout or any other fetch error, stick to the original model.
|
// On timeout or any other fetch error, stick to the original model.
|
||||||
return { effectiveModel: currentConfiguredModel, switched: false };
|
return currentConfiguredModel;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue