Skip to content

Commit e5c8aec

Browse files
author
Theodore Li
committed
Add rate limiting (3 tries, exponential backoff)
1 parent 3e6527a commit e5c8aec

File tree

1 file changed

+52
-1
lines changed

1 file changed

+52
-1
lines changed

apps/sim/tools/index.ts

Lines changed: 52 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -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

Comments
 (0)