diff --git a/packages/core/src/config/config.ts b/packages/core/src/config/config.ts index ba8f5d508b..75b95aea7c 100644 --- a/packages/core/src/config/config.ts +++ b/packages/core/src/config/config.ts @@ -1018,7 +1018,7 @@ export class Config implements McpContext, AgentLoopContext { params.gemmaModelRouter?.classifier?.model ?? 'gemma3-1b-gpu-custom', }, }; - this.retryFetchErrors = params.retryFetchErrors ?? false; + this.retryFetchErrors = params.retryFetchErrors ?? true; this.maxAttempts = Math.min( params.maxAttempts ?? DEFAULT_MAX_ATTEMPTS, DEFAULT_MAX_ATTEMPTS, diff --git a/packages/core/src/core/baseLlmClient.ts b/packages/core/src/core/baseLlmClient.ts index 0de4dd1e20..8888a41bbf 100644 --- a/packages/core/src/core/baseLlmClient.ts +++ b/packages/core/src/core/baseLlmClient.ts @@ -21,6 +21,8 @@ import { getErrorMessage } from '../utils/errors.js'; import { logMalformedJsonResponse } from '../telemetry/loggers.js'; import { MalformedJsonResponseEvent, LlmRole } from '../telemetry/types.js'; import { retryWithBackoff } from '../utils/retry.js'; +import { coreEvents } from '../utils/events.js'; +import { getDisplayString } from '../config/models.js'; import type { ModelConfigKey } from '../services/modelConfigService.js'; import { applyModelSelection, @@ -327,6 +329,17 @@ export class BaseLlmClient { : undefined, authType: this.authType ?? this.config.getContentGeneratorConfig()?.authType, + retryFetchErrors: this.config.getRetryFetchErrors(), + onRetry: (attempt, error, delayMs) => { + coreEvents.emitRetryAttempt({ + attempt, + maxAttempts: + availabilityMaxAttempts ?? maxAttempts ?? DEFAULT_MAX_ATTEMPTS, + delayMs, + error: error instanceof Error ? error.message : String(error), + model: getDisplayString(currentModel), + }); + }, }); } catch (error) { if (abortSignal?.aborted) { diff --git a/packages/core/src/core/client.ts b/packages/core/src/core/client.ts index bbef15d491..42224bec27 100644 --- a/packages/core/src/core/client.ts +++ b/packages/core/src/core/client.ts @@ -29,6 +29,8 @@ import { getCoreSystemPrompt } from './prompts.js'; import { checkNextSpeaker } from '../utils/nextSpeakerChecker.js'; import { reportError } from '../utils/errorReporting.js'; import { GeminiChat } from './geminiChat.js'; +import { coreEvents, CoreEvent } from '../utils/events.js'; +import { getDisplayString , resolveModel, isGemini2Model } from '../config/models.js'; import { retryWithBackoff, type RetryAvailabilityContext, @@ -69,9 +71,7 @@ import { applyModelSelection, createAvailabilityContextProvider, } from '../availability/policyHelpers.js'; -import { resolveModel, isGemini2Model } from '../config/models.js'; import { partToString } from '../utils/partUtils.js'; -import { coreEvents, CoreEvent } from '../utils/events.js'; const MAX_TURNS = 100; @@ -1088,7 +1088,18 @@ export class GeminiClient { onValidationRequired: onValidationRequiredCallback, authType: this.config.getContentGeneratorConfig()?.authType, maxAttempts: availabilityMaxAttempts, + retryFetchErrors: this.config.getRetryFetchErrors(), getAvailabilityContext, + onRetry: (attempt, error, delayMs) => { + coreEvents.emitRetryAttempt({ + attempt, + maxAttempts: + availabilityMaxAttempts ?? this.config.getMaxAttempts(), + delayMs, + error: error instanceof Error ? error.message : String(error), + model: getDisplayString(currentAttemptModel), + }); + }, }); return result; diff --git a/packages/core/src/tools/web-fetch.ts b/packages/core/src/tools/web-fetch.ts index 3170227188..af62c85500 100644 --- a/packages/core/src/tools/web-fetch.ts +++ b/packages/core/src/tools/web-fetch.ts @@ -29,6 +29,7 @@ import { import { LlmRole } from '../telemetry/llmRole.js'; import { WEB_FETCH_TOOL_NAME } from './tool-names.js'; import { debugLogger } from '../utils/debugLogger.js'; +import { coreEvents } from '../utils/events.js'; import { retryWithBackoff } from '../utils/retry.js'; import { WEB_FETCH_DEFINITION } from './definitions/coreTools.js'; import { resolveToolDeclaration } from './definitions/resolver.js'; @@ -212,6 +213,15 @@ class WebFetchToolInvocation extends BaseToolInvocation< }, { retryFetchErrors: this.config.getRetryFetchErrors(), + onRetry: (attempt, error, delayMs) => { + coreEvents.emitRetryAttempt({ + attempt, + maxAttempts: this.config.getMaxAttempts(), + delayMs, + error: error instanceof Error ? error.message : String(error), + model: 'Web Fetch', + }); + }, }, ); @@ -405,6 +415,15 @@ ${textContent} }, { retryFetchErrors: this.config.getRetryFetchErrors(), + onRetry: (attempt, error, delayMs) => { + coreEvents.emitRetryAttempt({ + attempt, + maxAttempts: this.config.getMaxAttempts(), + delayMs, + error: error instanceof Error ? error.message : String(error), + model: 'Web Fetch', + }); + }, }, );