Spaces:
Paused
Paused
File size: 4,730 Bytes
7824fcd c231bc0 7824fcd 4a940a5 c231bc0 7824fcd 5416ffb 4a940a5 94a5578 4a940a5 7824fcd 4a940a5 94a5578 4a940a5 94a5578 4a940a5 94a5578 4a940a5 94a5578 4a940a5 94a5578 4a940a5 7824fcd 5416ffb 7824fcd 4a940a5 | 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 | import { useState, useEffect, useCallback, useMemo } from "preact/hooks";
export interface CatalogModel {
id: string;
displayName: string;
isDefault: boolean;
supportedReasoningEfforts: { reasoningEffort: string; description: string }[];
defaultReasoningEffort: string;
}
export interface ModelFamily {
id: string;
displayName: string;
efforts: { reasoningEffort: string; description: string }[];
defaultEffort: string;
}
/**
* Extract model family ID from a model ID.
* gpt-5.3-codex-high → gpt-5.3-codex
* gpt-5.3-codex-spark → gpt-5.3-codex-spark (spark is a distinct family)
* gpt-5.4 → gpt-5.4
*/
function getFamilyId(id: string): string {
// Bare model: gpt-5.4
if (/^gpt-\d+(?:\.\d+)?$/.test(id)) return id;
// Spark family: gpt-X.Y-codex-spark
if (/^gpt-\d+(?:\.\d+)?-codex-spark$/.test(id)) return id;
// Mini family: gpt-X.Y-codex-mini
if (/^gpt-\d+(?:\.\d+)?-codex-mini$/.test(id)) return id;
// Codex base or tier variant (high/mid/low/max): family = gpt-X.Y-codex
const m = id.match(/^(gpt-\d+(?:\.\d+)?-codex)(?:-(?:high|mid|low|max))?$/);
if (m) return m[1];
// Legacy: gpt-5-codex, gpt-5-codex-mini
const legacy = id.match(/^(gpt-\d+-codex)(?:-(?:high|mid|low|max|mini))?$/);
if (legacy) return legacy[1];
return id;
}
/** Check if a model ID is a tier variant (not the base family model). */
function isTierVariant(id: string): boolean {
return /^gpt-\d+(?:\.\d+)?-codex-(?:high|mid|low|max)$/.test(id);
}
export function useStatus(accountCount: number) {
const [baseUrl, setBaseUrl] = useState("Loading...");
const [apiKey, setApiKey] = useState("Loading...");
const [models, setModels] = useState<string[]>([]);
const [selectedModel, setSelectedModel] = useState("");
const [modelCatalog, setModelCatalog] = useState<CatalogModel[]>([]);
const [selectedEffort, setSelectedEffort] = useState("medium");
const [selectedSpeed, setSelectedSpeed] = useState<string | null>(null);
const fetchModels = useCallback(async (isInitial: boolean) => {
try {
// Fetch full catalog for effort info
const catalogResp = await fetch("/v1/models/catalog");
const catalogData: CatalogModel[] = await catalogResp.json();
setModelCatalog(catalogData);
// Also fetch model list (includes aliases)
const resp = await fetch("/v1/models");
const data = await resp.json();
const ids: string[] = data.data.map((m: { id: string }) => m.id);
if (ids.length > 0) {
setModels(ids);
if (isInitial) {
const defaultModel = catalogData.find((m) => m.isDefault)?.id ?? ids[0] ?? "";
setSelectedModel(defaultModel);
} else {
// On refresh: only reset if current selection is no longer available
setSelectedModel((prev) => {
if (ids.includes(prev)) return prev;
return catalogData.find((m) => m.isDefault)?.id ?? ids[0] ?? prev;
});
}
}
} catch {
if (isInitial) setModels([]);
}
}, []);
useEffect(() => {
let intervalId: ReturnType<typeof setInterval> | null = null;
async function loadStatus() {
try {
const resp = await fetch("/auth/status");
const data = await resp.json();
if (!data.authenticated) return;
setBaseUrl(`${window.location.origin}/v1`);
setApiKey(data.proxy_api_key || "any-string");
await fetchModels(true);
// Refresh model list every 60s to pick up dynamic backend changes
intervalId = setInterval(() => { fetchModels(false); }, 60_000);
} catch (err) {
console.error("Status load error:", err);
}
}
loadStatus();
return () => { if (intervalId) clearInterval(intervalId); };
}, [fetchModels, accountCount]);
// Build model families — group catalog by family, excluding tier variants
const modelFamilies = useMemo((): ModelFamily[] => {
if (modelCatalog.length === 0) return [];
const familyMap = new Map<string, ModelFamily>();
for (const m of modelCatalog) {
const fid = getFamilyId(m.id);
// Only use the base family model (not tier variants) to define the family
if (isTierVariant(m.id)) continue;
if (familyMap.has(fid)) continue;
familyMap.set(fid, {
id: fid,
displayName: m.displayName,
efforts: m.supportedReasoningEfforts,
defaultEffort: m.defaultReasoningEffort,
});
}
return [...familyMap.values()];
}, [modelCatalog]);
return {
baseUrl,
apiKey,
models,
selectedModel,
setSelectedModel,
selectedEffort,
setSelectedEffort,
selectedSpeed,
setSelectedSpeed,
modelFamilies,
modelCatalog,
};
}
|