Skip to content

feat: add Ollama model management widget#15

Open
Alarisco wants to merge 55 commits into
HANCORE-linux:mainfrom
Alarisco:feat/ollama-widget
Open

feat: add Ollama model management widget#15
Alarisco wants to merge 55 commits into
HANCORE-linux:mainfrom
Alarisco:feat/ollama-widget

Conversation

@Alarisco

Copy link
Copy Markdown

Summary

Adds an optional, default-off Ollama widget for monitoring GPU usage and managing local models directly from the Quickshell bar.

This supersedes #14 and is updated for the latest main changes.

Features

  • GPU utilization bar widget with compact mode and sparkline history
  • Installed and loaded model overview with VRAM and runtime details
  • Exclusive model loading, eject, delete confirmation, and model pull progress
  • Runtime Keep Alive and context-length configuration
  • Live model state from Ollama's HTTP API
  • Shared Ollama data service with guarded asynchronous operations and error handling
  • Adaptive polling: 2 seconds while the panel is open, 15 seconds while closed

Bar Integration

  • Uses the existing slot delegate for drag/drop and persisted ordering
  • Supports split groups and live panel anchoring after layout changes
  • Participates in per-monitor narrow-stage reduction as group G16
  • Preserves legacy bar-order and split caches
  • Preserves the current main widget-cache schema (archBadgeShell at +31) and appends Ollama fields
  • Coexists with the integrated shell updater fallback introduced in the latest main

Reliability And Safety

  • Preserves numeric -1 for infinite Keep Alive values
  • Retries transient empty /api/ps responses without locking controls
  • Persists pending runtime configuration across restarts
  • Rejects malformed context values instead of partially parsing them
  • Validates curl status and final Ollama responses before reporting pulls as successful
  • Uses a private per-attempt progress file under the user runtime directory
  • Avoids privileged commands, service restarts, and Ollama CLI dependencies

Testing

  • QtTest suite: 44 passed, 0 failed
  • Ollama wiring regression test
  • Native Quickshell component smoke test
  • Bar-order migration coverage
  • Arch, theme, and shell updater regression suites
  • Bash syntax checks and git diff --check

Notes

Ollama is optional. The widget is disabled by default, and users without a local Ollama runtime will not incur polling or visible UI changes.

Alarisco added 30 commits July 11, 2026 19:57
Slow polling to 15s when panel is closed (reduced CPU/memory load), restore 2s
when panel is open for responsive GPU and loaded-model updates. Follows
the same adaptive pattern already used by the AI usage timer.
@Alarisco

Copy link
Copy Markdown
Author

All six findings from the review have been addressed. Changes are on the branch at a3ba630.


P1 — Runtime configuration not restored (4235dac)

FileView.text is a function in Quickshell 0.3.0; the code was stringifying it instead of calling it. Fixed by using runtimeConfigFile.text(). onLoadFailed no longer calls the parser, leaving defaults intact when the file is missing. The native smoke fixture now calls Qt.callLater(Qt.quit) after PASS/FAIL and the wrapper accepts only exit status 0 (124/137 are failures).

P1 — Parallel refreshes overwriting each other's errors (51d32dc)

Replaced the single lastError property with four independent slots: versionError, tagsError, loadedError, and actionError. lastError is now a readonly computed property using stable precedence (actionError || versionError || tagsError || loadedError). connected is readonly derived as the logical OR of per-endpoint connected flags, making it independent of completion order. A successful response from one endpoint can only clear its own slot. 8 new QtTest cases cover all error-order permutations.

P1 — Delete accepting stale tag data (6bbb1fc)

runModelAction now increments refreshEpoch before issuing the DELETE request, so any pre-delete in-flight tags or loaded-model response is rejected by the epoch guard. refreshTags and refreshLoaded set a boolean pending flag when their process is already running instead of dropping the call. The onExited handlers on both processes check the flag and start one coalesced follow-up refresh, guaranteeing exactly one post-delete update.

P2 — Pull progress spawning tail every 500 ms (dc9018a)

Replaced the progress file, pullProgressTimer, progressReaderProc, and the shell wrapper with a direct curl argv command. pullProc.stdout is now a SplitParser that emits each complete JSON line immediately. The last received line is retained in pullLastLine for completion validation via pullResultState(). Zero recurring child processes are spawned during a pull. Dead _exitCode property cleaned up in a3ba630.

