fix: close recovery review gaps

This commit is contained in:
Tianyi Cui
2026-07-20 22:11:26 +08:00
parent 40f7195266
commit cc2e14f76e
10 changed files with 62 additions and 11 deletions

View File

@@ -35,7 +35,10 @@ function classifyPiAiError(message: string): string {
if (/\b400\b|invalid.?request/i.test(message)) return 'INVALID_REQUEST'
if (/\b5\d\d\b/.test(message)) return 'SERVER'
if (/\btime(?:d)?\s*out\b|timeout/i.test(message)) return 'TIMEOUT'
if (/\b(?:network|connection|socket|fetch)\b|\bECONN[A-Z]+\b/i.test(message)) return 'TRANSPORT'
if (/\b(?:network|connection|socket|fetch)\b|\bECONN[A-Z]+\b/i.test(message)
|| /\b(?:other side closed|HTTP2 request did not get a response|WebSocket closed unexpectedly)\b/i.test(message)) {
return 'TRANSPORT'
}
return 'PI_AI_ERROR'
}

View File

@@ -535,6 +535,10 @@ describe('mapStopReason / mapUsage', () => {
.toMatchObject({ kind: 'error', failure: { code: 'RATE_LIMIT' } })
expect(mapStopReason(assistant({ stopReason: 'error', errorMessage: 'HTTP 429: insufficient_quota' })))
.toMatchObject({ kind: 'error', failure: { code: 'QUOTA' } })
expect(mapStopReason(assistant({
stopReason: 'error',
errorMessage: 'OpenAI API error (429): You exceeded your current quota, please check your plan and billing details.',
}))).toMatchObject({ kind: 'error', failure: { code: 'QUOTA' } })
expect(mapStopReason(assistant({ stopReason: 'error', errorMessage: 'HTTP 500: backend down' })))
.toMatchObject({ kind: 'error', failure: { code: 'SERVER' } })
expect(mapStopReason(assistant({ stopReason: 'error', errorMessage: 'provider timed out' })))
@@ -555,6 +559,15 @@ describe('mapStopReason / mapUsage', () => {
}))).toMatchObject({ kind: 'error', failure: { code: 'INVALID_REQUEST' } })
})
it.each([
'other side closed',
'HTTP2 request did not get a response',
'WebSocket closed unexpectedly',
])('maps pi-ai transport wording %j', (errorMessage) => {
expect(mapStopReason(assistant({ stopReason: 'error', errorMessage })))
.toMatchObject({ kind: 'error', failure: { code: 'TRANSPORT' } })
})
it('uses pi-ai provider-specific overflow classification without losing rate-limit exclusions', () => {
expect(mapStopReason(assistant({
stopReason: 'error',

View File

@@ -74,6 +74,7 @@ export function isContextWindowExceededError(detail: string): boolean {
export function isQuotaExceededError(detail: string): boolean {
return /\binsufficient[\s_-]+(?:quota|balance|credits?)\b/i.test(detail)
|| /\b(?:quota|usage[\s_-]+limit)[\s_-]+(?:exceeded|exhausted|reached)\b/i.test(detail)
|| /\bexceed(?:ed|s)?[\s_-]+(?:(?:your|the)[\s_-]+)?(?:current[\s_-]+)?quota\b/i.test(detail)
|| /\b(?:balance|credits?)[\s_-]+(?:exhausted|depleted)\b/i.test(detail)
|| /\bout[\s_-]+of[\s_-]+(?:credits?|budget)\b/i.test(detail)
}

View File

@@ -90,6 +90,7 @@ describe('LlmService', () => {
'account balance depleted',
'usage-limit-exceeded',
'out of credits',
'OpenAI API error (429): You exceeded your current quota, please check your plan and billing details.',
]) expect(isQuotaExceededError(detail)).toBe(true)
expect(isQuotaExceededError('HTTP 429: rate limit reached')).toBe(false)
expect(isQuotaExceededError('quota resets in one minute')).toBe(false)