"use client";
import { useState } from "react";
import PropTypes from "prop-types";
import { Button } from "@/shared/components";
function CompatibleModelRow({ modelId, fullModel, copied, onCopy, onDeleteAlias, onTest, testStatus, isTesting }) {
const borderColor = testStatus === "ok"
? "border-green-500/40"
: testStatus === "error"
? "border-red-500/40"
: "border-border";
const iconColor = testStatus === "ok"
? "#22c55e"
: testStatus === "error"
? "#ef4444"
: undefined;
return (
{testStatus === "ok" ? "check_circle" : testStatus === "error" ? "cancel" : "smart_toy"}
{modelId}
{fullModel}
{copied === `model-${modelId}` ? "Copied!" : "Copy"}
{onTest && (
{isTesting ? "Testing..." : "Test"}
)}
);
}
export default function CompatibleModelsSection({ providerStorageAlias, providerDisplayAlias, modelAliases, copied, onCopy, onSetAlias, onDeleteAlias, connections, isAnthropic }) {
const [newModel, setNewModel] = useState("");
const [adding, setAdding] = useState(false);
const [importing, setImporting] = useState(false);
const [testingModelId, setTestingModelId] = useState(null);
const [modelTestResults, setModelTestResults] = useState({});
const handleTestModel = async (modelId) => {
if (testingModelId) return;
setTestingModelId(modelId);
try {
const res = await fetch("/api/models/test", {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ model: `${providerStorageAlias}/${modelId}` }),
});
const data = await res.json();
setModelTestResults((prev) => ({ ...prev, [modelId]: data.ok ? "ok" : "error" }));
} catch {
setModelTestResults((prev) => ({ ...prev, [modelId]: "error" }));
} finally {
setTestingModelId(null);
}
};
const providerAliases = Object.entries(modelAliases).filter(
([, model]) => model.startsWith(`${providerStorageAlias}/`)
);
const allModels = providerAliases.map(([alias, fullModel]) => ({
modelId: fullModel.replace(`${providerStorageAlias}/`, ""),
fullModel,
alias,
}));
const generateDefaultAlias = (modelId) => {
const parts = modelId.split("/");
return parts[parts.length - 1];
};
const resolveAlias = (modelId) => {
const fullModel = `${providerStorageAlias}/${modelId}`;
// Skip if this exact model already has an alias
if (Object.values(modelAliases).includes(fullModel)) return null;
const baseAlias = generateDefaultAlias(modelId);
if (!modelAliases[baseAlias]) return baseAlias;
const prefixedAlias = `${providerDisplayAlias}-${baseAlias}`;
if (!modelAliases[prefixedAlias]) return prefixedAlias;
return null;
};
const handleAdd = async () => {
if (!newModel.trim() || adding) return;
const modelId = newModel.trim();
const resolvedAlias = resolveAlias(modelId);
if (!resolvedAlias) {
alert("All suggested aliases already exist. Please choose a different model or remove conflicting aliases.");
return;
}
setAdding(true);
try {
await onSetAlias(modelId, resolvedAlias, providerStorageAlias);
setNewModel("");
} catch (error) {
console.log("Error adding model:", error);
} finally {
setAdding(false);
}
};
const handleImport = async () => {
if (importing) return;
const activeConnection = connections.find((conn) => conn.isActive !== false);
if (!activeConnection) return;
setImporting(true);
try {
const res = await fetch(`/api/providers/${activeConnection.id}/models`);
const data = await res.json();
if (!res.ok) {
alert(data.error || "Failed to import models");
return;
}
const models = data.models || [];
if (models.length === 0) {
alert("No models returned from /models.");
return;
}
let importedCount = 0;
for (const model of models) {
const modelId = model.id || model.name || model.model;
if (!modelId) continue;
const resolvedAlias = resolveAlias(modelId);
if (!resolvedAlias) continue;
await onSetAlias(modelId, resolvedAlias, providerStorageAlias);
importedCount += 1;
}
if (importedCount === 0) {
alert("No new models were added.");
}
} catch (error) {
console.log("Error importing models:", error);
} finally {
setImporting(false);
}
};
const canImport = connections.some((conn) => conn.isActive !== false);
return (
Add {isAnthropic ? "Anthropic" : "OpenAI"}-compatible models manually or import them from the /models endpoint.
{!canImport && (
Add a connection to enable importing models.
)}
{allModels.length > 0 && (
{allModels.map(({ modelId, fullModel, alias }) => (
onDeleteAlias(alias)}
onTest={connections.length > 0 ? () => handleTestModel(modelId) : undefined}
testStatus={modelTestResults[modelId]}
isTesting={testingModelId === modelId}
/>
))}
)}
);
}
CompatibleModelsSection.propTypes = {
providerStorageAlias: PropTypes.string.isRequired,
providerDisplayAlias: PropTypes.string.isRequired,
modelAliases: PropTypes.object.isRequired,
copied: PropTypes.string,
onCopy: PropTypes.func.isRequired,
onSetAlias: PropTypes.func.isRequired,
onDeleteAlias: PropTypes.func.isRequired,
connections: PropTypes.arrayOf(PropTypes.shape({
id: PropTypes.string,
isActive: PropTypes.bool,
})).isRequired,
isAnthropic: PropTypes.bool,
};