Files
boocode/apps/coder/src/services/model-resolution/model-availability.ts
indifferentketchup 524a0deaa1 feat(coder): add model resolution core + multi-batch matcher
Model resolution (from oh-my-openagent/model-core): 6-step priority
resolution pipeline (UI select -> user config -> category default ->
user fallback -> policy chain -> system default), provider fallback
chains, fuzzy model matching, error classification, provider-specific
model ID transforms. 14 files, zero runtime deps.

Multi-batch matcher (from boocontext-audit): 6 batch types
(Observational, Actionable, PreviouslyApplied, Disambiguation,
ResponseAnalysis, LowCriticality) for behavioral guideline evaluation.
RelationalResolver with iterative convergence (DEPENDS_ON,
PRIORITIZES, ENTAILS, TAG_ALL, TAG_PRIORITIZES). SchematicGenerator
abstract class with retry and execution plans. 4 files.
2026-06-08 00:17:55 +00:00

65 lines
1.6 KiB
TypeScript

function normalizeModelName(name: string): string {
return name
.toLowerCase()
.replace(/claude-(opus|sonnet|haiku)-(\d+)[.-](\d+)/g, "claude-$1-$2.$3")
}
export function fuzzyMatchModel(
target: string,
available: Set<string>,
providers?: string[],
): string | null {
if (available.size === 0) {
return null
}
const targetNormalized = normalizeModelName(target)
let candidates = Array.from(available)
if (providers && providers.length > 0) {
const providerSet = new Set(providers)
candidates = candidates.filter((model) => {
const [provider] = model.split("/")
return providerSet.has(provider!)
})
}
if (candidates.length === 0) {
return null
}
const matches = candidates.filter((model) =>
normalizeModelName(model).includes(targetNormalized),
)
if (matches.length === 0) {
return null
}
const exactMatch = matches.find((model) => normalizeModelName(model) === targetNormalized)
if (exactMatch) {
return exactMatch
}
const exactModelIdMatches = matches.filter((model) => {
const modelId = model.split("/").slice(1).join("/")
return normalizeModelName(modelId) === targetNormalized
})
if (exactModelIdMatches.length > 0) {
return exactModelIdMatches.reduce((shortest, current) =>
current.length < shortest.length ? current : shortest,
)
}
return matches.reduce((shortest, current) =>
current.length < shortest.length ? current : shortest,
)
}
export function isModelAvailable(
targetModel: string,
availableModels: Set<string>,
): boolean {
return fuzzyMatchModel(targetModel, availableModels) !== null
}