feat(modelAvailabilityService): integrate model availability service into backend logic (#14470)

This commit is contained in:
Adam Weidman
2025-12-08 06:44:34 -08:00
committed by GitHub
parent 7a72037572
commit 8f4f8baa81
20 changed files with 1611 additions and 119 deletions

View File

@@ -12,17 +12,14 @@ import {
PREVIEW_GEMINI_MODEL,
} from '../config/models.js';
import { logFlashFallback, FlashFallbackEvent } from '../telemetry/index.js';
import { coreEvents } from '../utils/events.js';
import { openBrowserSecurely } from '../utils/secure-browser-launcher.js';
import { debugLogger } from '../utils/debugLogger.js';
import { getErrorMessage } from '../utils/errors.js';
import { ModelNotFoundError } from '../utils/httpErrors.js';
import {
RetryableQuotaError,
TerminalQuotaError,
} from '../utils/googleQuotaErrors.js';
import { TerminalQuotaError } from '../utils/googleQuotaErrors.js';
import { coreEvents } from '../utils/events.js';
import type { FallbackIntent, FallbackRecommendation } from './types.js';
import type { FailureKind } from '../availability/modelPolicy.js';
import { classifyFailureKind } from '../availability/errorClassification.js';
import {
buildFallbackPolicyContext,
resolvePolicyChain,
@@ -126,6 +123,9 @@ async function handlePolicyDrivenFallback(
chain,
failedModel,
);
const failureKind = classifyFailureKind(error);
if (!candidates.length) {
return null;
}
@@ -145,7 +145,7 @@ async function handlePolicyDrivenFallback(
return null;
}
const failureKind = classifyFailureKind(error);
// failureKind is already declared and calculated above
const action = resolvePolicyAction(failureKind, selectedPolicy);
if (action === 'silent') {
@@ -183,6 +183,7 @@ async function handlePolicyDrivenFallback(
failedModel,
fallbackModel,
authType,
error, // Pass the error so processIntent can handle preview-specific logic
);
} catch (handlerError) {
debugLogger.error('Fallback handler failed:', handlerError);
@@ -209,25 +210,42 @@ async function processIntent(
authType?: string,
error?: unknown,
): Promise<boolean> {
const isAvailabilityEnabled = config.isModelAvailabilityServiceEnabled();
switch (intent) {
case 'retry_always':
// If the error is non-retryable, e.g. TerminalQuota Error, trigger a regular fallback to flash.
// For all other errors, activate previewModel fallback.
if (
failedModel === PREVIEW_GEMINI_MODEL &&
!(error instanceof TerminalQuotaError)
) {
activatePreviewModelFallbackMode(config);
if (isAvailabilityEnabled) {
// TODO(telemetry): Implement generic fallback event logging. Existing
// logFlashFallback is specific to a single Model.
config.setActiveModel(fallbackModel);
} else {
activateFallbackMode(config, authType);
// If the error is non-retryable, e.g. TerminalQuota Error, trigger a regular fallback to flash.
// For all other errors, activate previewModel fallback.
if (
failedModel === PREVIEW_GEMINI_MODEL &&
!(error instanceof TerminalQuotaError)
) {
activatePreviewModelFallbackMode(config);
} else {
activateFallbackMode(config, authType);
}
}
return true;
case 'retry_once':
if (isAvailabilityEnabled) {
config.setActiveModel(fallbackModel);
}
return true;
case 'stop':
activateFallbackMode(config, authType);
if (isAvailabilityEnabled) {
// TODO(telemetry): Implement generic fallback event logging. Existing
// logFlashFallback is specific to a single Model.
config.setActiveModel(fallbackModel);
} else {
activateFallbackMode(config, authType);
}
return false;
case 'retry_later':
@@ -260,16 +278,3 @@ function activatePreviewModelFallbackMode(config: Config) {
// We might want a specific event for Preview Model fallback, but for now we just set the mode.
}
}
function classifyFailureKind(error?: unknown): FailureKind {
if (error instanceof TerminalQuotaError) {
return 'terminal';
}
if (error instanceof RetryableQuotaError) {
return 'transient';
}
if (error instanceof ModelNotFoundError) {
return 'not_found';
}
return 'unknown';
}