P2 — Native smoke test accepting a killed/hung shell (4235dac)

Addressed in the same commit as P1 above.

P2 — GPU metric reporting only the first GPU (20b50ef)

The collection command now emits all samples from nvidia-smi (every GPU, no head -1) and every readable DRM/hwmon sysfs path (no early exit 0). A new pure helper maxGpuPercent(raw) in OllamaDataLogic.js parses all numeric lines, rejects values outside [0, 100], and returns the maximum (or -1 when no valid samples exist). Widget label updated to "GPU max". Covered by 5 new QtTest cases.


Two-monitor UI smoke — completed manually:

  • Feature disabled: no visible changes on either output.
  • Enable/disable and compact mode toggled from both outputs.
  • Drag G16 within each region and across all three regions; both bars synchronized; order persisted after restart.
  • Split/merge before and after G16 on both outputs; state persisted after restart.
  • Narrow-stage reduction exercised on both outputs; G16 hid at stage 1 without overlap.
  • Panel opened, closed, and re-opened from each output; anchored under G16 and dismissed correctly.
  • One output disconnected and reconnected with the panel open; no stale panel, no duplicate bar.

Test results at a3ba630:

  • QtTest: 57 passed, 0 failed
  • Ollama wiring regression: passed
  • Native Quickshell smoke: exited 0
  • Arch, theme, and shell updater regression suites: passed
  • git diff --check: clean
  • Synthetic merge with current main (5fde51e): conflict-free

@Alarisco Alarisco requested a review from HANCORE-linux July 12, 2026 10:51
@HANCORE-linux

Copy link
Copy Markdown
Owner

Thanks for the thorough follow-up. I verified the updated branch at a3ba630.

The runtime-config restore, per-endpoint error slots, delete epoch/coalesced refresh, streaming pull progress, clean native-smoke exit, and multi-GPU maximum are all present. I also confirmed the reported automated results locally: 57/57 QtTests, wiring, native smoke, and the Arch/Theme/Shell updater regressions pass. The original fixes are well targeted.

One P1 issue remains in the new connection-state aggregation:

  • applyTags() sets tagsConnected = true after success but never resets it on HTTP or parse failure.
  • applyLoaded() does the same with loadedConnected.
  • handleOperationModels() also needs to clear loadedConnected for every failure path, including malformed responses.

Native reproduction against a3ba630:

CONNECTED_AFTER_SUCCESS=true
CONNECTED_AFTER_ALL_FAILURES=true
ENDPOINT_FLAGS=false,true,true

So after Ollama has responded successfully once, all three current endpoint requests can fail while the widget still reports connected=true.

Please clear the respective endpoint flag on both transport/HTTP and parse failures. Add an integration-style test against the real OllamaData component, not only the pure aggregateConnected() helper:

  1. Make version, tags, and loaded responses succeed and assert connected.
  2. Then fail all three and assert all endpoint flags plus connected are false.
  3. Cover malformed tags and loaded responses as failures as well.

After that test passes, I do not currently see another automated merge blocker. The documented two-monitor smoke remains accepted as the contributor's manual runtime validation.

Alarisco added 2 commits July 12, 2026 13:10
applyTags() and applyLoaded() were setting the endpoint connected flag
to true on success but not resetting it to false on HTTP/transport or
parse failures, so after one successful response all subsequent
failures left connected=true.

All three failure paths now clear the flag:
- applyTags(): HTTP failure and parse exception
- applyLoaded(): HTTP failure and parse exception
- handleOperationModels(): malformed response (catch path) in addition
  to the existing exitCode != 0 branch
Exercises the real OllamaData component (not just pure-logic helpers)
against the three new-in-review scenarios:

1. Version, tags, and loaded all succeed -> connected true and all
   endpoint flags true.
2. All three then fail via HTTP error -> every flag and connected are
   false.
3. Succeed again, then fail with malformed response bodies (unparseable
   JSON for tags; non-array models for loaded) -> respective endpoint
   connected flags reset to false.
@Alarisco

Copy link
Copy Markdown
Author

All points addressed at 65918e3.

  • Endpoint connected flags now reset to false on every failure path (HTTP error and parse exception) in applyTags(), applyLoaded(), and handleOperationModels().
  • Integration-style regression added to OllamaDataSmoke.qml covering the three scenarios: all succeed, all fail, malformed response bodies.

