




























@@ -1,10 +1,17 @@
1+import { normalizeLowercaseStringOrEmpty } from "../shared/string-coerce.js";
12import { normalizeModelCatalogProviderRows } from "./normalize.js";
2-import { normalizeModelCatalogProviderId } from "./refs.js";
3-import type { ModelCatalog, ModelCatalogDiscovery, NormalizedModelCatalogRow } from "./types.js";
3+import { buildModelCatalogMergeKey, normalizeModelCatalogProviderId } from "./refs.js";
4+import type {
5+ModelCatalog,
6+ModelCatalogAlias,
7+ModelCatalogDiscovery,
8+NormalizedModelCatalogRow,
9+} from "./types.js";
410511export type ManifestModelCatalogPlugin = {
612id: string;
7-modelCatalog?: Pick<ModelCatalog, "providers" | "discovery">;
13+providers?: readonly string[];
14+modelCatalog?: Pick<ModelCatalog, "providers" | "aliases" | "suppressions" | "discovery">;
815};
9161017export type ManifestModelCatalogRegistry = {
@@ -33,6 +40,18 @@ export type ManifestModelCatalogPlan = {
3340conflicts: readonly ManifestModelCatalogConflict[];
3441};
354243+export type ManifestModelCatalogSuppressionEntry = {
44+pluginId: string;
45+provider: string;
46+model: string;
47+mergeKey: string;
48+reason?: string;
49+};
50+51+export type ManifestModelCatalogSuppressionPlan = {
52+suppressions: readonly ManifestModelCatalogSuppressionEntry[];
53+};
54+3655export function planManifestModelCatalogRows(params: {
3756registry: ManifestModelCatalogRegistry;
3857providerFilter?: string;
@@ -94,29 +113,147 @@ function planManifestModelCatalogPluginEntries(params: {
94113return [];
95114}
96115116+const aliasesByTargetProvider = buildModelCatalogProviderAliasTargets(params.plugin);
117+97118return Object.entries(providers).flatMap(([provider, providerCatalog]) => {
98119const normalizedProvider = normalizeModelCatalogProviderId(provider);
99-if (
100-!normalizedProvider ||
101-(params.providerFilter && normalizedProvider !== params.providerFilter)
102-) {
120+if (!normalizedProvider) {
103121return [];
104122}
105-const rows = normalizeModelCatalogProviderRows({
106-provider: normalizedProvider,
107- providerCatalog,
108-source: "manifest",
109-});
110-if (rows.length === 0) {
123+const providerAliases = aliasesByTargetProvider.get(normalizedProvider) ?? [];
124+const plannedProviders = params.providerFilter
125+ ? providerAliases.includes(params.providerFilter) ||
126+normalizedProvider === params.providerFilter
127+ ? [params.providerFilter]
128+ : []
129+ : [normalizedProvider];
130+if (plannedProviders.length === 0) {
111131return [];
112132}
113-return [
114-{
115-pluginId: params.plugin.id,
116-provider: normalizedProvider,
117-discovery: params.plugin.modelCatalog?.discovery?.[normalizedProvider],
118- rows,
119-},
120-];
133+return plannedProviders.flatMap((plannedProvider) => {
134+const rows = normalizeModelCatalogProviderRows({
135+provider: plannedProvider,
136+ providerCatalog,
137+source: "manifest",
138+});
139+if (rows.length === 0) {
140+return [];
141+}
142+return [
143+{
144+pluginId: params.plugin.id,
145+provider: plannedProvider,
146+discovery: params.plugin.modelCatalog?.discovery?.[normalizedProvider],
147+rows: applyModelCatalogAliasOverrides({
148+ rows,
149+alias: params.plugin.modelCatalog?.aliases?.[plannedProvider],
150+}),
151+},
152+];
153+});
121154});
122155}
156+157+function buildOwnedProviderSet(plugin: ManifestModelCatalogPlugin): ReadonlySet<string> {
158+return new Set((plugin.providers ?? []).map(normalizeModelCatalogProviderId).filter(Boolean));
159+}
160+161+function buildModelCatalogProviderAliasTargets(
162+plugin: ManifestModelCatalogPlugin,
163+): ReadonlyMap<string, readonly string[]> {
164+const ownedProviders = buildOwnedProviderSet(plugin);
165+const aliasesByTargetProvider = new Map<string, string[]>();
166+for (const [rawAlias, alias] of Object.entries(plugin.modelCatalog?.aliases ?? {})) {
167+const aliasProvider = normalizeModelCatalogProviderId(rawAlias);
168+const targetProvider = normalizeModelCatalogProviderId(alias.provider);
169+if (!aliasProvider || !targetProvider || !ownedProviders.has(targetProvider)) {
170+continue;
171+}
172+const aliases = aliasesByTargetProvider.get(targetProvider) ?? [];
173+aliases.push(aliasProvider);
174+aliasesByTargetProvider.set(targetProvider, aliases);
175+}
176+return aliasesByTargetProvider;
177+}
178+179+function applyModelCatalogAliasOverrides(params: {
180+rows: readonly NormalizedModelCatalogRow[];
181+alias?: ModelCatalogAlias;
182+}): readonly NormalizedModelCatalogRow[] {
183+if (!params.alias) {
184+return params.rows;
185+}
186+return params.rows.map((row) => ({
187+ ...row,
188+ ...(params.alias.api ? { api: params.alias.api } : {}),
189+ ...(params.alias.baseUrl ? { baseUrl: params.alias.baseUrl } : {}),
190+}));
191+}
192+193+function pluginOwnsModelCatalogProviderRef(params: {
194+plugin: ManifestModelCatalogPlugin;
195+provider: string;
196+}): boolean {
197+const provider = normalizeModelCatalogProviderId(params.provider);
198+if (!provider) {
199+return false;
200+}
201+const ownedProviders = buildOwnedProviderSet(params.plugin);
202+if (ownedProviders.has(provider)) {
203+return true;
204+}
205+return Object.entries(params.plugin.modelCatalog?.aliases ?? {}).some(([rawAlias, alias]) => {
206+const aliasProvider = normalizeModelCatalogProviderId(rawAlias);
207+const targetProvider = normalizeModelCatalogProviderId(alias.provider);
208+return (
209+aliasProvider === provider && Boolean(targetProvider) && ownedProviders.has(targetProvider)
210+);
211+});
212+}
213+214+export function planManifestModelCatalogSuppressions(params: {
215+registry: ManifestModelCatalogRegistry;
216+providerFilter?: string;
217+modelFilter?: string;
218+}): ManifestModelCatalogSuppressionPlan {
219+const providerFilter = params.providerFilter
220+ ? normalizeModelCatalogProviderId(params.providerFilter)
221+ : undefined;
222+const modelFilter = params.modelFilter
223+ ? normalizeLowercaseStringOrEmpty(params.modelFilter)
224+ : undefined;
225+const suppressions: ManifestModelCatalogSuppressionEntry[] = [];
226+for (const plugin of params.registry.plugins) {
227+for (const suppression of plugin.modelCatalog?.suppressions ?? []) {
228+const provider = normalizeModelCatalogProviderId(suppression.provider);
229+const model = normalizeLowercaseStringOrEmpty(suppression.model);
230+if (!provider || !model) {
231+continue;
232+}
233+if (providerFilter && provider !== providerFilter) {
234+continue;
235+}
236+if (modelFilter && model !== modelFilter) {
237+continue;
238+}
239+if (!pluginOwnsModelCatalogProviderRef({ plugin, provider })) {
240+continue;
241+}
242+suppressions.push({
243+pluginId: plugin.id,
244+ provider,
245+ model,
246+mergeKey: buildModelCatalogMergeKey(provider, model),
247+ ...(suppression.reason ? { reason: suppression.reason } : {}),
248+});
249+}
250+}
251+return {
252+suppressions: suppressions.toSorted(
253+(left, right) =>
254+left.provider.localeCompare(right.provider) ||
255+left.model.localeCompare(right.model) ||
256+left.pluginId.localeCompare(right.pluginId),
257+),
258+};
259+}
此内容由惯性聚合(RSS阅读器)自动聚合整理,仅供阅读参考。 原文来自 — 版权归原作者所有。