🌳
pt0/serverF/aiF/classifyOrrErrAI.mts
1export type OrrErrClass = 'permanent' | 'rateLimit' | 'transient' | 'auth' | 'unknown'
3const permanentMsgRe = /model|invalid|not found|deprecat|unsupported/i
4const rateLimitMsgRe = /rate.?limit|429|too many requests|quota|throttl/i
5const transientMsgRe = /network|fetch failed|timeout|econn|etimedout|abort|socket hang up|5\d{2}\b/i
7const classifyByStatus = (code: number): OrrErrClass | undefined => {
8 if (code === 401 || code === 403) return 'auth'
9 if (code === 429) return 'rateLimit'
10 if (code >= 500) return 'transient'
11 if (code === 400 || code === 404) return 'permanent'
12 return undefined
15export const classifyOrrErr = (err: any): OrrErrClass => {
16 const msg = String(err?.message || '')
17 const statusMatch = msg.match(/OpenRouter\s+(\d{3})/) || msg.match(/\b(\d{3})\b/)
18 if (statusMatch) {
19 const classified = classifyByStatus(Number(statusMatch[1]))
20 if (classified) return classified
21 }
22 if (err?.name === 'AbortError' || transientMsgRe.test(msg)) return 'transient'
23 if (rateLimitMsgRe.test(msg)) return 'rateLimit'
24 if (permanentMsgRe.test(msg)) return 'permanent'
25 return 'unknown'