Skip to content

Commit 2a873e5

Browse files
fix(metrics): correct hosted-key failure attribution
- Re-point used/cost/failed labels at the freshly acquired key after reacquire - Classify quota-style 401/403 as rate_limited (mirror isRateLimitError) - Count returned success:false runs (e.g. deep_research polling) as failed
1 parent f7e580a commit 2a873e5

1 file changed

Lines changed: 24 additions & 9 deletions

File tree

apps/sim/tools/index.ts

Lines changed: 24 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -334,11 +334,11 @@ async function reacquireHostedKey(
334334
params: Record<string, unknown>,
335335
executionContext: ExecutionContext | undefined,
336336
requestId: string
337-
): Promise<boolean> {
338-
if (!tool.hosting) return false
337+
): Promise<string | null> {
338+
if (!tool.hosting) return null
339339
const { envKeyPrefix, apiKeyParam, byokProviderId, rateLimit } = tool.hosting
340340
const { workspaceId } = resolveToolScope(params, executionContext)
341-
if (!workspaceId) return false
341+
if (!workspaceId) return null
342342

343343
const provider = byokProviderId || tool.id
344344
const acquireResult = await getHostedKeyRateLimiter().acquireKey(
@@ -353,14 +353,14 @@ async function reacquireHostedKey(
353353
logger.warn(
354354
`[${requestId}] Re-acquire of hosted key for ${tool.id} failed: ${acquireResult.error ?? 'unknown'}`
355355
)
356-
return false
356+
return null
357357
}
358358

359359
params[apiKeyParam] = acquireResult.key
360360
logger.info(
361361
`[${requestId}] Re-acquired hosted key for ${tool.id} (${acquireResult.envVarName}) after upstream throttling`
362362
)
363-
return true
363+
return acquireResult.envVarName ?? 'unknown'
364364
}
365365

366366
/**
@@ -383,11 +383,19 @@ function isRateLimitError(error: unknown): boolean {
383383
return false
384384
}
385385

386-
/** Map a thrown tool error to a hosted-key failure reason for metrics. */
386+
/**
387+
* Map a thrown tool error to a hosted-key failure reason for metrics. Mirrors
388+
* `isRateLimitError`: some providers signal quota/rate-limit via 401/403 with a
389+
* descriptive message, so those count as `rate_limited`, not `auth`.
390+
*/
387391
function classifyHostedKeyFailure(error: unknown): 'rate_limited' | 'auth' | 'other' {
388392
const status = (error as { status?: number } | null)?.status
389393
if (status === 429 || status === 503) return 'rate_limited'
390-
if (status === 401 || status === 403) return 'auth'
394+
if (status === 401 || status === 403) {
395+
const message = ((error as { message?: string } | null)?.message ?? '').toLowerCase()
396+
if (message.includes('quota') || message.includes('rate limit')) return 'rate_limited'
397+
return 'auth'
398+
}
391399
return 'other'
392400
}
393401

@@ -1178,6 +1186,8 @@ export async function executeTool(
11781186
requestId,
11791187
hostedKeyInfo.envVarName
11801188
)
1189+
} else if (hostedKeyForMetrics) {
1190+
hostedKeyMetrics.recordFailed({ ...hostedKeyForMetrics, reason: 'other' })
11811191
}
11821192

11831193
const strippedOutput = postProcessToolOutput(normalizedToolId, finalResult.output ?? {})
@@ -1202,13 +1212,16 @@ export async function executeTool(
12021212
envVarName: hostedKeyInfo.envVarName!,
12031213
executionContext,
12041214
reacquireAfterRetriesExhausted: async () => {
1205-
const reacquired = await reacquireHostedKey(
1215+
const reacquiredEnvVar = await reacquireHostedKey(
12061216
tool,
12071217
contextParams,
12081218
executionContext,
12091219
requestId
12101220
)
1211-
if (!reacquired) return null
1221+
if (!reacquiredEnvVar) return null
1222+
// Re-point metric labels at the freshly acquired key.
1223+
hostedKeyInfo.envVarName = reacquiredEnvVar
1224+
if (hostedKeyForMetrics) hostedKeyForMetrics.key = reacquiredEnvVar
12121225
return () => executeToolRequest(toolId, tool, contextParams)
12131226
},
12141227
})
@@ -1244,6 +1257,8 @@ export async function executeTool(
12441257
requestId,
12451258
hostedKeyInfo.envVarName
12461259
)
1260+
} else if (hostedKeyForMetrics) {
1261+
hostedKeyMetrics.recordFailed({ ...hostedKeyForMetrics, reason: 'other' })
12471262
}
12481263

12491264
const strippedOutput = postProcessToolOutput(normalizedToolId, finalResult.output ?? {})

0 commit comments

Comments
 (0)