🌳
pt0/serverF/aiF/modelFallbackAI.mts
2import { classifyOrrErr, type OrrErrClass } from './classifyOrrErrAI.mts'
6export type ModelAttempt = {model: string, classification: OrrErrClass, errMsg: string, at: number}
7export type FallbackDetailedResult<T> = {result?: T, attempts: ModelAttempt[]}
9export const fmtExhaustionMsg = (attempts: ModelAttempt[]) =>
10 attempts.map(a => `${a.model} (${a.classification}: ${a.errMsg.slice(0, 80)})`).join('; ')
12const runFallback = async <T,>({
13 candidates, callFn, isSuccess = (r: T) => r != null, context = 'modelFallback',
14}: {
15 candidates: string[]
16 callFn: (model: string) => Promise<T>
17 isSuccess?: (r: T) => boolean
18 context?: string
19}): Promise<FallbackDetailedResult<T>> => {
20 const attempts: ModelAttempt[] = []
21 for (const model of candidates) {
22 if (isModelFailed(model)) {
23 const info = getFailedModelInfo(model)
24 attempts.push({model, classification: info?.classification || 'permanent', errMsg: 'pre-blacklisted', at: Date.now()})
25 continue
26 }
27 try {
28 const result = await callFn(model)
29 if (isSuccess(result)) return {result, attempts}
30 attempts.push({model, classification: 'unknown', errMsg: 'no result', at: Date.now()})
31 mcpDebugLog(`${context}: no result, trying next ${model}`)
32 } catch (err: any) {
33 const classification = classifyOrrErr(err)
34 const errMsg = err?.message || String(err)
35 markModelFailed(model, classification)
36 attempts.push({model, classification, errMsg, at: Date.now()})
37 mcpDebugLog(`${context}: errored, trying next ${model} ${classification}: ${errMsg}`)
38 if (classification === 'permanent') await noThrowNotifErr(`modelOffline ${model}`, {context, classification, errMsg})
39 }
40 }
41 return {result: undefined, attempts}
44export const tryWithModelFallback = async <T,>(opts: {
45 candidates: string[]
46 callFn: (model: string) => Promise<T>
47 isSuccess?: (r: T) => boolean
48 context?: string
49}): Promise<T | undefined> => (await runFallback<T>(opts)).result
51export const tryWithModelFallbackDetailed = async <T,>(opts: {
52 candidates: string[]
53 callFn: (model: string) => Promise<T>
54 isSuccess?: (r: T) => boolean
55 context?: string
56}): Promise<FallbackDetailedResult<T>> => runFallback<T>(opts)