mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-07-31 12:22:14 +03:00
Register AgentRouter across the provider registry, pricing, docs, and dashboard metadata so it appears as a first-class OpenAI-compatible passthrough option. Add a dedicated `/api/models/test` endpoint and provider-page controls for on-demand single-model diagnostics, including latency feedback and success/error status, to help verify mappings without triggering broader connection tests or rate limits. Align header casing expectations in provider validation tests with the current registry contract.
171 lines
9.5 KiB
JavaScript
171 lines
9.5 KiB
JavaScript
const fs = require('fs');
|
|
|
|
const path = 'src/app/(dashboard)/dashboard/providers/[id]/page.tsx';
|
|
let content = fs.readFileSync(path, 'utf8');
|
|
|
|
// 1. Add ModelRowProps fields
|
|
content = content.replace(
|
|
' togglingHidden?: boolean;\n}',
|
|
' togglingHidden?: boolean;\n onTestModel?: (modelId: string, fullModel: string) => Promise<void>;\n testStatus?: "ok" | "error" | null;\n testingModel?: boolean;\n}'
|
|
);
|
|
|
|
// 2. Add PassthroughModelRowProps fields
|
|
content = content.replace(
|
|
' togglingHidden?: boolean;\n}',
|
|
' togglingHidden?: boolean;\n onTestModel?: (modelId: string, fullModel: string) => Promise<void>;\n testStatus?: "ok" | "error" | null;\n testingModel?: boolean;\n}'
|
|
);
|
|
|
|
// 3. Add PassthroughModelsSectionProps fields
|
|
content = content.replace(
|
|
' togglingModelId?: string | null;\n}',
|
|
' togglingModelId?: string | null;\n onTestModel?: (modelId: string, fullModel: string) => Promise<void>;\n modelTestStatus?: Record<string, "ok" | "error" | null>;\n testingModelId?: string | null;\n}'
|
|
);
|
|
|
|
// 4. Add CompatibleModelsSectionProps fields
|
|
content = content.replace(
|
|
' togglingModelId?: string | null;\n}',
|
|
' togglingModelId?: string | null;\n onTestModel?: (modelId: string, fullModel: string) => Promise<void>;\n modelTestStatus?: Record<string, "ok" | "error" | null>;\n testingModelId?: string | null;\n}'
|
|
);
|
|
|
|
// 5. Update ModelRow
|
|
content = content.replace(
|
|
' compatDisabled,\n onToggleHidden,\n togglingHidden,\n}: ModelRowProps) {',
|
|
' compatDisabled,\n onToggleHidden,\n togglingHidden,\n onTestModel,\n testStatus,\n testingModel,\n}: ModelRowProps) {'
|
|
);
|
|
content = content.replace(
|
|
' <div className="flex shrink-0 items-center gap-1">',
|
|
` <div className="flex shrink-0 items-center gap-1">
|
|
{onTestModel && (
|
|
<button
|
|
onClick={() => onTestModel(model.id, fullModel)}
|
|
disabled={testingModel}
|
|
className={\`rounded p-0.5 hover:bg-sidebar transition-colors disabled:opacity-40 disabled:cursor-not-allowed \${testStatus === "ok" ? "text-green-500" : testStatus === "error" ? "text-red-500" : "text-text-muted hover:text-primary"}\`}
|
|
title={testingModel ? t("testingModel", "Testing...") : testStatus === "ok" ? "OK" : testStatus === "error" ? "Error" : t("testModel", "Test Model")}
|
|
>
|
|
{testingModel ? (
|
|
<span className="material-symbols-outlined text-sm animate-spin">progress_activity</span>
|
|
) : testStatus === "ok" ? (
|
|
<span className="material-symbols-outlined text-sm">check_circle</span>
|
|
) : testStatus === "error" ? (
|
|
<span className="material-symbols-outlined text-sm">error</span>
|
|
) : (
|
|
<span className="material-symbols-outlined text-sm">play_circle</span>
|
|
)}
|
|
</button>
|
|
)}`
|
|
);
|
|
|
|
// 6. Update PassthroughModelRow
|
|
content = content.replace(
|
|
' compatDisabled,\n onToggleHidden,\n togglingHidden,\n}: PassthroughModelRowProps) {',
|
|
' compatDisabled,\n onToggleHidden,\n togglingHidden,\n onTestModel,\n testStatus,\n testingModel,\n}: PassthroughModelRowProps) {'
|
|
);
|
|
content = content.replace(
|
|
' <div className="flex shrink-0 items-center gap-1 self-start">',
|
|
` <div className="flex shrink-0 items-center gap-1 self-start">
|
|
{onTestModel && (
|
|
<button
|
|
onClick={() => onTestModel(modelId, fullModel)}
|
|
disabled={testingModel}
|
|
className={\`rounded p-0.5 hover:bg-sidebar transition-colors disabled:opacity-40 disabled:cursor-not-allowed \${testStatus === "ok" ? "text-green-500" : testStatus === "error" ? "text-red-500" : "text-text-muted hover:text-primary"}\`}
|
|
title={testingModel ? t("testingModel", "Testing...") : testStatus === "ok" ? "OK" : testStatus === "error" ? "Error" : t("testModel", "Test Model")}
|
|
>
|
|
{testingModel ? (
|
|
<span className="material-symbols-outlined text-sm animate-spin">progress_activity</span>
|
|
) : testStatus === "ok" ? (
|
|
<span className="material-symbols-outlined text-sm">check_circle</span>
|
|
) : testStatus === "error" ? (
|
|
<span className="material-symbols-outlined text-sm">error</span>
|
|
) : (
|
|
<span className="material-symbols-outlined text-sm">play_circle</span>
|
|
)}
|
|
</button>
|
|
)}`
|
|
);
|
|
|
|
// 7. Update PassthroughModelsSection component passing props down
|
|
content = content.replace(
|
|
' togglingModelId,\n}: PassthroughModelsSectionProps)',
|
|
' togglingModelId,\n onTestModel,\n modelTestStatus,\n testingModelId,\n}: PassthroughModelsSectionProps)'
|
|
);
|
|
content = content.replace(
|
|
' saveModelCompatFlags={saveModelCompatFlags}\n compatDisabled={compatSavingModelId === cm.id}',
|
|
' saveModelCompatFlags={saveModelCompatFlags}\n compatDisabled={compatSavingModelId === cm.id}\n onTestModel={onTestModel}\n testStatus={modelTestStatus?.[cm.id] || null}\n testingModel={testingModelId === cm.id}'
|
|
);
|
|
content = content.replace(
|
|
' compatDisabled={compatSavingModelId === alias}\n onToggleHidden={onToggleHidden}',
|
|
' compatDisabled={compatSavingModelId === alias}\n onToggleHidden={onToggleHidden}\n onTestModel={onTestModel}\n testStatus={modelTestStatus?.[alias] || null}\n testingModel={testingModelId === alias}'
|
|
);
|
|
|
|
// 8. Update CompatibleModelsSection component passing props down
|
|
content = content.replace(
|
|
' togglingModelId,\n}: CompatibleModelsSectionProps) {',
|
|
' togglingModelId,\n onTestModel,\n modelTestStatus,\n testingModelId,\n}: CompatibleModelsSectionProps) {'
|
|
);
|
|
content = content.replace(
|
|
' saveModelCompatFlags={saveModelCompatFlags}\n compatDisabled={compatSavingModelId === cm.id}',
|
|
' saveModelCompatFlags={saveModelCompatFlags}\n compatDisabled={compatSavingModelId === cm.id}\n onTestModel={onTestModel}\n testStatus={modelTestStatus?.[cm.id] || null}\n testingModel={testingModelId === cm.id}'
|
|
);
|
|
content = content.replace(
|
|
' compatDisabled={compatSavingModelId === model.id}\n onToggleHidden={(modelId, hidden)',
|
|
' compatDisabled={compatSavingModelId === model.id}\n onTestModel={onTestModel}\n testStatus={modelTestStatus?.[model.id] || null}\n testingModel={testingModelId === model.id}\n onToggleHidden={(modelId, hidden)'
|
|
);
|
|
|
|
// 9. Update ProviderDetailPage
|
|
content = content.replace(
|
|
' const [togglingModelId, setTogglingModelId] = useState<string | null>(null);',
|
|
' const [togglingModelId, setTogglingModelId] = useState<string | null>(null);\n const [testingModelId, setTestingModelId] = useState<string | null>(null);\n const [modelTestStatus, setModelTestStatus] = useState<Record<string, "ok" | "error">>({});'
|
|
);
|
|
|
|
// Add the onTestModel function
|
|
content = content.replace(
|
|
' const handleSetAlias = async (modelId, alias, providerAliasOverride = providerAlias) => {',
|
|
` const onTestModel = async (modelId: string, fullModel: string) => {
|
|
setTestingModelId(modelId);
|
|
setModelTestStatus(prev => ({ ...prev, [modelId]: undefined }));
|
|
try {
|
|
const res = await fetch("/api/models/test", {
|
|
method: "POST",
|
|
headers: { "Content-Type": "application/json" },
|
|
body: JSON.stringify({ providerId: selectedConnection?.provider || providerNode?.id || providerId, modelId: fullModel })
|
|
});
|
|
const data = await res.json();
|
|
if (res.ok && data.status === "ok") {
|
|
notify.success(providerText(t, "testModelSuccess", \`Model \${modelId} is working. Latency: \${data.latencyMs}ms\`, { modelId, latencyMs: data.latencyMs }));
|
|
setModelTestStatus(prev => ({ ...prev, [modelId]: "ok" }));
|
|
} else {
|
|
notify.error(data.error || "Model test failed");
|
|
setModelTestStatus(prev => ({ ...prev, [modelId]: "error" }));
|
|
}
|
|
} catch (err) {
|
|
notify.error("Network error testing model");
|
|
setModelTestStatus(prev => ({ ...prev, [modelId]: "error" }));
|
|
} finally {
|
|
setTestingModelId(null);
|
|
}
|
|
};
|
|
|
|
const handleSetAlias = async (modelId, alias, providerAliasOverride = providerAlias) => {`
|
|
);
|
|
|
|
// Pass to PassthroughModelsSection in ProviderDetailPage
|
|
content = content.replace(
|
|
' togglingModelId={togglingModelId}\n />',
|
|
' togglingModelId={togglingModelId}\n onTestModel={onTestModel}\n modelTestStatus={modelTestStatus}\n testingModelId={testingModelId}\n />'
|
|
);
|
|
|
|
// Pass to CompatibleModelsSection in ProviderDetailPage
|
|
content = content.replace(
|
|
' togglingModelId={togglingModelId}\n />',
|
|
' togglingModelId={togglingModelId}\n onTestModel={onTestModel}\n modelTestStatus={modelTestStatus}\n testingModelId={testingModelId}\n />'
|
|
);
|
|
|
|
// Pass to ModelRow in ProviderDetailPage (fallback/normal models list)
|
|
content = content.replace(
|
|
' togglingHidden={togglingModelId === model.id}\n />',
|
|
' togglingHidden={togglingModelId === model.id}\n onTestModel={onTestModel}\n testStatus={modelTestStatus[model.id] || null}\n testingModel={testingModelId === model.id}\n />'
|
|
);
|
|
|
|
fs.writeFileSync(path, content);
|
|
console.log("Successfully updated page.tsx");
|