feat: implement credential selection logic for provider credentials
This commit is contained in:
parent
9f8b2906c4
commit
7d297df9aa
|
|
@ -92,6 +92,7 @@ import {
|
||||||
formatTimestamp,
|
formatTimestamp,
|
||||||
parseTimestamp,
|
parseTimestamp,
|
||||||
} from "@/lib/formatters";
|
} from "@/lib/formatters";
|
||||||
|
import { selectPreferredProviderCredential } from "@/lib/provider-credential-selection";
|
||||||
|
|
||||||
type SessionSummary = {
|
type SessionSummary = {
|
||||||
key: string;
|
key: string;
|
||||||
|
|
@ -865,11 +866,27 @@ export default function DashboardPage() {
|
||||||
const credentialsRes = await listProviderCredentialsApiV1ProviderCredentialsGet();
|
const credentialsRes = await listProviderCredentialsApiV1ProviderCredentialsGet();
|
||||||
if (credentialsRes.status !== 200) return [];
|
if (credentialsRes.status !== 200) return [];
|
||||||
|
|
||||||
const credentials = (credentialsRes.data ?? []).filter(
|
const credentials = credentialsRes.data ?? [];
|
||||||
(cred) => cred.active && cred.has_session_key,
|
const providerIds = Array.from(
|
||||||
|
new Set(
|
||||||
|
credentials
|
||||||
|
.filter((cred) => cred.active && cred.has_session_key)
|
||||||
|
.map((cred) => cred.provider),
|
||||||
|
),
|
||||||
|
);
|
||||||
|
const selectedCredentials = providerIds
|
||||||
|
.map((providerId) =>
|
||||||
|
selectPreferredProviderCredential(
|
||||||
|
credentials.filter((cred) => cred.has_session_key),
|
||||||
|
providerId,
|
||||||
|
),
|
||||||
|
)
|
||||||
|
.filter(
|
||||||
|
(cred): cred is (typeof credentials)[number] =>
|
||||||
|
Boolean(cred?.active && cred.has_session_key),
|
||||||
);
|
);
|
||||||
const settled = await Promise.allSettled(
|
const settled = await Promise.allSettled(
|
||||||
credentials.map((cred) =>
|
selectedCredentials.map((cred) =>
|
||||||
getProviderUsageLiveApiV1ProviderCredentialsCredentialIdUsageGet(
|
getProviderUsageLiveApiV1ProviderCredentialsCredentialIdUsageGet(
|
||||||
cred.id,
|
cred.id,
|
||||||
).then((res) => ({ cred, res })),
|
).then((res) => ({ cred, res })),
|
||||||
|
|
|
||||||
|
|
@ -18,6 +18,10 @@ import {
|
||||||
PopoverContent,
|
PopoverContent,
|
||||||
PopoverTrigger,
|
PopoverTrigger,
|
||||||
} from "@/components/ui/popover";
|
} from "@/components/ui/popover";
|
||||||
|
import {
|
||||||
|
credentialIsUsable,
|
||||||
|
selectPreferredProviderCredential,
|
||||||
|
} from "@/lib/provider-credential-selection";
|
||||||
|
|
||||||
type NavbarProviderId = "anthropic" | "openai";
|
type NavbarProviderId = "anthropic" | "openai";
|
||||||
|
|
||||||
|
|
@ -134,42 +138,6 @@ function providerStatusColor(status: ProviderNavbarItem["status"]): string {
|
||||||
return "bg-[color:var(--text-quiet)]";
|
return "bg-[color:var(--text-quiet)]";
|
||||||
}
|
}
|
||||||
|
|
||||||
function credentialIsUsable(cred: ProviderCredentialRead): boolean {
|
|
||||||
return cred.active && (cred.has_api_key || cred.has_session_key || Boolean(cred.base_url));
|
|
||||||
}
|
|
||||||
|
|
||||||
function navbarCredentialScore(cred: ProviderCredentialRead): number {
|
|
||||||
let score = 0;
|
|
||||||
|
|
||||||
if (cred.active) score += 100;
|
|
||||||
if (cred.has_session_key) score += 50;
|
|
||||||
if (cred.account_key === "default") score += 40;
|
|
||||||
if (cred.display_name.toLowerCase().includes("local")) score += 30;
|
|
||||||
if (cred.has_api_key) score += 10;
|
|
||||||
if (cred.base_url) score += 5;
|
|
||||||
|
|
||||||
return score;
|
|
||||||
}
|
|
||||||
|
|
||||||
function selectNavbarCredential(
|
|
||||||
credentials: ProviderCredentialRead[],
|
|
||||||
providerId: NavbarProviderId,
|
|
||||||
): ProviderCredentialRead | null {
|
|
||||||
const providerCredentials = credentials.filter((cred) => cred.provider === providerId);
|
|
||||||
const usableCredentials = providerCredentials.filter(credentialIsUsable);
|
|
||||||
|
|
||||||
return (
|
|
||||||
[...usableCredentials].sort((a, b) => {
|
|
||||||
const scoreDelta = navbarCredentialScore(b) - navbarCredentialScore(a);
|
|
||||||
if (scoreDelta !== 0) return scoreDelta;
|
|
||||||
return a.account_key.localeCompare(b.account_key);
|
|
||||||
})[0] ??
|
|
||||||
providerCredentials.find((cred) => cred.active) ??
|
|
||||||
providerCredentials[0] ??
|
|
||||||
null
|
|
||||||
);
|
|
||||||
}
|
|
||||||
|
|
||||||
function MiniRemainingBar({
|
function MiniRemainingBar({
|
||||||
pct,
|
pct,
|
||||||
className = "w-10",
|
className = "w-10",
|
||||||
|
|
@ -202,7 +170,7 @@ function buildProviderNavbarItems({
|
||||||
isLoading: boolean;
|
isLoading: boolean;
|
||||||
}): ProviderNavbarItem[] {
|
}): ProviderNavbarItem[] {
|
||||||
return NAVBAR_PROVIDER_IDS.map((providerId) => {
|
return NAVBAR_PROVIDER_IDS.map((providerId) => {
|
||||||
const credential = selectNavbarCredential(credentials, providerId);
|
const credential = selectPreferredProviderCredential(credentials, providerId);
|
||||||
const activeCredential = credential && credentialIsUsable(credential) ? credential : null;
|
const activeCredential = credential && credentialIsUsable(credential) ? credential : null;
|
||||||
const usage = activeCredential ? usageByCredentialId[activeCredential.id] : null;
|
const usage = activeCredential ? usageByCredentialId[activeCredential.id] : null;
|
||||||
const status: ProviderNavbarItem["status"] = activeCredential
|
const status: ProviderNavbarItem["status"] = activeCredential
|
||||||
|
|
@ -293,7 +261,7 @@ export function ProviderNavbarStatus() {
|
||||||
|
|
||||||
const usageCredentials = useMemo(() => {
|
const usageCredentials = useMemo(() => {
|
||||||
return NAVBAR_PROVIDER_IDS.map((providerId) =>
|
return NAVBAR_PROVIDER_IDS.map((providerId) =>
|
||||||
selectNavbarCredential(credentials, providerId),
|
selectPreferredProviderCredential(credentials, providerId),
|
||||||
).filter((cred): cred is ProviderCredentialRead => Boolean(cred && credentialIsUsable(cred)));
|
).filter((cred): cred is ProviderCredentialRead => Boolean(cred && credentialIsUsable(cred)));
|
||||||
}, [credentials]);
|
}, [credentials]);
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,37 @@
|
||||||
|
import type { ProviderCredentialRead } from "@/api/generated/model";
|
||||||
|
|
||||||
|
export function credentialIsUsable(cred: ProviderCredentialRead): boolean {
|
||||||
|
return cred.active && (cred.has_api_key || cred.has_session_key || Boolean(cred.base_url));
|
||||||
|
}
|
||||||
|
|
||||||
|
function providerCredentialScore(cred: ProviderCredentialRead): number {
|
||||||
|
let score = 0;
|
||||||
|
|
||||||
|
if (cred.active) score += 100;
|
||||||
|
if (cred.has_session_key) score += 50;
|
||||||
|
if (cred.account_key === "default") score += 40;
|
||||||
|
if (cred.display_name.toLowerCase().includes("local")) score += 30;
|
||||||
|
if (cred.has_api_key) score += 10;
|
||||||
|
if (cred.base_url) score += 5;
|
||||||
|
|
||||||
|
return score;
|
||||||
|
}
|
||||||
|
|
||||||
|
export function selectPreferredProviderCredential(
|
||||||
|
credentials: ProviderCredentialRead[],
|
||||||
|
providerId: string,
|
||||||
|
): ProviderCredentialRead | null {
|
||||||
|
const providerCredentials = credentials.filter((cred) => cred.provider === providerId);
|
||||||
|
const usableCredentials = providerCredentials.filter(credentialIsUsable);
|
||||||
|
|
||||||
|
return (
|
||||||
|
[...usableCredentials].sort((a, b) => {
|
||||||
|
const scoreDelta = providerCredentialScore(b) - providerCredentialScore(a);
|
||||||
|
if (scoreDelta !== 0) return scoreDelta;
|
||||||
|
return a.account_key.localeCompare(b.account_key);
|
||||||
|
})[0] ??
|
||||||
|
providerCredentials.find((cred) => cred.active) ??
|
||||||
|
providerCredentials[0] ??
|
||||||
|
null
|
||||||
|
);
|
||||||
|
}
|
||||||
Loading…
Reference in New Issue