57 QtTests, wiring, native smoke, and updater regressions all pass.

@HANCORE-linux

Copy link
Copy Markdown
Owner

The offline merged runtime test is now in progress against the real local qwen2.5-coder:7b model. Core integration passed so far: default-off behavior, enable/disable, populated panel, compact mode, G16 drag across regions, and an initial split/merge smoke.

Two UI findings were reproduced during the live test:

P2 - Delete button loses contrast and the glyph is not fully rendered

In OllamaPanel.qml, the delete hover state uses root.fillPrimaryHover for the button background while changing the icon to root.seal. On the tested gold theme these are too close, so the trash icon becomes difficult to distinguish. The trash glyph also appears partially clipped/incomplete inside the 28x28 control.

Please keep the icon fully bounded and use a foreground color with clear contrast in all three states: idle, hover, and armed-for-confirmation. The armed state should remain visually distinct from a normal hover.

Verification: on a dark/gold theme, inspect idle -> hover -> first-click/armed at scale 1.0. The complete glyph must remain visible and immediately recognizable in every state.

P2 - Tooltip coverage is inconsistent for icon-only controls

Some icon controls already use TooltipMixin, but the delete action and the header Refresh/Close icon buttons do not. This leaves the destructive action unexplained and makes similar icon-only controls behave inconsistently.

Please add tooltips to all icon-only controls. For delete, the UI/tooltip should make the two-stage behavior explicit: the first click only arms confirmation; the second click permanently deletes the local model; Escape, closing the panel, or clicking elsewhere cancels the armed state.

Verification: hover every icon-only control and confirm a correctly anchored tooltip appears without clipping. For delete, verify that the first click performs no deletion, the armed state is clearly communicated, cancellation works, and only the second confirmed click sends DELETE.

P2 - Configuration option chips have no hover feedback

The clickable Keep Alive (5m, 30m, infinity) and Context (auto, 8k, 16k, 32k, Custom) options do not expose the same interaction feedback as the other controls. Their delegates have no hover-aware state, no pointing cursor, and no hover color animation. The surrounding Configuration toggle does have hover feedback, which makes the options inside it feel inconsistent and less obviously interactive.

Please give these clickable option chips the same restrained hover treatment used by the rest of the panel while preserving the stronger selected state. Disabled/busy options must remain visually distinct and must not show an active cursor.

Verification: move across every Keep Alive and Context option in selected, unselected, and disabled states. Unselected enabled options should visibly but subtly react; selected options must remain clearly selected; disabled options must not imply clickability; no layout shift should occur.

No code was changed during this test. These are runtime UI findings from the isolated local merge candidate.

@HANCORE-linux

Copy link
Copy Markdown
Owner

Additional UI consistency findings from the same offline runtime test:

P2 - Configuration alignment and icon scale do not match the surrounding UI

The Configuration label/indicator does not appear optically centered within its control. The bottom configuration/gear icon also renders noticeably smaller than comparable icon controls in the Control Center, so the row does not read as a balanced control group.

Please align the label and disclosure indicator as one visually centered unit, and use the established Control Center icon sizing/bounds for the gear action rather than a visually smaller glyph. This should be optical alignment, not only mathematical anchors.centerIn, because icon-font metrics can still look displaced.

Verification: compare the Configuration control and gear action directly with existing Control Center icon controls at scale 1.0. Their visual centers, glyph bounds, and perceived icon sizes should match without shifting on hover.

P2 - Infinite Keep Alive option is too small to read reliably

The infinity option immediately to the right of 30m uses the same 10px text size as the other labels. The glyph has substantially less visual mass at that size and is difficult to recognize in the 36x22 chip.

Please size the infinity glyph optically rather than forcing the same text size as 5m/30m, while keeping the chip dimensions stable. A tooltip or accessible label such as Keep alive indefinitely should make its meaning unambiguous.

Verification: at scale 1.0, 5m, 30m, and infinity must be equally legible; the infinity glyph must be centered and recognizable in idle, hover, selected, and disabled states, with no layout shift.

No code was changed; these are additional observations from the isolated merged candidate.

@HANCORE-linux

Copy link
Copy Markdown
Owner

One more configuration-layout issue confirmed in the offline runtime candidate:

P2 - Custom Context text is not centered like the other option chips

