mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-23 07:32:20 +03:00
@@ -1051,7 +1051,7 @@ export default function ProviderDetailPage() {
|
||||
name: cm.name || cm.id,
|
||||
source: cm.source === "api-sync" ? "api-sync" : "custom",
|
||||
}));
|
||||
return [...builtInModels, ...syncedExtras, ...customExtras];
|
||||
return [...builtInModels, ...syncedExtras];
|
||||
}, [providerId, registryModels, syncedAvailableModels, modelMeta.customModels]);
|
||||
const providerAlias = getProviderAlias(providerId);
|
||||
const isManagedAvailableModelsProvider = isCompatible || providerId === "openrouter";
|
||||
@@ -2680,11 +2680,7 @@ export default function ProviderDetailPage() {
|
||||
</p>
|
||||
</div>
|
||||
<div className="flex items-center gap-2">
|
||||
<Button
|
||||
size="sm"
|
||||
icon="add"
|
||||
onClick={() => setShowAddApiKeyModal(true)}
|
||||
>
|
||||
<Button size="sm" icon="add" onClick={() => setShowAddApiKeyModal(true)}>
|
||||
{t("add")}
|
||||
</Button>
|
||||
<Button
|
||||
@@ -3076,15 +3072,13 @@ export default function ProviderDetailPage() {
|
||||
{renderModelsSection()}
|
||||
|
||||
{/* Custom Models — available for all providers */}
|
||||
{!isManagedAvailableModelsProvider && (
|
||||
<CustomModelsSection
|
||||
providerId={providerId}
|
||||
providerAlias={providerDisplayAlias}
|
||||
copied={copied}
|
||||
onCopy={copy}
|
||||
onModelsChanged={fetchProviderModelMeta}
|
||||
/>
|
||||
)}
|
||||
<CustomModelsSection
|
||||
providerId={providerId}
|
||||
providerAlias={providerDisplayAlias}
|
||||
copied={copied}
|
||||
onCopy={copy}
|
||||
onModelsChanged={fetchProviderModelMeta}
|
||||
/>
|
||||
</Card>
|
||||
)}
|
||||
|
||||
@@ -3875,6 +3869,7 @@ function CustomModelsSection({
|
||||
const [editingApiFormat, setEditingApiFormat] = useState("chat-completions");
|
||||
const [editingEndpoints, setEditingEndpoints] = useState<string[]>(["chat"]);
|
||||
const [savingModelId, setSavingModelId] = useState<string | null>(null);
|
||||
const [togglingModelId, setTogglingModelId] = useState<string | null>(null);
|
||||
|
||||
const customMap = useMemo(() => buildCompatMap(customModels), [customModels]);
|
||||
const overrideMap = useMemo(() => buildCompatMap(modelCompatOverrides), [modelCompatOverrides]);
|
||||
@@ -3943,6 +3938,28 @@ function CustomModelsSection({
|
||||
}
|
||||
};
|
||||
|
||||
const handleToggleHidden = async (modelId: string, hidden: boolean) => {
|
||||
setTogglingModelId(modelId);
|
||||
try {
|
||||
const res = await fetch(
|
||||
`/api/provider-models?provider=${encodeURIComponent(providerId)}&modelId=${encodeURIComponent(modelId)}`,
|
||||
{
|
||||
method: "PATCH",
|
||||
headers: { "Content-Type": "application/json" },
|
||||
body: JSON.stringify({ isHidden: hidden }),
|
||||
}
|
||||
);
|
||||
if (res.ok) {
|
||||
await fetchCustomModels();
|
||||
onModelsChanged?.();
|
||||
}
|
||||
} catch (e) {
|
||||
console.error("Failed to toggle model visibility:", e);
|
||||
} finally {
|
||||
setTogglingModelId(null);
|
||||
}
|
||||
};
|
||||
|
||||
const beginEdit = (model) => {
|
||||
setEditingModelId(model.id);
|
||||
setEditingApiFormat(model.apiFormat || "chat-completions");
|
||||
@@ -4311,6 +4328,16 @@ function CustomModelsSection({
|
||||
showDeveloperToggle
|
||||
disabled={savingModelId === model.id}
|
||||
/>
|
||||
<button
|
||||
onClick={() => handleToggleHidden(model.id, !model.isHidden)}
|
||||
disabled={togglingModelId === model.id}
|
||||
className="rounded p-1 text-text-muted hover:bg-sidebar hover:text-primary disabled:opacity-50"
|
||||
title={model.isHidden ? t("unhideModel") : t("hideModel")}
|
||||
>
|
||||
<span className="material-symbols-outlined text-sm">
|
||||
{model.isHidden ? "visibility_off" : "visibility"}
|
||||
</span>
|
||||
</button>
|
||||
<button
|
||||
onClick={() => handleRemove(model.id)}
|
||||
className="rounded p-1 text-red-500 hover:bg-red-50"
|
||||
|
||||
@@ -545,7 +545,15 @@ export default function ProxyRegistryManager() {
|
||||
title={editingId ? t("modalEditTitle") : t("modalCreateTitle")}
|
||||
maxWidth="lg"
|
||||
>
|
||||
<div className="flex flex-col gap-3">
|
||||
<form
|
||||
className="flex flex-col gap-3"
|
||||
onSubmit={(e) => {
|
||||
e.preventDefault();
|
||||
handleSave();
|
||||
}}
|
||||
autoComplete="off"
|
||||
data-1p-ignore="true"
|
||||
>
|
||||
<div className="grid grid-cols-2 gap-3">
|
||||
<div>
|
||||
<label className="text-xs text-text-muted mb-1 block">{t("labelName")}</label>
|
||||
@@ -643,7 +651,7 @@ export default function ProxyRegistryManager() {
|
||||
Save
|
||||
</Button>
|
||||
</div>
|
||||
</div>
|
||||
</form>
|
||||
</Modal>
|
||||
|
||||
<Modal
|
||||
|
||||
@@ -105,12 +105,7 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
const existingConnections = await getProviderConnections({ provider });
|
||||
if (!allowMultipleCompatibleConnections && existingConnections.length > 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "Only one connection is allowed for this OpenAI Compatible node" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
// Allow multiple connections for compatible nodes exactly like first-party providers
|
||||
|
||||
providerSpecificData = {
|
||||
...(providerSpecificData || {}),
|
||||
@@ -135,12 +130,7 @@ export async function POST(request: Request) {
|
||||
}
|
||||
|
||||
const existingConnections = await getProviderConnections({ provider });
|
||||
if (!allowMultipleCompatibleConnections && existingConnections.length > 0) {
|
||||
return NextResponse.json(
|
||||
{ error: "Only one connection is allowed for this Anthropic Compatible node" },
|
||||
{ status: 400 }
|
||||
);
|
||||
}
|
||||
// Allow multiple connections for compatible nodes exactly like first-party providers
|
||||
|
||||
providerSpecificData = {
|
||||
...(providerSpecificData || {}),
|
||||
|
||||
Reference in New Issue
Block a user