diff --git a/core/http/react-ui/src/pages/Backends.jsx b/core/http/react-ui/src/pages/Backends.jsx
index 4e73a9669..3cbb71872 100644
--- a/core/http/react-ui/src/pages/Backends.jsx
+++ b/core/http/react-ui/src/pages/Backends.jsx
@@ -26,6 +26,7 @@ export default function Backends() {
const [expandedRow, setExpandedRow] = useState(null)
const [confirmDialog, setConfirmDialog] = useState(null)
const [allBackends, setAllBackends] = useState([])
+ const [upgrades, setUpgrades] = useState({})
const fetchBackends = useCallback(async () => {
try {
@@ -52,6 +53,13 @@ export default function Backends() {
if (!loading) fetchBackends()
}, [operations.length])
+ // Fetch available upgrades
+ useEffect(() => {
+ backendsApi.checkUpgrades()
+ .then(data => setUpgrades(data || {}))
+ .catch(() => {})
+ }, [operations.length])
+
// Client-side filtering by tag
const filteredBackends = filter
? allBackends.filter(b => {
@@ -114,6 +122,15 @@ export default function Backends() {
})
}
+ const handleUpgrade = async (id) => {
+ try {
+ await backendsApi.upgrade(id)
+ addToast(`Upgrading ${id}...`, 'info')
+ } catch (err) {
+ addToast(`Upgrade failed: ${err.message}`, 'error')
+ }
+ }
+
const handleManualInstall = async (e) => {
e.preventDefault()
if (!manualUri.trim()) { addToast('Please enter a URI', 'warning'); return }
@@ -179,6 +196,14 @@ export default function Backends() {
Installed
+ {Object.keys(upgrades).length > 0 && (
+
+
+ {Object.keys(upgrades).length}
+
+
Updates
+
+ )}
Docs
@@ -300,6 +325,11 @@ export default function Backends() {
{/* Name */}
{b.name || b.id}
+ {b.version && (
+
+ v{b.version}
+
+ )}
|
{/* Description */}
@@ -346,9 +376,17 @@ export default function Backends() {
) : b.installed ? (
-
- Installed
-
+
+
+ Installed
+
+ {upgrades[b.name] && (
+
+
+ {upgrades[b.name].available_version ? `v${upgrades[b.name].available_version}` : 'Update'}
+
+ )}
+
) : (
Not Installed
@@ -361,9 +399,15 @@ export default function Backends() {
e.stopPropagation()}>
{b.installed ? (
<>
-
+ {upgrades[b.name] ? (
+
+ ) : (
+
+ )}
diff --git a/core/http/react-ui/src/pages/Manage.jsx b/core/http/react-ui/src/pages/Manage.jsx
index 6ac429ff4..3d24ebea8 100644
--- a/core/http/react-ui/src/pages/Manage.jsx
+++ b/core/http/react-ui/src/pages/Manage.jsx
@@ -22,6 +22,7 @@ export default function Manage() {
const [backendsLoading, setBackendsLoading] = useState(true)
const [reloading, setReloading] = useState(false)
const [reinstallingBackends, setReinstallingBackends] = useState(new Set())
+ const [upgrades, setUpgrades] = useState({})
const [confirmDialog, setConfirmDialog] = useState(null)
const [distributedMode, setDistributedMode] = useState(false)
const [togglingModels, setTogglingModels] = useState(new Set())
@@ -62,6 +63,15 @@ export default function Manage() {
nodesApi.list().then(() => setDistributedMode(true)).catch(() => {})
}, [fetchLoadedModels, fetchBackends])
+ // Fetch available backend upgrades
+ useEffect(() => {
+ if (activeTab === 'backends') {
+ backendsApi.checkUpgrades()
+ .then(data => setUpgrades(data || {}))
+ .catch(() => {})
+ }
+ }, [activeTab])
+
const handleStopModel = (modelName) => {
setConfirmDialog({
title: 'Stop Model',
@@ -169,6 +179,22 @@ export default function Manage() {
}
}
+ const handleUpgradeBackend = async (name) => {
+ try {
+ setReinstallingBackends(prev => new Set(prev).add(name))
+ await backendsApi.upgrade(name)
+ addToast(`Upgrading ${name}...`, 'info')
+ } catch (err) {
+ addToast(`Failed to upgrade: ${err.message}`, 'error')
+ } finally {
+ setReinstallingBackends(prev => {
+ const next = new Set(prev)
+ next.delete(name)
+ return next
+ })
+ }
+ }
+
const handleDeleteBackend = (name) => {
setConfirmDialog({
title: 'Delete Backend',
@@ -471,6 +497,17 @@ export default function Manage() {
For: {backend.Metadata.meta_backend_for}
)}
+ {backend.Metadata?.version && (
+
+
+ Version: v{backend.Metadata.version}
+ {upgrades[backend.Name] && (
+
+ → v{upgrades[backend.Name].available_version}
+
+ )}
+
+ )}
{backend.Metadata?.installed_at && (
@@ -485,12 +522,12 @@ export default function Manage() {
{!backend.IsSystem ? (
<>
diff --git a/core/http/react-ui/src/utils/api.js b/core/http/react-ui/src/utils/api.js
index b9d3983d2..1edac8cbd 100644
--- a/core/http/react-ui/src/utils/api.js
+++ b/core/http/react-ui/src/utils/api.js
@@ -120,6 +120,9 @@ export const backendsApi = {
installExternal: (body) => postJSON(API_CONFIG.endpoints.installExternalBackend, body),
getJob: (uid) => fetchJSON(API_CONFIG.endpoints.backendJob(uid)),
deleteInstalled: (name) => postJSON(API_CONFIG.endpoints.deleteInstalledBackend(name), {}),
+ checkUpgrades: () => fetchJSON(API_CONFIG.endpoints.backendsUpgrades),
+ forceCheckUpgrades: () => postJSON(API_CONFIG.endpoints.backendsUpgradesCheck, {}),
+ upgrade: (name) => postJSON(API_CONFIG.endpoints.upgradeBackend(name), {}),
}
// Chat API (non-streaming)
diff --git a/core/http/react-ui/src/utils/config.js b/core/http/react-ui/src/utils/config.js
index 70fb7f57a..66767eb3f 100644
--- a/core/http/react-ui/src/utils/config.js
+++ b/core/http/react-ui/src/utils/config.js
@@ -23,6 +23,9 @@ export const API_CONFIG = {
installExternalBackend: '/api/backends/install-external',
backendJob: (uid) => `/api/backends/job/${uid}`,
deleteInstalledBackend: (name) => `/api/backends/system/delete/${name}`,
+ backendsUpgrades: '/api/backends/upgrades',
+ backendsUpgradesCheck: '/api/backends/upgrades/check',
+ upgradeBackend: (name) => `/api/backends/upgrade/${name}`,
// Resources
resources: '/api/resources',