mirror of
https://github.com/diegosouzapw/OmniRoute.git
synced 2026-08-05 23:02:10 +03:00
feat: filter api/models by active providers + disabled indicator on provider cards (Closes #81) (#85)
Co-authored-by: diegosouzapw <diegosouzapw@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
parent
c34973f40d
commit
937a8f3714
@@ -134,14 +134,18 @@ export default function ProvidersPage() {
|
||||
const error = errorConns.length;
|
||||
const total = providerConnections.length;
|
||||
|
||||
// Check if all connections are manually disabled
|
||||
const allDisabled = total > 0 && providerConnections.every((c) => c.isActive === false);
|
||||
|
||||
// Get latest error info
|
||||
const latestError = errorConns.sort(
|
||||
(a: any, b: any) => (new Date(b.lastErrorAt || 0) as any) - (new Date(a.lastErrorAt || 0) as any)
|
||||
(a: any, b: any) =>
|
||||
(new Date(b.lastErrorAt || 0) as any) - (new Date(a.lastErrorAt || 0) as any)
|
||||
)[0];
|
||||
const errorCode = latestError ? getConnectionErrorTag(latestError) : null;
|
||||
const errorTime = latestError?.lastErrorAt ? getRelativeTime(latestError.lastErrorAt) : null;
|
||||
|
||||
return { connected, error, total, errorCode, errorTime };
|
||||
return { connected, error, total, errorCode, errorTime, allDisabled };
|
||||
};
|
||||
|
||||
const handleBatchTest = async (mode, providerId = null) => {
|
||||
@@ -422,7 +426,7 @@ export default function ProvidersPage() {
|
||||
}
|
||||
|
||||
function ProviderCard({ providerId, provider, stats, authType }) {
|
||||
const { connected, error, errorCode, errorTime } = stats;
|
||||
const { connected, error, errorCode, errorTime, allDisabled } = stats;
|
||||
const [imgError, setImgError] = useState(false);
|
||||
|
||||
const dotColors = {
|
||||
@@ -437,7 +441,7 @@ function ProviderCard({ providerId, provider, stats, authType }) {
|
||||
<Link href={`/dashboard/providers/${providerId}`} className="group">
|
||||
<Card
|
||||
padding="xs"
|
||||
className="h-full hover:bg-black/[0.01] dark:hover:bg-white/[0.01] transition-colors cursor-pointer"
|
||||
className={`h-full hover:bg-black/[0.01] dark:hover:bg-white/[0.01] transition-colors cursor-pointer ${allDisabled ? "opacity-50" : ""}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -470,8 +474,19 @@ function ProviderCard({ providerId, provider, stats, authType }) {
|
||||
/>
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 text-xs flex-wrap">
|
||||
{getStatusDisplay(connected, error, errorCode)}
|
||||
{errorTime && <span className="text-text-muted">• {errorTime}</span>}
|
||||
{allDisabled ? (
|
||||
<Badge variant="default" size="sm">
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="material-symbols-outlined text-[12px]">pause_circle</span>
|
||||
Disabled
|
||||
</span>
|
||||
</Badge>
|
||||
) : (
|
||||
<>
|
||||
{getStatusDisplay(connected, error, errorCode)}
|
||||
{errorTime && <span className="text-text-muted">• {errorTime}</span>}
|
||||
</>
|
||||
)}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
@@ -503,7 +518,7 @@ ProviderCard.propTypes = {
|
||||
|
||||
// API Key providers - use image with textIcon fallback (same as OAuth providers)
|
||||
function ApiKeyProviderCard({ providerId, provider, stats, authType }) {
|
||||
const { connected, error, errorCode, errorTime } = stats;
|
||||
const { connected, error, errorCode, errorTime, allDisabled } = stats;
|
||||
const isCompatible = providerId.startsWith(OPENAI_COMPATIBLE_PREFIX);
|
||||
const isAnthropicCompatible = providerId.startsWith(ANTHROPIC_COMPATIBLE_PREFIX);
|
||||
const [imgError, setImgError] = useState(false);
|
||||
@@ -531,7 +546,7 @@ function ApiKeyProviderCard({ providerId, provider, stats, authType }) {
|
||||
<Link href={`/dashboard/providers/${providerId}`} className="group">
|
||||
<Card
|
||||
padding="xs"
|
||||
className="h-full hover:bg-black/[0.01] dark:hover:bg-white/[0.01] transition-colors cursor-pointer"
|
||||
className={`h-full hover:bg-black/[0.01] dark:hover:bg-white/[0.01] transition-colors cursor-pointer ${allDisabled ? "opacity-50" : ""}`}
|
||||
>
|
||||
<div className="flex items-center justify-between">
|
||||
<div className="flex items-center gap-3">
|
||||
@@ -564,18 +579,29 @@ function ApiKeyProviderCard({ providerId, provider, stats, authType }) {
|
||||
/>
|
||||
</h3>
|
||||
<div className="flex items-center gap-2 text-xs flex-wrap">
|
||||
{getStatusDisplay(connected, error, errorCode)}
|
||||
{isCompatible && (
|
||||
{allDisabled ? (
|
||||
<Badge variant="default" size="sm">
|
||||
{provider.apiType === "responses" ? "Responses" : "Chat"}
|
||||
<span className="flex items-center gap-1">
|
||||
<span className="material-symbols-outlined text-[12px]">pause_circle</span>
|
||||
Disabled
|
||||
</span>
|
||||
</Badge>
|
||||
) : (
|
||||
<>
|
||||
{getStatusDisplay(connected, error, errorCode)}
|
||||
{isCompatible && (
|
||||
<Badge variant="default" size="sm">
|
||||
{provider.apiType === "responses" ? "Responses" : "Chat"}
|
||||
</Badge>
|
||||
)}
|
||||
{isAnthropicCompatible && (
|
||||
<Badge variant="default" size="sm">
|
||||
Messages
|
||||
</Badge>
|
||||
)}
|
||||
{errorTime && <span className="text-text-muted">• {errorTime}</span>}
|
||||
</>
|
||||
)}
|
||||
{isAnthropicCompatible && (
|
||||
<Badge variant="default" size="sm">
|
||||
Messages
|
||||
</Badge>
|
||||
)}
|
||||
{errorTime && <span className="text-text-muted">• {errorTime}</span>}
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -1,20 +1,37 @@
|
||||
import { NextResponse } from "next/server";
|
||||
import { getModelAliases, setModelAlias } from "@/models";
|
||||
import { getModelAliases, setModelAlias, getProviderConnections } from "@/models";
|
||||
import { AI_MODELS } from "@/shared/constants/config";
|
||||
|
||||
// GET /api/models - Get models with aliases
|
||||
export async function GET() {
|
||||
// GET /api/models - Get models with aliases (only from active providers by default)
|
||||
export async function GET(request: Request) {
|
||||
try {
|
||||
const { searchParams } = new URL(request.url);
|
||||
const showAll = searchParams.get("all") === "true";
|
||||
|
||||
const modelAliases = await getModelAliases();
|
||||
|
||||
const models = AI_MODELS.map((m) => {
|
||||
// Get active provider connections to filter available models
|
||||
let activeProviders: Set<string> | null = null;
|
||||
if (!showAll) {
|
||||
try {
|
||||
const connections = await getProviderConnections();
|
||||
const active = connections.filter((c: any) => c.isActive !== false);
|
||||
activeProviders = new Set(active.map((c: any) => c.provider));
|
||||
} catch {
|
||||
// If DB unavailable, show all models
|
||||
}
|
||||
}
|
||||
|
||||
const models = AI_MODELS.map((m: any) => {
|
||||
const fullModel = `${m.provider}/${m.model}`;
|
||||
const available = !activeProviders || activeProviders.has(m.provider);
|
||||
return {
|
||||
...m,
|
||||
fullModel,
|
||||
alias: modelAliases[fullModel] || m.model,
|
||||
available,
|
||||
};
|
||||
});
|
||||
}).filter((m: any) => showAll || m.available);
|
||||
|
||||
return NextResponse.json({ models });
|
||||
} catch (error) {
|
||||
|
||||
Reference in New Issue
Block a user