The normal Context options use centered UiText at 10px. The Custom option instead uses a TextInput filling the chip with 6px left/right margins and default left alignment; its placeholder is also explicitly left-anchored. This makes Custom visibly misaligned with auto, 8k, 16k, and 32k, even though the font size itself matches.

Please use the same optical horizontal/vertical alignment and typography contract as the other configuration chips for both the placeholder and entered value. Editing/focus state must not shift the text or resize the row.

Verification: compare auto, numeric options, the inactive Custom placeholder, focused Custom input, and a saved custom value at scale 1.0. Baselines and visual centers must match, the caret must remain visible, long values must stay within the stable chip bounds, and no layout shift may occur.

No code was changed; this was confirmed from the merged runtime candidate and the corresponding QML layout.

@HANCORE-linux

Copy link
Copy Markdown
Owner

P2 - Pull field rejects a pasted ollama pull ... command

The runtime test reproduced invalid model name when pasting:

ollama pull qwen2.5-coder:7b

Root cause is in pullModel(): input normalization strips an ollama run prefix, but not ollama pull . The complete string is therefore sent as the JSON model value to /api/pull, which Ollama correctly rejects.

This is inconsistent: the field is specifically a Pull control, and accepting ollama run ... while rejecting the more natural ollama pull ... command is unexpected.

Please normalize the explicitly supported command forms before constructing the JSON request, while keeping the existing direct argv/JSON path and not introducing shell execution. Inputs containing flags or a command without a model should fail locally with a clear validation message rather than being sent to Ollama.

Verification cases:

  • qwen2.5-coder:7b -> qwen2.5-coder:7b
  • ollama pull qwen2.5-coder:7b -> qwen2.5-coder:7b
  • existing supported ollama run qwen2.5-coder:7b -> qwen2.5-coder:7b
  • surrounding whitespace/case variants behave according to the documented contract
  • ollama pull without a model is rejected locally
  • command flags or additional shell-like tokens are rejected, not executed and not silently folded into the model name

No code was changed; this was reproduced against the isolated merged candidate and the real local Ollama API.

@HANCORE-linux

Copy link
Copy Markdown
Owner

P2 - Pull progress has no transferred-size, speed, or ETA feedback

During the real 4.7 GiB model pull, the panel shows status/progress/percentage but no estimated remaining time. This matches the implementation: applyPullProgress() consumes completed and total only to calculate pullProgress and pullPercent; it keeps no sample timestamps, transfer rate, downloaded-size label, or ETA state.

Please add compact transfer feedback such as:

1.8 / 4.7 GiB · 42 MiB/s · about 1m 10s remaining

Important: Ollama progress is reported per digest/layer. A naive (total - completed) / rate presented as whole-model ETA can be misleading when the next layer begins. Track samples per digest and either aggregate all known layers with a smoothed throughput, or explicitly label the estimate as the current layer. When there are not enough stable samples, show Calculating... or omit ETA rather than displaying a false value.

Recommended behavior:

  • derive rate from monotonic time and byte deltas, using a short moving average/EWMA;
  • reset rate/ETA state for each new pull and handle digest/layer changes;
  • never show negative, infinite, or wildly stale ETA;
  • retain the last stable UI while a sample is malformed;
  • keep dimensions stable so the panel does not shift as labels change;
  • completion and failure must clear ETA state.

Verification: exercise a throttled multi-layer fixture with changing rates and a real model pull. Confirm size/rate update, ETA stabilizes after several samples, layer transitions do not produce negative or nonsensical values, and success/failure resets the display.

P2 - Raw layer digests are exposed as user-facing status

The real pull displays statuses such as pulling 60.... This is Ollama's raw per-layer SHA-256 digest. It is useful for diagnostics but has no actionable meaning in the normal panel and reads like an unexplained error/code.

Please map known Ollama states to user-facing labels, for example:

  • pulling manifest -> Preparing model...
  • pulling <digest> -> Downloading model data... or a tracked Downloading layer n/N...
  • verifying sha256 digest -> Verifying download...
  • writing manifest -> Finalizing...
  • success -> Done

Keep the raw digest only in an optional diagnostic detail/tooltip if needed. Unknown statuses should degrade to a readable generic download state rather than exposing an opaque identifier.

P1 - An active pull cannot be cancelled from the panel

While pullBusy is true, the input row is replaced by progress, but there is no Cancel control and no cancelPull() state transition. A multi-gigabyte download can therefore not be stopped from the UI that started it.

