mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-04 22:32:12 +03:00
fix(providers): refresh GitHub Copilot catalog (#6154)
* fix(providers): refresh github copilot catalog Limit GitHub Copilot discovery to the curated supported model set and keep the provider cooldown panel client-safe by moving countdown formatting out of localDb. * chore(quality): rebaseline providerPageHelpers.ts file-size (+13, #6154 copilot catalog) The GitHub Copilot catalog refresh grows the provider-page model-section helper (1021->1034). Fast-path PR->release skips check:file-size, so the bump lands with the PR. Justification recorded in file-size-baseline.json. Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com> --------- Co-authored-by: diegosouzapw <diegosouza.pw@gmail.com>
This commit is contained in:
@@ -15,6 +15,7 @@ import { describe, it, expect, vi, beforeEach, afterEach } from "vitest";
|
||||
import {
|
||||
buildCompatMap,
|
||||
isModelHiddenFn,
|
||||
getDisplayModelAlias,
|
||||
effectiveNormalizeForProtocol,
|
||||
effectivePreserveForProtocol,
|
||||
anyNormalizeCompatBadge,
|
||||
@@ -35,10 +36,7 @@ vi.mock("next/navigation", () => ({
|
||||
vi.mock("next-intl", () => ({
|
||||
useTranslations: () => (key: string, values?: Record<string, unknown>) => {
|
||||
if (values) {
|
||||
return Object.entries(values).reduce(
|
||||
(acc, [k, v]) => acc.replace(`{${k}}`, String(v)),
|
||||
key
|
||||
);
|
||||
return Object.entries(values).reduce((acc, [k, v]) => acc.replace(`{${k}}`, String(v)), key);
|
||||
}
|
||||
return key;
|
||||
},
|
||||
@@ -85,6 +83,22 @@ describe("providerPageHelpers — model-compat pure functions", () => {
|
||||
expect(isModelHiddenFn("unknown-model", customMap, overrideMap)).toBe(false);
|
||||
});
|
||||
|
||||
it("isModelHiddenFn ignores deleted tombstones when reading visibility", () => {
|
||||
const customMap = buildCompatMap([]);
|
||||
const overrideMap = buildCompatMap([
|
||||
{ id: "gpt-4o-2024-11-20", isHidden: true, isDeleted: true },
|
||||
{ id: "gpt-5-mini", isHidden: true },
|
||||
]);
|
||||
|
||||
expect(isModelHiddenFn("gpt-4o-2024-11-20", customMap, overrideMap)).toBe(false);
|
||||
expect(isModelHiddenFn("gpt-5-mini", customMap, overrideMap)).toBe(true);
|
||||
});
|
||||
|
||||
it("getDisplayModelAlias ignores provider-scoped identity aliases", () => {
|
||||
expect(getDisplayModelAlias("gpt-4o-2024-11-20", "gpt-4o-2024-11-20")).toBeNull();
|
||||
expect(getDisplayModelAlias("gpt-5-mini", "fast-mini")).toBe("fast-mini");
|
||||
});
|
||||
|
||||
it("effectiveNormalizeForProtocol returns correct flag", () => {
|
||||
const customMap = buildCompatMap(customModels);
|
||||
const overrideMap = buildCompatMap(overrideModels);
|
||||
@@ -115,10 +129,10 @@ describe("providerPageHelpers — model-compat pure functions", () => {
|
||||
});
|
||||
|
||||
it("formatProviderModelsErrorResponse extracts error.message", async () => {
|
||||
const mockRes = new Response(
|
||||
JSON.stringify({ error: { message: "Model not found" } }),
|
||||
{ status: 422, statusText: "Unprocessable Entity" }
|
||||
);
|
||||
const mockRes = new Response(JSON.stringify({ error: { message: "Model not found" } }), {
|
||||
status: 422,
|
||||
statusText: "Unprocessable Entity",
|
||||
});
|
||||
const detail = await formatProviderModelsErrorResponse(mockRes);
|
||||
expect(detail).toBe("Model not found");
|
||||
});
|
||||
@@ -185,7 +199,9 @@ describe("PassthroughModelRow — render smoke test", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => { root.unmount(); });
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
container.remove();
|
||||
});
|
||||
|
||||
@@ -222,7 +238,9 @@ describe("ModelVisibilityToolbar — render smoke test", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => { root.unmount(); });
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
container.remove();
|
||||
});
|
||||
|
||||
@@ -259,7 +277,9 @@ describe("useModelCompatState — hook unit test via component wrapper", () => {
|
||||
});
|
||||
|
||||
afterEach(() => {
|
||||
act(() => { root.unmount(); });
|
||||
act(() => {
|
||||
root.unmount();
|
||||
});
|
||||
container.remove();
|
||||
});
|
||||
|
||||
@@ -267,7 +287,12 @@ describe("useModelCompatState — hook unit test via component wrapper", () => {
|
||||
const { useModelCompatState } = await import("../hooks/useModelCompatState");
|
||||
|
||||
const customModels = [
|
||||
{ id: "gpt-4o", normalizeToolCallId: true, preserveOpenAIDeveloperRole: false, isHidden: true },
|
||||
{
|
||||
id: "gpt-4o",
|
||||
normalizeToolCallId: true,
|
||||
preserveOpenAIDeveloperRole: false,
|
||||
isHidden: true,
|
||||
},
|
||||
];
|
||||
const modelCompatOverrides: any[] = [];
|
||||
|
||||
@@ -281,7 +306,9 @@ describe("useModelCompatState — hook unit test via component wrapper", () => {
|
||||
compat.effectiveModelPreserveDeveloper("gpt-4o"),
|
||||
compat.anyNormalizeCompatBadge("gpt-4o"),
|
||||
compat.anyNoPreserveCompatBadge("gpt-4o"),
|
||||
].map(String).join(",");
|
||||
]
|
||||
.map(String)
|
||||
.join(",");
|
||||
return <span data-testid="results">{results}</span>;
|
||||
}
|
||||
|
||||
@@ -291,8 +318,9 @@ describe("useModelCompatState — hook unit test via component wrapper", () => {
|
||||
|
||||
const span = container.querySelector("[data-testid='results']");
|
||||
expect(span).not.toBeNull();
|
||||
const [hidden, notHidden, normalize, preserve, anyNorm, anyNoPreserve] =
|
||||
(span!.textContent ?? "").split(",");
|
||||
const [hidden, notHidden, normalize, preserve, anyNorm, anyNoPreserve] = (
|
||||
span!.textContent ?? ""
|
||||
).split(",");
|
||||
|
||||
expect(hidden).toBe("true");
|
||||
expect(notHidden).toBe("false");
|
||||
|
||||
@@ -17,6 +17,7 @@ import { resolveManagedModelAlias } from "@/shared/utils/providerModelAliases";
|
||||
import { useNotificationStore } from "@/store/notificationStore";
|
||||
import {
|
||||
buildCompatMap,
|
||||
getDisplayModelAlias,
|
||||
providerText,
|
||||
type CompatModelRow,
|
||||
} from "../providerPageHelpers";
|
||||
@@ -57,10 +58,7 @@ export interface CompatibleModelsSectionProps {
|
||||
effectiveModelNormalize: (alias: string) => boolean;
|
||||
effectiveModelPreserveDeveloper: (alias: string) => boolean;
|
||||
getUpstreamHeadersRecord: (modelId: string, protocol: string) => Record<string, string>;
|
||||
saveModelCompatFlags: (
|
||||
modelId: string,
|
||||
flags: CompatibleModelsSaveFlags
|
||||
) => Promise<void>;
|
||||
saveModelCompatFlags: (modelId: string, flags: CompatibleModelsSaveFlags) => Promise<void>;
|
||||
compatSavingModelId?: string;
|
||||
onModelsChanged?: () => void;
|
||||
isModelHidden: (modelId: string) => boolean;
|
||||
@@ -155,7 +153,8 @@ export default function CompatibleModelsSection({
|
||||
for (const [alias, fullModel] of providerAliases) {
|
||||
const fmStr = fullModel as string;
|
||||
const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr;
|
||||
aliasByModelId.set(modelId, alias as string);
|
||||
const displayAlias = getDisplayModelAlias(modelId, alias as string);
|
||||
if (displayAlias) aliasByModelId.set(modelId, displayAlias);
|
||||
}
|
||||
|
||||
const addModel = (model: CompatModelRow, source: string) => {
|
||||
@@ -194,11 +193,13 @@ export default function CompatibleModelsSection({
|
||||
const fmStr = fullModel as string;
|
||||
const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr;
|
||||
if (!modelId || seenModelIds.has(modelId)) continue;
|
||||
const displayAlias = getDisplayModelAlias(modelId, alias as string);
|
||||
if (!displayAlias) continue;
|
||||
const customModel = customModelMap.get(modelId);
|
||||
rows.push({
|
||||
modelId,
|
||||
alias: alias as string,
|
||||
displayName: alias as string,
|
||||
alias: displayAlias,
|
||||
displayName: displayAlias,
|
||||
source: customModel ? customModel.source || "custom" : "alias",
|
||||
isFree:
|
||||
modelId.endsWith(":free") ||
|
||||
|
||||
@@ -21,6 +21,7 @@ import {
|
||||
import { useNotificationStore } from "@/store/notificationStore";
|
||||
import {
|
||||
buildCompatMap,
|
||||
getDisplayModelAlias,
|
||||
providerText,
|
||||
testAllResultsText,
|
||||
evaluateTestAllEntry,
|
||||
@@ -228,7 +229,8 @@ export default function PassthroughModelsSection({
|
||||
for (const [alias, fullModel] of providerAliases) {
|
||||
const fmStr = fullModel as string;
|
||||
const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr;
|
||||
aliasByModelId.set(modelId, alias as string);
|
||||
const displayAlias = getDisplayModelAlias(modelId, alias as string);
|
||||
if (displayAlias) aliasByModelId.set(modelId, displayAlias);
|
||||
fullModelByModelId.set(modelId, fmStr);
|
||||
}
|
||||
|
||||
@@ -266,12 +268,14 @@ export default function PassthroughModelsSection({
|
||||
const fmStr = fullModel as string;
|
||||
const modelId = fmStr.startsWith(prefix) ? fmStr.slice(prefix.length) : fmStr;
|
||||
if (!modelId || seenModelIds.has(modelId)) continue;
|
||||
const displayAlias = getDisplayModelAlias(modelId, alias as string);
|
||||
if (!displayAlias) continue;
|
||||
const customModel = customModelMap.get(modelId);
|
||||
rows.push({
|
||||
modelId,
|
||||
fullModel: fmStr,
|
||||
alias: alias as string,
|
||||
displayName: alias as string,
|
||||
alias: displayAlias,
|
||||
displayName: displayAlias,
|
||||
source: customModel ? customModel.source || "custom" : "alias",
|
||||
isFree:
|
||||
modelId.endsWith(":free") ||
|
||||
|
||||
@@ -15,7 +15,11 @@ import { useState } from "react";
|
||||
import { Button } from "@/shared/components";
|
||||
import { matchesModelCatalogQuery } from "@/shared/utils/modelCatalogSearch";
|
||||
import { isFreeModel, sortModelsFreeFirst } from "@/shared/utils/freeModels";
|
||||
import { providerText, type ProviderMessageTranslator } from "../providerPageHelpers";
|
||||
import {
|
||||
getDisplayModelAlias,
|
||||
providerText,
|
||||
type ProviderMessageTranslator,
|
||||
} from "../providerPageHelpers";
|
||||
import ModelRow, { ModelVisibilityToolbar } from "./ModelRow";
|
||||
import PassthroughModelsSection from "./PassthroughModelsSection";
|
||||
import CompatibleModelsSection from "./CompatibleModelsSection";
|
||||
@@ -86,11 +90,7 @@ export interface ProviderModelsSectionProps {
|
||||
setAutoHideFailed: (v: boolean) => void;
|
||||
setVisibilityFilter: (v: "all" | "visible" | "hidden") => void;
|
||||
saveModelCompatFlags: (modelId: string, patch: ModelCompatSavePatch) => Promise<void>;
|
||||
handleToggleModelHidden: (
|
||||
providerKey: string,
|
||||
modelId: string,
|
||||
hidden: boolean
|
||||
) => Promise<void>;
|
||||
handleToggleModelHidden: (providerKey: string, modelId: string, hidden: boolean) => Promise<void>;
|
||||
handleBulkToggleModelHidden: (
|
||||
providerKey: string,
|
||||
modelIds: string[],
|
||||
@@ -187,8 +187,7 @@ export default function ProviderModelsSection({
|
||||
</button>
|
||||
);
|
||||
|
||||
const clearAllButton = (modelMeta.customModels.length > 0 ||
|
||||
providerAliasEntries.length > 0) && (
|
||||
const clearAllButton = (modelMeta.customModels.length > 0 || providerAliasEntries.length > 0) && (
|
||||
<button
|
||||
onClick={handleClearAllModels}
|
||||
disabled={clearingModels}
|
||||
@@ -251,9 +250,7 @@ export default function ProviderModelsSection({
|
||||
onModelsChanged={fetchProviderModelMeta}
|
||||
allowImport={compatibleSupportsModelImport}
|
||||
isModelHidden={effectiveModelHidden}
|
||||
onToggleHidden={(modelId, hidden) =>
|
||||
handleToggleModelHidden(providerId, modelId, hidden)
|
||||
}
|
||||
onToggleHidden={(modelId, hidden) => handleToggleModelHidden(providerId, modelId, hidden)}
|
||||
onBulkToggleHidden={(modelIds, hidden) =>
|
||||
handleBulkToggleModelHidden(providerId, modelIds, hidden)
|
||||
}
|
||||
@@ -325,9 +322,7 @@ export default function ProviderModelsSection({
|
||||
saveModelCompatFlags={saveModelCompatFlags}
|
||||
compatSavingModelId={compatSavingModelId}
|
||||
isModelHidden={effectiveModelHidden}
|
||||
onToggleHidden={(modelId, hidden) =>
|
||||
handleToggleModelHidden(providerId, modelId, hidden)
|
||||
}
|
||||
onToggleHidden={(modelId, hidden) => handleToggleModelHidden(providerId, modelId, hidden)}
|
||||
onBulkToggleHidden={(modelIds, hidden) =>
|
||||
handleBulkToggleModelHidden(providerId, modelIds, hidden)
|
||||
}
|
||||
@@ -377,7 +372,9 @@ export default function ProviderModelsSection({
|
||||
(acc, [alias, fullModel]) => {
|
||||
const prefix = `${providerDisplayAlias}/`;
|
||||
if (fullModel.startsWith(prefix)) {
|
||||
acc[fullModel.slice(prefix.length)] = alias;
|
||||
const modelId = fullModel.slice(prefix.length);
|
||||
const displayAlias = getDisplayModelAlias(modelId, alias);
|
||||
if (displayAlias) acc[modelId] = displayAlias;
|
||||
}
|
||||
return acc;
|
||||
},
|
||||
@@ -467,9 +464,7 @@ export default function ProviderModelsSection({
|
||||
onCopy={onCopy}
|
||||
onSetAlias={(a) => onSetAlias(model.id, a, providerDisplayAlias)}
|
||||
onDeleteAlias={
|
||||
aliasByModelId[model.id]
|
||||
? () => onDeleteAlias(aliasByModelId[model.id])
|
||||
: undefined
|
||||
aliasByModelId[model.id] ? () => onDeleteAlias(aliasByModelId[model.id]) : undefined
|
||||
}
|
||||
t={t}
|
||||
showDeveloperToggle
|
||||
|
||||
@@ -70,6 +70,7 @@ export type CompatModelRow = {
|
||||
normalizeToolCallId?: boolean;
|
||||
preserveOpenAIDeveloperRole?: boolean;
|
||||
isHidden?: boolean;
|
||||
isDeleted?: boolean;
|
||||
upstreamHeaders?: Record<string, string>;
|
||||
compatByProtocol?: CompatByProtocolMap;
|
||||
/** #2905: per-model upstream wire-format override. */ targetFormat?: string;
|
||||
@@ -557,19 +558,31 @@ export function buildCompatMap(rows: CompatModelRow[]): CompatModelMap {
|
||||
return m;
|
||||
}
|
||||
|
||||
export function getDisplayModelAlias(modelId: string, alias?: string | null): string | null {
|
||||
const trimmed = typeof alias === "string" ? alias.trim() : "";
|
||||
if (!trimmed || trimmed === modelId) return null;
|
||||
return trimmed;
|
||||
}
|
||||
|
||||
function readActiveHiddenFlag(row: CompatModelRow | undefined): boolean | undefined {
|
||||
if (!row || row.isDeleted === true) return undefined;
|
||||
if (Object.prototype.hasOwnProperty.call(row, "isHidden")) {
|
||||
return Boolean(row.isHidden);
|
||||
}
|
||||
return undefined;
|
||||
}
|
||||
|
||||
export function isModelHiddenFn(
|
||||
modelId: string,
|
||||
customMap: CompatModelMap,
|
||||
overrideMap: CompatModelMap
|
||||
): boolean {
|
||||
const c = customMap.get(modelId);
|
||||
if (c && Object.prototype.hasOwnProperty.call(c, "isHidden")) {
|
||||
return Boolean(c.isHidden);
|
||||
}
|
||||
const o = overrideMap.get(modelId);
|
||||
if (o && Object.prototype.hasOwnProperty.call(o, "isHidden")) {
|
||||
return Boolean(o.isHidden);
|
||||
}
|
||||
const customHidden = readActiveHiddenFlag(customMap.get(modelId));
|
||||
if (customHidden !== undefined) return customHidden;
|
||||
|
||||
const overrideHidden = readActiveHiddenFlag(overrideMap.get(modelId));
|
||||
if (overrideHidden !== undefined) return overrideHidden;
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
@@ -292,7 +292,7 @@ export const MODEL_SPECS: Record<string, ModelSpec> = {
|
||||
supportsTools: true,
|
||||
supportsVision: true,
|
||||
adaptiveThinkingOnly: true,
|
||||
aliases: BEDROCK_CLAUDE_ALIASES("claude-opus-4-8", "claude-opus-4.8"),
|
||||
aliases: BEDROCK_CLAUDE_ALIASES("claude-opus-4-8", "claude-opus-4.8", "claude-opus-4.8-fast"),
|
||||
},
|
||||
|
||||
// ── Claude Sonnet 4.5 ───────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user