refactor(agents): dedupe model fallback candidate logic

This commit is contained in:
Peter Steinberger
2026-02-15 06:07:01 +00:00
parent adee048247
commit ed03b834d5
+42 -56
View File
@@ -55,19 +55,10 @@ function shouldRethrowAbort(err: unknown): boolean {
return isFallbackAbortError(err) && !isTimeoutError(err); return isFallbackAbortError(err) && !isTimeoutError(err);
} }
function resolveImageFallbackCandidates(params: { function createModelCandidateCollector(allowlist: Set<string> | null | undefined): {
cfg: OpenClawConfig | undefined; candidates: ModelCandidate[];
defaultProvider: string; addCandidate: (candidate: ModelCandidate, enforceAllowlist: boolean) => void;
modelOverride?: string; } {
}): ModelCandidate[] {
const aliasIndex = buildModelAliasIndex({
cfg: params.cfg ?? {},
defaultProvider: params.defaultProvider,
});
const allowlist = buildConfiguredAllowlistKeys({
cfg: params.cfg,
defaultProvider: params.defaultProvider,
});
const seen = new Set<string>(); const seen = new Set<string>();
const candidates: ModelCandidate[] = []; const candidates: ModelCandidate[] = [];
@@ -86,6 +77,39 @@ function resolveImageFallbackCandidates(params: {
candidates.push(candidate); candidates.push(candidate);
}; };
return { candidates, addCandidate };
}
type ModelFallbackErrorHandler = (attempt: {
provider: string;
model: string;
error: unknown;
attempt: number;
total: number;
}) => void | Promise<void>;
type ModelFallbackRunResult<T> = {
result: T;
provider: string;
model: string;
attempts: FallbackAttempt[];
};
function resolveImageFallbackCandidates(params: {
cfg: OpenClawConfig | undefined;
defaultProvider: string;
modelOverride?: string;
}): ModelCandidate[] {
const aliasIndex = buildModelAliasIndex({
cfg: params.cfg ?? {},
defaultProvider: params.defaultProvider,
});
const allowlist = buildConfiguredAllowlistKeys({
cfg: params.cfg,
defaultProvider: params.defaultProvider,
});
const { candidates, addCandidate } = createModelCandidateCollector(allowlist);
const addRaw = (raw: string, enforceAllowlist: boolean) => { const addRaw = (raw: string, enforceAllowlist: boolean) => {
const resolved = resolveModelRefFromString({ const resolved = resolveModelRefFromString({
raw: String(raw ?? ""), raw: String(raw ?? ""),
@@ -156,23 +180,7 @@ function resolveFallbackCandidates(params: {
cfg: params.cfg, cfg: params.cfg,
defaultProvider, defaultProvider,
}); });
const seen = new Set<string>(); const { candidates, addCandidate } = createModelCandidateCollector(allowlist);
const candidates: ModelCandidate[] = [];
const addCandidate = (candidate: ModelCandidate, enforceAllowlist: boolean) => {
if (!candidate.provider || !candidate.model) {
return;
}
const key = modelKey(candidate.provider, candidate.model);
if (seen.has(key)) {
return;
}
if (enforceAllowlist && allowlist && !allowlist.has(key)) {
return;
}
seen.add(key);
candidates.push(candidate);
};
addCandidate(normalizedPrimary, false); addCandidate(normalizedPrimary, false);
@@ -217,19 +225,8 @@ export async function runWithModelFallback<T>(params: {
/** Optional explicit fallbacks list; when provided (even empty), replaces agents.defaults.model.fallbacks. */ /** Optional explicit fallbacks list; when provided (even empty), replaces agents.defaults.model.fallbacks. */
fallbacksOverride?: string[]; fallbacksOverride?: string[];
run: (provider: string, model: string) => Promise<T>; run: (provider: string, model: string) => Promise<T>;
onError?: (attempt: { onError?: ModelFallbackErrorHandler;
provider: string; }): Promise<ModelFallbackRunResult<T>> {
model: string;
error: unknown;
attempt: number;
total: number;
}) => void | Promise<void>;
}): Promise<{
result: T;
provider: string;
model: string;
attempts: FallbackAttempt[];
}> {
const candidates = resolveFallbackCandidates({ const candidates = resolveFallbackCandidates({
cfg: params.cfg, cfg: params.cfg,
provider: params.provider, provider: params.provider,
@@ -335,19 +332,8 @@ export async function runWithImageModelFallback<T>(params: {
cfg: OpenClawConfig | undefined; cfg: OpenClawConfig | undefined;
modelOverride?: string; modelOverride?: string;
run: (provider: string, model: string) => Promise<T>; run: (provider: string, model: string) => Promise<T>;
onError?: (attempt: { onError?: ModelFallbackErrorHandler;
provider: string; }): Promise<ModelFallbackRunResult<T>> {
model: string;
error: unknown;
attempt: number;
total: number;
}) => void | Promise<void>;
}): Promise<{
result: T;
provider: string;
model: string;
attempts: FallbackAttempt[];
}> {
const candidates = resolveImageFallbackCandidates({ const candidates = resolveImageFallbackCandidates({
cfg: params.cfg, cfg: params.cfg,
defaultProvider: DEFAULT_PROVIDER, defaultProvider: DEFAULT_PROVIDER,