Please add an explicit Cancel action during pull. Cancellation should terminate the active curl process, distinguish Cancelled from Download failed, clear/reset progress state deterministically, leave the panel usable for retry, and not close merely because the panel is hidden. Do not require confirmation for cancellation because retrying is safe.

Verification:

  • start a delayed pull and cancel it during transfer;
  • the curl child exits promptly and no polling/helper process remains;
  • UI reports Cancelled, not Failed or Done;
  • progress/ETA state resets and a subsequent retry works;
  • closing/reopening the panel without pressing Cancel does not stop the transfer;
  • confirm with the Ollama backend that client disconnect actually cancels server-side work rather than only hiding progress.

No code was changed; this was observed during the isolated merged runtime test.

@HANCORE-linux

Copy link
Copy Markdown
Owner

P1 - Successful pull is not reconciled into Installed Models

The real end-to-end pull exposed a lifecycle gap:

  • the client curl process ended at approximately 14:08:22;
  • /api/tags / ollama list was still empty immediately afterward;
  • Ollama continued finalizing the partial blob server-side;
  • the model was registered roughly two minutes later;
  • the open panel did not add qwen2.5-coder:7b automatically;
  • the user had not pressed Refresh or reopened the panel.

This matches the control flow: finishPull() performs one immediate refreshTags() when curl exits. If Ollama has not registered the manifest yet, that refresh returns an empty list. There is no periodic tags refresh or bounded post-pull verification, so the UI remains stale indefinitely.

Please treat stream success as Finalizing..., then verify the exact requested model through a bounded retry/reconcile loop. Do not report completion until the model is visible through the Ollama API and the installed-model list has been updated. The verification must continue if the panel is hidden and must not introduce high-frequency process polling.

Verification fixture:

  1. Pull stream returns success.
  2. /api/tags omits the model for several delayed responses.
  3. A later response includes the requested model.
  4. UI stays in Finalizing during the delay, adds the model automatically, then reports success.
  5. A bounded timeout produces an explicit finalization error instead of Done with a stale list.

P2 - Pull success feedback disappears immediately

finishPull() assigns pullStatus = "Done" and then immediately sets pullBusy = false. The progress column containing pullStatus is visible only while pullBusy, so the success state disappears as soon as it is written. There is no elapsed time, transient completion banner, or completion notification.

After successful reconciliation, show a short stable confirmation such as:

qwen2.5-coder:7b ready · pulled in 6m 24s

Keep it visible for a few seconds without shifting panel dimensions. If the panel was closed during a long pull, surface completion through the existing shell notification/badge pattern. Failure should remain visible until acknowledged; success may auto-dismiss.

Verification: use a deterministic clock fixture and assert that success appears only after tag reconciliation, contains the model and elapsed duration, remains visible for the intended interval, then clears without hiding an error or moving the layout.

No code was changed. This is evidence from the isolated merged candidate and the real Ollama pull lifecycle.

@HANCORE-linux

Copy link
Copy Markdown
Owner

Follow-up to the 65918e3 fix report: the connected-state correction and its native integration smoke are verified. We then merged the PR locally into current main without pushing and ran the candidate against a real local qwen2.5-coder:7b installation.

Before this follow-up, origin/main was fetched again and remains 5fde51e. It is a full ancestor of both PR head 65918e3 and local merge candidate 578887f; the recent shell-updater, NetworkManager, and Rebel commits are therefore retained. The merge diff contains only the 16 declared Ollama/bar-integration files and does not modify the updater scripts, NetworkManager implementation, or Rebel asset.

Verified working

  • conflict-free offline merge into current main;
  • 57/57 QtTests, native smoke, wiring, QML lint, and Arch/Theme/Shell regressions pass;
  • default-off produces zero Ollama HTTP polling and no visible widget;
  • enable/disable, populated panel, compact mode, single-monitor G16 drag, and initial split/merge work;
  • real API parsing reports version, installed/loaded model, VRAM, context, and GPU correctly;
  • a real 4.7 GiB pull completed successfully;
  • exactly one curl process handled the pull, with no recurring tail or helper process.

