Studio: add Studio version info to General settings (#5675)
* chore: add Studio version info to General settings * chore: cache Studio version fetch * fix: clear cached Studio version request on failure * fix: retry incomplete Studio version fetch * fix: remove cached Studio version fetch * chore: move connections below API * fix: localize studio version rows
This commit is contained in:
parent
fbe4d08ad0
commit
5fa253c99e
4 changed files with 55 additions and 24 deletions
|
|
@ -1,7 +1,7 @@
|
|||
// SPDX-License-Identifier: AGPL-3.0-only
|
||||
// Copyright 2026-present the Unsloth AI Inc. team. All rights reserved. See /studio/LICENSE.AGPL-3.0
|
||||
|
||||
import { getAuthToken } from "@/features/auth";
|
||||
import { getAuthToken, refreshSession } from "@/features/auth";
|
||||
import { useT } from "@/i18n";
|
||||
import { apiUrl } from "@/lib/api-base";
|
||||
import { useEffect, useState } from "react";
|
||||
|
|
@ -9,29 +9,57 @@ import { SettingsRow } from "./settings-row";
|
|||
import { SettingsSection } from "./settings-section";
|
||||
|
||||
type ApiObject = Record<string, unknown>;
|
||||
|
||||
async function fetchStudioVersions(): Promise<{
|
||||
type StudioVersions = {
|
||||
packageVersion: string | null;
|
||||
studioVersion: string | null;
|
||||
}> {
|
||||
};
|
||||
|
||||
const EMPTY_VERSIONS: StudioVersions = {
|
||||
packageVersion: null,
|
||||
studioVersion: null,
|
||||
};
|
||||
|
||||
function parseStudioVersions(data: ApiObject): StudioVersions {
|
||||
const packageVersion = data["version"];
|
||||
const studioVersion = data["studio_version"];
|
||||
return {
|
||||
packageVersion:
|
||||
typeof packageVersion === "string" ? packageVersion : null,
|
||||
studioVersion: typeof studioVersion === "string" ? studioVersion : null,
|
||||
};
|
||||
}
|
||||
|
||||
function hasAllVersions(versions: StudioVersions): boolean {
|
||||
return Boolean(versions.packageVersion && versions.studioVersion);
|
||||
}
|
||||
|
||||
async function requestStudioVersions(
|
||||
token: string | null,
|
||||
): Promise<StudioVersions> {
|
||||
const headers = new Headers();
|
||||
if (token) headers.set("Authorization", `Bearer ${token}`);
|
||||
const res = await fetch(apiUrl("/api/health"), { headers });
|
||||
if (!res.ok) {
|
||||
return EMPTY_VERSIONS;
|
||||
}
|
||||
const data = (await res.json()) as ApiObject;
|
||||
return parseStudioVersions(data);
|
||||
}
|
||||
|
||||
async function fetchStudioVersions(): Promise<StudioVersions> {
|
||||
try {
|
||||
const token = getAuthToken();
|
||||
const headers = new Headers();
|
||||
if (token) headers.set("Authorization", `Bearer ${token}`);
|
||||
const res = await fetch(apiUrl("/api/health"), { headers });
|
||||
if (!res.ok) {
|
||||
return { packageVersion: null, studioVersion: null };
|
||||
const versions = await requestStudioVersions(token);
|
||||
if (!token || hasAllVersions(versions)) {
|
||||
return versions;
|
||||
}
|
||||
const data = (await res.json()) as ApiObject;
|
||||
const packageVersion = data.version;
|
||||
const studioVersion = data.studio_version;
|
||||
return {
|
||||
packageVersion:
|
||||
typeof packageVersion === "string" ? packageVersion : null,
|
||||
studioVersion: typeof studioVersion === "string" ? studioVersion : null,
|
||||
};
|
||||
|
||||
if (await refreshSession()) {
|
||||
return requestStudioVersions(getAuthToken());
|
||||
}
|
||||
return versions;
|
||||
} catch {
|
||||
return { packageVersion: null, studioVersion: null };
|
||||
return EMPTY_VERSIONS;
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -50,18 +50,18 @@ const TABS: TabDef[] = [
|
|||
icon: PaintBrush02Icon,
|
||||
},
|
||||
{ id: "chat", labelKey: "settings.tabs.chat", icon: Message01Icon },
|
||||
{
|
||||
id: "connections",
|
||||
labelKey: "settings.tabs.connections",
|
||||
icon: CloudIcon,
|
||||
badgeKey: "common.new",
|
||||
},
|
||||
{
|
||||
id: "api-keys",
|
||||
labelKey: "settings.tabs.apiKeys",
|
||||
icon: Globe02Icon,
|
||||
badgeKey: "common.new",
|
||||
},
|
||||
{
|
||||
id: "connections",
|
||||
labelKey: "settings.tabs.connections",
|
||||
icon: CloudIcon,
|
||||
badgeKey: "common.new",
|
||||
},
|
||||
{ id: "about", labelKey: "settings.tabs.about", icon: HelpCircleIcon },
|
||||
];
|
||||
|
||||
|
|
|
|||
|
|
@ -125,6 +125,7 @@ export function AboutTab() {
|
|||
{hw.gpus.map((gpu, i) => (
|
||||
<SettingsRow
|
||||
// Index key: device order from the backend is stable per request.
|
||||
// biome-ignore lint/suspicious/noArrayIndexKey: The hardware API does not expose a stable device id.
|
||||
key={i}
|
||||
label={
|
||||
hw.gpus.length > 1
|
||||
|
|
|
|||
|
|
@ -296,6 +296,8 @@ export function GeneralTab() {
|
|||
</SettingsRow>
|
||||
</SettingsSection>
|
||||
|
||||
<StudioVersionSection />
|
||||
|
||||
<SettingsSection title={t("settings.general.helperLlm.sectionTitle")}>
|
||||
<SettingsRow
|
||||
label={t("settings.general.helperLlm.preloadOnStartup")}
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue