@@ -102,6 +102,50 @@ async function injectHostedKeyIfNeeded(
102102 return true // Bill the user
103103}
104104
105+ /**
106+ * Check if an error is a rate limit (throttling) error
107+ */
108+ function isRateLimitError ( error : unknown ) : boolean {
109+ if ( error && typeof error === 'object' ) {
110+ const status = ( error as { status ?: number } ) . status
111+ // 429 = Too Many Requests, 503 = Service Unavailable (sometimes used for rate limiting)
112+ if ( status === 429 || status === 503 ) return true
113+ }
114+ return false
115+ }
116+
117+ /**
118+ * Execute a function with exponential backoff retry for rate limiting errors.
119+ * Only used for hosted key requests.
120+ */
121+ async function executeWithRetry < T > (
122+ fn : ( ) => Promise < T > ,
123+ requestId : string ,
124+ toolId : string ,
125+ maxRetries = 3 ,
126+ baseDelayMs = 1000
127+ ) : Promise < T > {
128+ let lastError : unknown
129+
130+ for ( let attempt = 0 ; attempt <= maxRetries ; attempt ++ ) {
131+ try {
132+ return await fn ( )
133+ } catch ( error ) {
134+ lastError = error
135+
136+ if ( ! isRateLimitError ( error ) || attempt === maxRetries ) {
137+ throw error
138+ }
139+
140+ const delayMs = baseDelayMs * Math . pow ( 2 , attempt )
141+ logger . warn ( `[${ requestId } ] Rate limited for ${ toolId } , retrying in ${ delayMs } ms (attempt ${ attempt + 1 } /${ maxRetries } )` )
142+ await new Promise ( ( resolve ) => setTimeout ( resolve , delayMs ) )
143+ }
144+ }
145+
146+ throw lastError
147+ }
148+
105149/**
106150 * Calculate cost based on pricing model
107151 */
@@ -569,7 +613,14 @@ export async function executeTool(
569613 }
570614
571615 // Execute the tool request directly (internal routes use regular fetch, external use SSRF-protected fetch)
572- const result = await executeToolRequest ( toolId , tool , contextParams )
616+ // Wrap with retry logic for hosted keys to handle rate limiting due to higher usage
617+ const result = isUsingHostedKey
618+ ? await executeWithRetry (
619+ ( ) => executeToolRequest ( toolId , tool , contextParams ) ,
620+ requestId ,
621+ toolId
622+ )
623+ : await executeToolRequest ( toolId , tool , contextParams )
573624
574625 // Apply post-processing if available and not skipped
575626 let finalResult = result
0 commit comments