Remaining P1 blockers

  1. No pull cancellation: a multi-gigabyte transfer started by the panel cannot be stopped from the panel. Cancellation needs an explicit state transition, prompt child termination, correct Cancelled feedback, retry support, and proof that server-side work also stops. Details: feat: add Ollama model management widget #15 (comment)

  2. Post-pull reconciliation is stale: curl ended, /api/tags was still empty, Ollama continued finalizing for roughly two minutes, and the open panel never added the model automatically. The single immediate refreshTags() is insufficient; use bounded finalization verification and report success only after the requested model is visible. Details: feat: add Ollama model management widget #15 (comment)

Remaining UI/UX work

Please add targeted regressions for cancellation, delayed post-pull tag visibility, command normalization, status mapping, and completion-state lifetime. The visual findings require a runtime theme/scale smoke in addition to static assertions.

Single-monitor runtime behavior is independently verified. The two-monitor matrix remains the contributor's documented validation because the reviewer currently has only one output available.

No project or live files were modified for these findings; the candidate runs from an isolated local review worktree.

@Alarisco

Copy link
Copy Markdown
Author

@HANCORE-linux Thanks for the detailed runtime review. I addressed the follow-up findings in the new commits.

  • Replaced the ambiguous armed trash action with an explicit inline confirmation containing the model name, Cancel, and a labeled danger action. Escape, backdrop click, panel close, and other model actions cancel it.
  • Added tooltip coverage for the delete, refresh, close, and other icon-only controls; configuration chips now provide hover/cursor feedback while preserving disabled and selected states.
  • Corrected configuration-chip alignment: the infinity option has a larger optical glyph, and Custom context text is centered for both placeholder and entered values.
  • Normalized supported pasted ollama pull <model> and ollama run <model> inputs locally without shell execution, rejecting incomplete or unsupported command forms.
  • Reworked pull handling around the streaming curl response: readable status labels, transferred size/rate/ETA feedback, explicit cancellation, bounded post-pull tag reconciliation, and a transient completion result.
  • Fixed delete-confirmation lifetime so normal model refreshes no longer close it early. The model action row now uses wider spacing and moves above the confirmation panel, preventing visual overlap.

Targeted regression coverage was added for pull integration and panel wiring. The current targeted suites pass:

  • tests/test-ollama-wiring.sh
  • tests/test-ollama-pull-integration.sh

@HANCORE-linux

HANCORE-linux commented Jul 13, 2026

Copy link
Copy Markdown
Owner

Thanks for the substantial follow-up work. The optional/default-off contract, API-only model operations, bar-order migration, and G16/control-panel integration are well structured. The current PR also merges cleanly with current main, and the existing Ollama, BarOrder, pull, shell-updater, theme-updater, and Arch-updater suites pass.

I completed a review of all 20 changed files. I am not ready to merge the PR yet. Please address the following items and include the requested performance evidence.

Required correctness fixes

1. Restore correct global tooltip placement

TooltipOverlay.qml now centers every tooltip around tooltipY. TooltipMixin.qml supplies the owner's local scene coordinate, not an absolute screen coordinate. This regresses existing bar tooltips: top-bar tooltips overlap the bar, and bottom-bar tooltips can be placed near the top of the screen.

The current wiring test asserts the new formula but does not validate geometry, so it preserves the regression.

Please retain the existing bar-position-aware placement for bar-origin tooltips and add a separate placement mode/anchor path for panel-origin tooltips. Verify top bar, bottom bar, and Ollama panel tooltips on more than one screen position.

2. Make pull reconciliation match the observed Ollama lifecycle

The current reconciliation limit is eight attempts at one-second intervals. Our real qwen2.5-coder:7b test already demonstrated that Ollama can report stream success and register the model in /api/tags roughly two minutes later. The new delayed fixture exposes the model after only three tag requests, so it does not cover the reproduced failure.

Please use a documented time-based deadline/backoff that covers the observed finalization period. Keep the state at Finalizing... until the exact requested model appears. Add a test where registration occurs beyond the current eight-second limit, preferably using injectable timing so the suite need not run for minutes.

During reconciliation the download has already completed. The current Cancel action only stops local verification and can leave a model that appears later while the UI reports Pull cancelled. Hide the cancel action after streaming completes, or explicitly represent it as stopping local waiting and retain a later refresh/reconciliation path.

No badge or desktop notification is required for merge. Either may be added as an optional UX enhancement for pulls completed while the panel is closed. The required behavior is that the model list updates automatically and failures remain visible when the panel is reopened.

