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:
Lee Jackson 2026-06-15 19:41:12 +01:00 committed by GitHub
commit 5fa253c99e
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
4 changed files with 55 additions and 24 deletions

View file

@ -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;
}
}

View file

@ -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 },
];

View file

@ -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

View file

@ -296,6 +296,8 @@ export function GeneralTab() {
</SettingsRow>
</SettingsSection>
<StudioVersionSection />
<SettingsSection title={t("settings.general.helperLlm.sectionTitle")}>
<SettingsRow
label={t("settings.general.helperLlm.preloadOnStartup")}