fix(AI): preserve semver tag in DB on AMD Ollama updates

Closes #855.

PR #804's AMD branch in `updateContainer()` overrode `newImage` to
`ollama/ollama:rocm` and then persisted that literal string to
`service.container_image` (line 1273). Two downstream consequences for
every AMD user who clicked Update on AI Assistant:

1. Apps page (`apps.tsx`) extracts the displayed version from
   `container_image` and rendered the literal string "rocm".
2. `ContainerRegistryService.getAvailableUpdates()` parsed `currentTag =
   "rocm"`, which isn't semver, so `parseMajorVersion` returned NaN, the
   filter didn't reject newer tags by major-version, and `isNewerVersion`
   treated any future tag as newer. Result: the same update reappeared
   on every check, forever.

Fix: separate "what we run" from "what we persist". `runtimeImage`
holds the tag passed to `docker.pull()` and `createContainer()` (still
`:rocm` for AMD), while `newImage` keeps the semver tag and is the
value written to the DB. Surgical: 3 references renamed plus 1
declaration added.

The install path (`_createContainer`) already had the right shape
(runtime-only override, no DB write of the override), so this PR only
touches `updateContainer`.

Test plan:
- `npm run typecheck` passes locally.
- Manual repro on NOMAD2 (AMD HX 370 / 890M, rc.2): before fix, DB
  shows `container_image = ollama/ollama:rocm` after triggering an
  Ollama update via Settings > Apps; Apps page shows version "rocm";
  `/api/system/services/check-updates` immediately re-reports the same
  update available. After fix, DB shows `container_image =
  ollama/ollama:<targetVersion>`; Apps page shows the semver; check-
  updates does not re-report the same update.
- nomad_ollama container itself still runs the `:rocm` image
  (verified via `docker inspect`).

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
This commit is contained in:
Chris Sherwood 2026-05-11 10:12:47 -07:00 committed by Jake Turner
parent 6c799dd602
commit 019a5a4927
1 changed files with 12 additions and 8 deletions

View File

@ -1103,13 +1103,17 @@ export class DockerService {
this.activeInstallations.add(serviceName)
// Compute new image string. AMD-on-Ollama overrides this to the rolling :rocm tag
// (set during GPU detection below) since per-version ROCm tags aren't always published.
// newImage = the semver tag we record in the DB after the update (e.g. ollama/ollama:0.23.2).
// runtimeImage = the tag we actually pull and run. For AMD-on-Ollama these diverge: we run
// the rolling :rocm tag because per-version ROCm tags aren't always published, but the DB
// must keep the semver tag so the Apps page shows the actual version (not literally "rocm")
// and the registry update-check parses a valid tag (instead of looping on the same update).
const currentImage = service.container_image
const imageBase = currentImage.includes(':')
? currentImage.substring(0, currentImage.lastIndexOf(':'))
: currentImage
let newImage = `${imageBase}:${targetVersion}`
const newImage = `${imageBase}:${targetVersion}`
let runtimeImage = newImage
// GPU detection runs before the pull so AMD updates pull ollama/ollama:rocm rather
// than the standard tag. Detection result is reused below when building the new
@ -1137,7 +1141,7 @@ export class DockerService {
'update-gpu-config',
`AMD GPU detected. Using ROCm image with /dev/kfd and /dev/dri passthrough...`
)
newImage = 'ollama/ollama:rocm'
runtimeImage = 'ollama/ollama:rocm'
updatedAmdDevices = await this._discoverAMDDevices()
updatedAmdGpuConfigured = true
} else {
@ -1158,9 +1162,9 @@ export class DockerService {
}
}
// Step 1: Pull new image
this._broadcast(serviceName, 'update-pulling', `Pulling image ${newImage}...`)
const pullStream = await this.docker.pull(newImage)
// Step 1: Pull new image (runtimeImage diverges from newImage for AMD, see above)
this._broadcast(serviceName, 'update-pulling', `Pulling image ${runtimeImage}...`)
const pullStream = await this.docker.pull(runtimeImage)
await new Promise((res) => this.docker.modem.followProgress(pullStream, res))
// Step 2: Find and stop existing container
@ -1205,7 +1209,7 @@ export class DockerService {
}
const newContainerConfig: any = {
Image: newImage,
Image: runtimeImage,
name: serviceName,
Env: finalEnv.length > 0 ? finalEnv : undefined,
Cmd: inspectData.Config?.Cmd || undefined,