3. Validate /api/tags structurally

parseTags() accepts {"models":{}} as an empty model list. I verified this with an isolated QML counter-test: it returned accepted=true length=0. applyTags() can therefore clear the installed-model list and mark tagsConnected=true for a structurally invalid response.

Require models to be an array and require every entry to have a non-empty model name, matching the defensive behavior already used by parseLoaded(). An invalid response must preserve the previous list, set the tags connection state to false, and expose an error. Add wrong-schema and missing-name tests, not only invalid-JSON coverage.

Maintainability and payload cleanup

  • tests/OllamaPullIntegrationSmoke.qml and versions/V1/OllamaPullIntegrationSmoke.qml are byte-identical. Only the copy under versions/V1 is referenced.
  • OllamaDataSmoke.qml and OllamaPullIntegrationSmoke.qml are test-only ShellRoots inside the production payload, so every user receives them through install/self-update.
  • Keep canonical smoke fixtures under tests/, point the runners there, and remove test-only roots from versions/V1.
  • The total PR size is not itself a blocker: about one third is useful test coverage. However, OllamaPanel.qml and OllamaData.qml each approach 1,000 lines and combine unrelated responsibilities. Please split the panel into focused sections and separate GPU sampling and pull/model-operation state from the shared data service. The objective is clear ownership and reduced regression blast radius, not an arbitrary line-count target.

Polling and GPU behavior

With Ollama enabled, the current implementation polls version, loaded models, and GPU every 15 seconds while the panel is closed; loaded models and GPU switch to two seconds while open.

Please revise this contract:

  • Version and loaded-model API polling are not needed while the panel is closed. Refresh on panel open, after operations, IPC/manual refresh, and other explicit lifecycle events.
  • Detect the GPU provider once per relevant lifecycle instead of probing every backend on every tick.
  • Prefer readable Linux sysfs utilization sources where available. Kernel gpu_busy_percent is an AMDGPU interface, not a universal GPU interface.
  • Use NVIDIA's supported utilization source only for an NVIDIA device. Avoid spawning a fresh nvidia-smi process every two seconds; use a persistent sampler or otherwise demonstrate that the chosen cadence has negligible child-process cost.
  • If neither sysfs nor a working NVIDIA source exists, show N/A, stop repeated failed probes, and allow an explicit refresh/panel reopen to retry provider detection.

On the review system, an RTX 2080 exposes no readable gpu_busy_percent source and the installed nvidia-smi currently cannot communicate with the driver. The present implementation therefore produces N/A while continuing to spawn the shell probe.

Pull progress and remaining UI checks

  • Pull byte/rate/ETA state resets per digest/layer, but the UI presents it like total model progress. Either aggregate known layers or label it clearly as Current layer and add a multi-digest test.
  • The reported delete hover contrast still uses the same fillPrimaryHover/seal pairing and needs a visual check across representative themes.
  • The Configuration heading remains left anchored. Please align it consistently with the intended control-center treatment.
  • Update the PR description: its test count and former progress-file description no longer match the implementation.

Required performance evidence

Please provide an A/B report against current main, using the same machine, monitor/workspace state, stable bar process, and no active model pull:

  1. Current main, 120 seconds.
  2. PR with modOllama=false, 120 seconds.
  3. PR with Ollama enabled and panel closed, 120 seconds.
  4. PR with Ollama enabled and panel open, 60 seconds.

Record for every window:

  • Quickshell own CPU from /proc/<pid>/stat deltas.
  • Quickshell child CPU from cutime+cstime deltas.
  • One-second samples with p50/p95, not a single top snapshot.
  • PSS from /proc/<pid>/smaps_rollup before and after.
  • Counts/source attribution for bash, curl, and nvidia-smi process starts.
  • QSG render/main-thread deltas if available.

Also verify these backend cases through real hardware where available and deterministic fixtures for the missing cases:

  • sysfs source available, no unnecessary nvidia-smi invocation;
  • working NVIDIA source;
  • no supported source, stable N/A without continuing failed process probes;
  • default-off state, no Ollama polling processes and no visible UI change.

Please include the exact commands and raw artifacts so the result is reproducible. Static wiring tests alone are not sufficient for the performance claim.

Once these points are addressed, I can repeat the offline merge, full updater regression matrix, runtime/UI smoke, and performance plausibility check before merge.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants