mirror of
https://github.com/openclaw/openclaw.git
synced 2026-08-12 21:53:00 -06:00
refactor(gateway): isolate model catalog outcomes
This commit is contained in:
@@ -0,0 +1,54 @@
|
||||
import { normalizeProviderId } from "@openclaw/model-catalog-core/provider-id";
|
||||
import type { ModelAuthAvailabilityEvaluation } from "../../agents/model-auth-availability.js";
|
||||
import type { ProviderCatalogOutcome } from "../../plugins/provider-catalog.types.js";
|
||||
|
||||
export function projectPublicProviderCatalogOutcomes(
|
||||
outcomes: readonly ProviderCatalogOutcome[] | undefined,
|
||||
): ProviderCatalogOutcome[] | undefined {
|
||||
return outcomes?.map(({ provider, profileId, status }) => ({
|
||||
provider,
|
||||
...(profileId ? { profileId } : {}),
|
||||
status,
|
||||
}));
|
||||
}
|
||||
|
||||
export function applyProviderCatalogOutcomesToModelAuth(params: {
|
||||
provider: string;
|
||||
modelId: string;
|
||||
outcomes?: readonly ProviderCatalogOutcome[];
|
||||
resolved: ModelAuthAvailabilityEvaluation;
|
||||
evaluateForProfile: (profileId: string) => ModelAuthAvailabilityEvaluation;
|
||||
}): ModelAuthAvailabilityEvaluation {
|
||||
const provider = normalizeProviderId(params.provider);
|
||||
const outcomes =
|
||||
params.outcomes?.filter((outcome) => normalizeProviderId(outcome.provider) === provider) ?? [];
|
||||
const modelId = params.modelId.trim().toLowerCase();
|
||||
const authorizingProfileIds = outcomes.flatMap((outcome) =>
|
||||
outcome.status === "ready" &&
|
||||
outcome.profileId &&
|
||||
outcome.modelIds?.some((candidate) => candidate.trim().toLowerCase() === modelId)
|
||||
? [outcome.profileId]
|
||||
: [],
|
||||
);
|
||||
if (
|
||||
authorizingProfileIds.length > 0 &&
|
||||
!authorizingProfileIds.includes(params.resolved.selectedProfileId ?? "")
|
||||
) {
|
||||
for (const profileId of authorizingProfileIds) {
|
||||
const candidate = params.evaluateForProfile(profileId);
|
||||
if (candidate.availability === true) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return { ...params.resolved, availability: false };
|
||||
}
|
||||
// Stored credentials prove presence, not acceptance. Apply rejection only to
|
||||
// the profile discovery tested, or another valid profile would be hidden.
|
||||
return outcomes.some(
|
||||
(outcome) =>
|
||||
outcome.status === "auth-rejected" &&
|
||||
(outcome.profileId === undefined || outcome.profileId === params.resolved.selectedProfileId),
|
||||
)
|
||||
? { ...params.resolved, availability: false }
|
||||
: params.resolved;
|
||||
}
|
||||
@@ -58,6 +58,10 @@ import { normalizeAgentId } from "../../routing/session-key.js";
|
||||
import type { GatewayAgentRuntime } from "../../shared/session-types.js";
|
||||
import { resolveGatewayModelThinkingProfile } from "../session-utils-model.js";
|
||||
import { createModelsListAuthResolver } from "./models-list-auth-resolver.js";
|
||||
import {
|
||||
applyProviderCatalogOutcomesToModelAuth,
|
||||
projectPublicProviderCatalogOutcomes,
|
||||
} from "./models-list-provider-outcomes.js";
|
||||
import type { GatewayRequestContext } from "./types.js";
|
||||
|
||||
type ModelsListEntry = Pick<
|
||||
@@ -76,14 +80,6 @@ type ModelsListResult = {
|
||||
providerOutcomes?: readonly ProviderCatalogOutcome[];
|
||||
};
|
||||
|
||||
function projectProviderCatalogOutcomes(outcomes: readonly ProviderCatalogOutcome[] | undefined) {
|
||||
return outcomes?.map(({ provider, profileId, status }) => ({
|
||||
provider,
|
||||
...(profileId ? { profileId } : {}),
|
||||
status,
|
||||
}));
|
||||
}
|
||||
|
||||
let loggedSlowModelsListCatalog = false;
|
||||
|
||||
// Unknown views are rejected by protocol validation first; this helper keeps the
|
||||
@@ -210,39 +206,13 @@ function createModelsListEntryEvaluator(params: {
|
||||
}),
|
||||
}
|
||||
: evaluation;
|
||||
const provider = normalizeProviderId(entry.provider);
|
||||
const outcomes =
|
||||
params.providerOutcomes?.filter(
|
||||
(outcome) => normalizeProviderId(outcome.provider) === provider,
|
||||
) ?? [];
|
||||
const modelId = (identity?.id ?? entry.id).trim().toLowerCase();
|
||||
const authorizingProfiles = outcomes.filter(
|
||||
(outcome) =>
|
||||
outcome.status === "ready" &&
|
||||
outcome.profileId !== undefined &&
|
||||
outcome.modelIds?.some((candidate) => candidate.trim().toLowerCase() === modelId),
|
||||
);
|
||||
if (
|
||||
authorizingProfiles.length > 0 &&
|
||||
!authorizingProfiles.some((outcome) => outcome.profileId === resolved.selectedProfileId)
|
||||
) {
|
||||
for (const outcome of authorizingProfiles) {
|
||||
const candidate = evaluateForProfile(outcome.profileId);
|
||||
if (candidate.availability === true) {
|
||||
return candidate;
|
||||
}
|
||||
}
|
||||
return { ...resolved, availability: false };
|
||||
}
|
||||
// Stored credentials prove presence, not acceptance. Apply the live rejection only to the
|
||||
// profile discovery tested; widening it would hide routes backed by another valid profile.
|
||||
return outcomes.some(
|
||||
(outcome) =>
|
||||
outcome.status === "auth-rejected" &&
|
||||
(outcome.profileId === undefined || outcome.profileId === resolved.selectedProfileId),
|
||||
)
|
||||
? { ...resolved, availability: false }
|
||||
: resolved;
|
||||
return applyProviderCatalogOutcomesToModelAuth({
|
||||
provider: entry.provider,
|
||||
modelId: identity?.id ?? entry.id,
|
||||
outcomes: params.providerOutcomes,
|
||||
resolved,
|
||||
evaluateForProfile,
|
||||
});
|
||||
});
|
||||
pending.set(cacheKey, next);
|
||||
return next;
|
||||
@@ -649,7 +619,7 @@ export async function buildModelsListResult(
|
||||
const catalog = snapshot.entries;
|
||||
const routeVariants = snapshot.routeVariants;
|
||||
const providerOutcomes = snapshot.providerOutcomes;
|
||||
const publicProviderOutcomes = projectProviderCatalogOutcomes(providerOutcomes);
|
||||
const publicProviderOutcomes = projectPublicProviderCatalogOutcomes(providerOutcomes);
|
||||
const outcomeProjection = publicProviderOutcomes?.length
|
||||
? { providerOutcomes: publicProviderOutcomes }
|
||||
: {};
|
||||
|
||||
Reference in New Issue
Block a user