96 lines
4.1 KiB
TypeScript
96 lines
4.1 KiB
TypeScript
import { useEffect, useMemo, useState } from "react";
|
|
|
|
import { api } from "../services/api/client";
|
|
import type { ProviderProfile, ProviderProfileStatusItem } from "../types/domain";
|
|
|
|
export interface ProviderProfileOption {
|
|
value: ProviderProfile;
|
|
label: string;
|
|
configured?: boolean;
|
|
}
|
|
|
|
export const builtinProviderProfileOrder: readonly ProviderProfile[] = Object.freeze(["deepseek", "dsflash-go", "codex-api", "minimax-m3"]);
|
|
|
|
const builtinProviderProfileLabels: Readonly<Record<string, string>> = Object.freeze({
|
|
deepseek: "DeepSeek",
|
|
"dsflash-go": "DeepSeek V4 Flash",
|
|
"codex-api": "Codex API",
|
|
"minimax-m3": "MiniMax-M3"
|
|
});
|
|
|
|
export function providerProfileLabel(profile: ProviderProfile): string {
|
|
return builtinProviderProfileLabels[profile] ?? profile;
|
|
}
|
|
|
|
export function useProviderProfileOptions(selectedProfile: ProviderProfile): { options: ProviderProfileOption[] } {
|
|
const [options, setOptions] = useState<ProviderProfileOption[]>(() => defaultProviderProfileOptions(selectedProfile));
|
|
|
|
useEffect(() => {
|
|
let cancelled = false;
|
|
void (async () => {
|
|
const response = await api.providerProfileCatalog();
|
|
if (cancelled) return;
|
|
if (!response.ok) {
|
|
setOptions(defaultProviderProfileOptions(selectedProfile));
|
|
return;
|
|
}
|
|
const items = profileItems(response.data);
|
|
setOptions(items.length > 0 ? optionsFromItems(items, selectedProfile) : defaultProviderProfileOptions(selectedProfile));
|
|
})();
|
|
return () => {
|
|
cancelled = true;
|
|
};
|
|
}, [selectedProfile]);
|
|
|
|
const normalized = useMemo(() => ensureSelectedProfile(options, selectedProfile), [options, selectedProfile]);
|
|
return { options: normalized };
|
|
}
|
|
|
|
function profileItems(payload: unknown): ProviderProfileStatusItem[] {
|
|
const record = payload && typeof payload === "object" ? payload as { items?: ProviderProfileStatusItem[]; data?: { items?: ProviderProfileStatusItem[] } } : {};
|
|
return Array.isArray(record.items) ? record.items : Array.isArray(record.data?.items) ? record.data.items : [];
|
|
}
|
|
|
|
function optionsFromItems(items: ProviderProfileStatusItem[], selectedProfile: ProviderProfile): ProviderProfileOption[] {
|
|
const ordered = [...items]
|
|
.filter((item) => typeof item.profile === "string" && item.profile.trim().length > 0)
|
|
.sort((left, right) => compareProviderProfiles(left.profile, right.profile))
|
|
.map((item) => ({ value: item.profile, label: providerProfileLabel(item.profile), configured: item.configured } satisfies ProviderProfileOption));
|
|
return ensureSelectedProfile(uniqueOptions(ordered), selectedProfile);
|
|
}
|
|
|
|
function defaultProviderProfileOptions(selectedProfile: ProviderProfile): ProviderProfileOption[] {
|
|
const base = builtinProviderProfileOrder.map((profile) => ({ value: profile, label: providerProfileLabel(profile) } satisfies ProviderProfileOption));
|
|
return ensureSelectedProfile(base, selectedProfile);
|
|
}
|
|
|
|
function ensureSelectedProfile(options: ProviderProfileOption[], selectedProfile: ProviderProfile): ProviderProfileOption[] {
|
|
const normalized = String(selectedProfile ?? "").trim();
|
|
if (!normalized) return uniqueOptions(options);
|
|
if (options.some((option) => option.value === normalized)) return uniqueOptions(options);
|
|
return uniqueOptions([{ value: normalized, label: providerProfileLabel(normalized) }, ...options]);
|
|
}
|
|
|
|
function uniqueOptions(options: ProviderProfileOption[]): ProviderProfileOption[] {
|
|
const seen = new Set<string>();
|
|
const result: ProviderProfileOption[] = [];
|
|
for (const option of options) {
|
|
const value = String(option.value ?? "").trim();
|
|
if (!value || seen.has(value)) continue;
|
|
seen.add(value);
|
|
result.push({ ...option, value });
|
|
}
|
|
return result;
|
|
}
|
|
|
|
function compareProviderProfiles(left: ProviderProfile, right: ProviderProfile): number {
|
|
const leftIndex = builtinProviderProfileOrder.indexOf(left);
|
|
const rightIndex = builtinProviderProfileOrder.indexOf(right);
|
|
if (leftIndex >= 0 || rightIndex >= 0) {
|
|
if (leftIndex < 0) return 1;
|
|
if (rightIndex < 0) return -1;
|
|
if (leftIndex !== rightIndex) return leftIndex - rightIndex;
|
|
}
|
|
return left.localeCompare(right);
|
|
}
|