mirror of
https://github.com/google-gemini/gemini-cli.git
synced 2026-04-18 09:11:55 -07:00
FEAT: Add availabilityService (#81)
* auto and fallback work * test fixes * fixes * Show model dialog even if there's no more fallback option * fix tests * fix failing test * disable showInDialog for model in settings * revert package-lock.json * remove dup line --------- Co-authored-by: Sehoon Shon <sshon@google.com>
This commit is contained in:
committed by
Tommaso Sciortino
parent
4b3d858f31
commit
af94beea11
@@ -19,7 +19,7 @@ describe('policyCatalog', () => {
|
||||
it('returns preview chain when preview enabled', () => {
|
||||
const chain = getModelPolicyChain({ previewEnabled: true });
|
||||
expect(chain[0]?.model).toBe(PREVIEW_GEMINI_MODEL);
|
||||
expect(chain).toHaveLength(3);
|
||||
expect(chain).toHaveLength(2);
|
||||
});
|
||||
|
||||
it('returns default chain when preview disabled', () => {
|
||||
@@ -31,7 +31,7 @@ describe('policyCatalog', () => {
|
||||
it('marks preview transients as sticky retries', () => {
|
||||
const [previewPolicy] = getModelPolicyChain({ previewEnabled: true });
|
||||
expect(previewPolicy.model).toBe(PREVIEW_GEMINI_MODEL);
|
||||
expect(previewPolicy.stateTransitions.transient).toBe('sticky_retry');
|
||||
expect(previewPolicy.stateTransitions.transient).toBe('terminal');
|
||||
});
|
||||
|
||||
it('applies default actions and state transitions for unspecified kinds', () => {
|
||||
|
||||
@@ -13,6 +13,7 @@ import type {
|
||||
import {
|
||||
DEFAULT_GEMINI_FLASH_MODEL,
|
||||
DEFAULT_GEMINI_MODEL,
|
||||
PREVIEW_GEMINI_FLASH_MODEL,
|
||||
PREVIEW_GEMINI_MODEL,
|
||||
} from '../config/models.js';
|
||||
import type { UserTierId } from '../code_assist/types.js';
|
||||
@@ -48,13 +49,8 @@ const DEFAULT_CHAIN: ModelPolicyChain = [
|
||||
];
|
||||
|
||||
const PREVIEW_CHAIN: ModelPolicyChain = [
|
||||
definePolicy({
|
||||
model: PREVIEW_GEMINI_MODEL,
|
||||
stateTransitions: { transient: 'sticky_retry' },
|
||||
actions: { transient: 'silent' },
|
||||
}),
|
||||
definePolicy({ model: DEFAULT_GEMINI_MODEL }),
|
||||
definePolicy({ model: DEFAULT_GEMINI_FLASH_MODEL, isLastResort: true }),
|
||||
definePolicy({ model: PREVIEW_GEMINI_MODEL }),
|
||||
definePolicy({ model: PREVIEW_GEMINI_FLASH_MODEL, isLastResort: true }),
|
||||
];
|
||||
|
||||
/**
|
||||
|
||||
@@ -12,6 +12,7 @@ import {
|
||||
} from './policyHelpers.js';
|
||||
import { createDefaultPolicy } from './policyCatalog.js';
|
||||
import type { Config } from '../config/config.js';
|
||||
import { DEFAULT_GEMINI_MODEL_AUTO } from '../config/models.js';
|
||||
|
||||
const createMockConfig = (overrides: Partial<Config> = {}): Config =>
|
||||
({
|
||||
@@ -43,7 +44,7 @@ describe('policyHelpers', () => {
|
||||
|
||||
it('returns the default chain when active model is "auto"', () => {
|
||||
const config = createMockConfig({
|
||||
getModel: () => 'auto',
|
||||
getModel: () => DEFAULT_GEMINI_MODEL_AUTO,
|
||||
});
|
||||
const chain = resolvePolicyChain(config);
|
||||
|
||||
@@ -63,7 +64,7 @@ describe('policyHelpers', () => {
|
||||
];
|
||||
const context = buildFallbackPolicyContext(chain, 'b');
|
||||
expect(context.failedPolicy?.model).toBe('b');
|
||||
expect(context.candidates.map((p) => p.model)).toEqual(['c', 'a']);
|
||||
expect(context.candidates.map((p) => p.model)).toEqual(['c']);
|
||||
});
|
||||
|
||||
it('returns full chain when model is not in policy list', () => {
|
||||
|
||||
@@ -14,7 +14,7 @@ import type {
|
||||
RetryAvailabilityContext,
|
||||
} from './modelPolicy.js';
|
||||
import { createDefaultPolicy, getModelPolicyChain } from './policyCatalog.js';
|
||||
import { DEFAULT_GEMINI_MODEL, getEffectiveModel } from '../config/models.js';
|
||||
import { DEFAULT_GEMINI_MODEL, resolveModel } from '../config/models.js';
|
||||
import type { ModelSelectionResult } from './modelAvailabilityService.js';
|
||||
|
||||
/**
|
||||
@@ -24,23 +24,29 @@ import type { ModelSelectionResult } from './modelAvailabilityService.js';
|
||||
export function resolvePolicyChain(
|
||||
config: Config,
|
||||
preferredModel?: string,
|
||||
wrapsAround: boolean = false,
|
||||
): ModelPolicyChain {
|
||||
// Availability uses the active/requested model directly. Legacy fallback logic
|
||||
// (getEffectiveModel) only applies when availability is disabled.
|
||||
const modelFromConfig =
|
||||
preferredModel ?? config.getActiveModel?.() ?? config.getModel();
|
||||
|
||||
const isPreviewRequest =
|
||||
modelFromConfig.includes('gemini-3') ||
|
||||
modelFromConfig.includes('preview') ||
|
||||
modelFromConfig === 'fiercefalcon';
|
||||
|
||||
const chain = getModelPolicyChain({
|
||||
previewEnabled: !!config.getPreviewFeatures(),
|
||||
previewEnabled: isPreviewRequest,
|
||||
userTier: config.getUserTier(),
|
||||
});
|
||||
// TODO: This will be replaced when we get rid of Fallback Modes.
|
||||
// Switch to getActiveModel()
|
||||
const activeModel =
|
||||
preferredModel ??
|
||||
getEffectiveModel(config.getModel(), config.isInFallbackMode());
|
||||
const activeModel = resolveModel(modelFromConfig);
|
||||
|
||||
if (activeModel === 'auto') {
|
||||
return [...chain];
|
||||
}
|
||||
|
||||
if (chain.some((policy) => policy.model === activeModel)) {
|
||||
return [...chain];
|
||||
const activeIndex = chain.findIndex((policy) => policy.model === activeModel);
|
||||
if (activeIndex !== -1) {
|
||||
return wrapsAround
|
||||
? [...chain.slice(activeIndex), ...chain.slice(0, activeIndex)]
|
||||
: [...chain.slice(activeIndex)];
|
||||
}
|
||||
|
||||
// If the user specified a model not in the default chain, we assume they want
|
||||
@@ -51,10 +57,14 @@ export function resolvePolicyChain(
|
||||
/**
|
||||
* Produces the failed policy (if it exists in the chain) and the list of
|
||||
* fallback candidates that follow it.
|
||||
* @param chain - The ordered list of available model policies.
|
||||
* @param failedModel - The identifier of the model that failed.
|
||||
* @param wrapsAround - If true, treats the chain as a circular buffer.
|
||||
*/
|
||||
export function buildFallbackPolicyContext(
|
||||
chain: ModelPolicyChain,
|
||||
failedModel: string,
|
||||
wrapsAround: boolean = false,
|
||||
): {
|
||||
failedPolicy?: ModelPolicy;
|
||||
candidates: ModelPolicy[];
|
||||
@@ -65,9 +75,12 @@ export function buildFallbackPolicyContext(
|
||||
}
|
||||
// Return [candidates_after, candidates_before] to prioritize downgrades
|
||||
// (continuing the chain) before wrapping around to upgrades.
|
||||
const candidates = wrapsAround
|
||||
? [...chain.slice(index + 1), ...chain.slice(0, index)]
|
||||
: [...chain.slice(index + 1)];
|
||||
return {
|
||||
failedPolicy: chain[index],
|
||||
candidates: [...chain.slice(index + 1), ...chain.slice(0, index)],
|
||||
candidates,
|
||||
};
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user