"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.

setNewModel(e.target.value)} onKeyDown={(e) => e.key === "Enter" && handleAdd()} placeholder={isAnthropic ? "claude-3-opus-20240229" : "gpt-4o"} className="w-full px-3 py-2 text-sm border border-border rounded-lg bg-background focus:outline-none focus:border-primary" />
{!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, };