Skip to content

3.1.2 beta test7#248

Open
leslielazzarino wants to merge 5 commits into
devfrom
3.1.2-beta-test7
Open

3.1.2 beta test7#248
leslielazzarino wants to merge 5 commits into
devfrom
3.1.2-beta-test7

Conversation

@leslielazzarino

@leslielazzarino leslielazzarino commented Jun 29, 2026

Copy link
Copy Markdown

Description:

Please provide a brief description of the changes made in this pull request.

Related Issue:

If this pull request is related to any existing issue, please mention the issue number here.

Changes Made:

Please list down the specific changes made in this pull request.

Testing Done:

Please describe the testing that has been done to ensure the changes made in this pull request are functioning as expected.

Screenshots:

If applicable, please provide screenshots or GIFs or videos to visually demonstrate the changes made.

Checklist:

  • I have tested the changes locally.
  • I have self-reviewed the code changes.
  • I have updated the documentation, if necessary.

Summary by CodeRabbit

  • New Features
    • Added a one-step WebUI setup flow to install dependencies, fetch required resources, ensure Electron, and set up local tooling.
    • Added automatic git pre-commit hook installation during setup (and a re-run option).
    • Introduced an experimental OpenVINO “INT4 KV Cache precision” option that applies on the next model load.
    • Remote chat images are now downscaled to ~1MP before being saved/sent to reduce upload/storage use.
  • Bug Fixes
    • Improved backend service start/stop and reinstall reliability, with stronger Windows shutdown/cleanup handling.
  • Documentation
    • Updated development setup instructions to use the new one-step command.

Signed-off-by: Leslie Lazzarino <leslie.lazzarino@tngtech.com>
Signed-off-by: Leslie Lazzarino <leslie.lazzarino@tngtech.com>
Signed-off-by: Leslie Lazzarino <leslie.lazzarino@tngtech.com>
@coderabbitai

coderabbitai Bot commented Jun 29, 2026

Copy link
Copy Markdown

Review Change Stack

Warning

Review limit reached

@mschuettlerTNG, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 20 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 35fb7b3d-0e51-4cc0-80ea-c8d633aae5cc

📥 Commits

Reviewing files that changed from the base of the PR and between 016a1b5 and 9134a46.

📒 Files selected for processing (1)
  • WebUI/package.json
📝 Walkthrough

Walkthrough

Adds pre-commit tooling for the monorepo, hardens OpenVINO backend install and shutdown behavior, and downscales remote chat image data URIs before persisting them.

Changes

Pre-commit Hook Infrastructure

Layer / File(s) Summary
Pre-commit config and npm wiring
.pre-commit-config.yaml, WebUI/package.json, readme.md
Defines Ruff hooks for Python services and local ESLint/Prettier hooks for WebUI; adds setup and install-hooks npm scripts; updates readme setup instructions to use npm run setup.
ensure-precommit-hook.mjs installer
WebUI/build/scripts/ensure-precommit-hook.mjs
Idempotent Node.js script: checks for git working tree, verifies config presence, skips if hook marker already present, selects pre-commit or uvx runner, runs install, and exits 0 on any non-fatal failure.

OpenVINO Backend Robustness

Layer / File(s) Summary
Version marker persistence and getInstalledVersion
WebUI/electron/subprocesses/openVINOBackendService.ts
Adds versionMarkerPath field and constructor init; writes installed-version.json at end of set_up(); reworks getInstalledVersion() to read the marker first, falling back to ovms --version.
terminateProcessTree and centralized stop
WebUI/electron/subprocesses/openVINOBackendService.ts
Adds terminateProcessTree() with Windows taskkill /T /F fallback; adds stopAllModelServers() for fault-isolated shutdown; updates stop() and all per-server stop methods to use these. set_up() now stops servers before reinstall.
Windows extraction cleanup helpers
WebUI/electron/subprocesses/openVINOBackendService.ts
Adds killStrayOvmsProcessesWindows() and removeDirWithRetry(); extractOvms() uses them to kill lingering ovms.exe and retry directory deletion on transient Windows filesystem errors.

Remote Image Downscaling to 1MP

Layer / File(s) Summary
downscaleDataUriTo1MP utility
WebUI/src/lib/utils.ts
Refactors downscaleImageTo1MP into shared blob-conversion/resize helpers; adds exported downscaleDataUriTo1MP that downscales a base64 data URI to ~1MP with fallback to original.
Apply downscaling in homeAgent
WebUI/src/assets/js/store/homeAgent.ts
Imports downscaleDataUriTo1MP and applies it to each remote image data URI in prepareRemoteFiles before passing to saveImageToMediaInput.

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~50 minutes

Possibly related PRs

  • TNG/AI-Playground#202: Adds the same saveImageToMediaInput/aipg-media:// data-URI persistence path that this PR now feeds with downscaled remote images.
  • TNG/AI-Playground#240: Touches the same Home Agent remote image handling path that now applies downscaleDataUriTo1MP.
  • TNG/AI-Playground#247: Also modifies WebUI/electron/subprocesses/openVINOBackendService.ts and overlaps with OVMS launch/setup behavior.

Suggested reviewers

  • mschuettlerTNG

Poem

🐇 Hop hop, the hooks are set,
The warren’s files are tidy yet.
OVMS sighs and shuts its doors,
Tiny images drift through burrows and chores.
The rabbit hums, “All snug, all bright—
1MP dreams in the moonlit night.”

🚥 Pre-merge checks | ✅ 2 | ❌ 3

❌ Failed checks (3 warnings)

Check name Status Explanation Resolution
Title check ⚠️ Warning The title is just a version/branch label and does not describe the OpenVINO KV cache changes in this PR. Rename it to a concise summary of the main change, such as adding OpenVINO INT4 KV cache support.
Description check ⚠️ Warning The description is only the template prompts and does not contain the required summary, issue reference, changes, testing, or checklist details. Fill in each template section with the actual PR details, especially changes made and testing done, and mark the checklist items as applicable.
Docstring Coverage ⚠️ Warning Docstring coverage is 70.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch 3.1.2-beta-test7

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 6

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
WebUI/src/assets/js/store/homeAgent.ts (1)

1128-1138: 🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Use the post-downscale MIME/ext here, not the inbound one.

downscaleDataUriTo1MP() can re-encode non-JPEG/WebP images as PNG, but this code still stamps the FileUIPart with img.mime and derives the filename extension from the original type. That means the persisted bytes and advertised metadata can diverge for formats like GIF/BMP/AVIF. Parse the MIME from downscaledUri (or have the helper return it) before setting mediaType/filename.

Suggested fix
         try {
           const dataUri = `data:${img.mime};base64,${img.data_base64}`
           // Match the regular chat path: cap images at 1MP before persisting so
           // oversized remote images aren't passed full-resolution to the model.
           const downscaledUri = await downscaleDataUriTo1MP(dataUri)
+          const mediaType = downscaledUri.match(/^data:([^;,]+)[;,]/)?.[1] ?? img.mime
           const aipgUrl = await saveImageToMediaInput(downscaledUri)
-          const ext = img.mime === 'image/jpeg' ? 'jpg' : img.mime.replace('image/', '')
+          const ext = mediaType === 'image/jpeg' ? 'jpg' : mediaType.replace('image/', '')
           parts.push({
             type: 'file',
-            mediaType: img.mime,
+            mediaType,
             url: aipgUrl,
             filename: `${sourceLabel}-${Date.now()}-${i}.${ext}`,
           })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/src/assets/js/store/homeAgent.ts` around lines 1128 - 1138, The
FileUIPart metadata in homeAgent’s image persistence path is using the inbound
img.mime and derived extension even after downscaleDataUriTo1MP() may re-encode
the bytes to a different format. Update the logic in this image handling block
to derive the final MIME type and filename extension from downscaledUri (or have
downscaleDataUriTo1MP return the post-conversion MIME), then use that value for
mediaType and filename so the stored metadata matches the persisted content.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In @.pre-commit-config.yaml:
- Around line 29-36: The WebUI pre-commit hooks are still invoking bash via the
lint and prettier entries, which breaks environments without Bash. Update the
hook definitions to use npm’s prefix-based invocation instead of the current
bash -c wrapper in the lint:eslint and format entries so WebUI runs directly
from the repo root and remains Windows-compatible.

In `@WebUI/build/scripts/ensure-precommit-hook.mjs`:
- Around line 71-83: The hook selection logic in ensure-precommit-hook.mjs
currently checks only global pre-commit and uvx, which can skip installation on
a fresh clone. Update the runner resolution in the block that sets runner to
first use the repo-managed build/resources/uv.exe (the uv downloaded by setup),
then fall back to PATH-based pre-commit and uvx. Keep the existing
install-failure messaging path unchanged if none of the runners are available.

In `@WebUI/electron/subprocesses/openVINOBackendService.ts`:
- Around line 1563-1567: The Windows cleanup in killStrayOvmsProcessesWindows is
too broad because taskkill /F /IM ovms.exe /T kills every OVMS process on the
machine. Update this method to target only processes tied to this instance by
locating and terminating PIDs or executables under this.ovmsDir, and keep the
cleanup scoped to the current installation. If that is not feasible, remove the
blanket kill and rely on removeDirWithRetry() to report the lock failure
instead.
- Around line 136-139: The version marker lifecycle in openVINOBackendService is
currently anchored to serviceDir, which can leave installed-version.json behind
after ovmsDir is deleted during reinstall. Update the version marker handling in
the setup/reinstall flow and getInstalledVersion() so the marker is tied to the
extracted OVMS directory instead of a persistent serviceDir path, and ensure
cleanup/removal happens whenever ovmsDir is removed so stale releaseTag/version
data cannot be returned through IPC.
- Around line 1701-1706: `terminateProcessTree()` is exiting after the direct
PID terminates, which can bypass the Windows `taskkill /T /F` cleanup and leave
child OVMS processes running. Update the `terminateProcessTree` flow to continue
through the Windows tree-teardown path even after `proc.kill('SIGTERM')`
succeeds, so `taskkill` still runs when needed. Also, `stopAllModelServers()`
currently only warns on failures and hides them; change it to collect and
propagate stop errors so `stop()` can fail or report an unsuccessful stop
instead of always returning `stopped`, using the existing `stoppers`,
`stopAllModelServers`, and `stop()` entry points.

In `@WebUI/src/lib/utils.ts`:
- Around line 378-390: The fallback branches in the canvas downscale flow are
logging directly with console.error, which bypasses the shared WebUI error
pipeline. Update the affected logic in utils.ts around the canvas context and
canvas.toBlob failure paths to either throw/return a typed silent error to the
caller or report through the existing errors store via useErrors/errors.report.
Keep the existing helper behavior but remove bare console.error calls so these
failures are routed consistently through the shared error handling path.

---

Outside diff comments:
In `@WebUI/src/assets/js/store/homeAgent.ts`:
- Around line 1128-1138: The FileUIPart metadata in homeAgent’s image
persistence path is using the inbound img.mime and derived extension even after
downscaleDataUriTo1MP() may re-encode the bytes to a different format. Update
the logic in this image handling block to derive the final MIME type and
filename extension from downscaledUri (or have downscaleDataUriTo1MP return the
post-conversion MIME), then use that value for mediaType and filename so the
stored metadata matches the persisted content.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 6858146f-0c0c-4778-b54f-dba92f2cf103

📥 Commits

Reviewing files that changed from the base of the PR and between cc59d5d and ae19dad.

📒 Files selected for processing (7)
  • .pre-commit-config.yaml
  • WebUI/build/scripts/ensure-precommit-hook.mjs
  • WebUI/electron/subprocesses/openVINOBackendService.ts
  • WebUI/package.json
  • WebUI/src/assets/js/store/homeAgent.ts
  • WebUI/src/lib/utils.ts
  • readme.md

Comment thread .pre-commit-config.yaml
Comment on lines +29 to +36
entry: bash -c 'cd WebUI && npm run lint:eslint'
files: ^WebUI/.*\.(ts|tsx|mts|cts|js|jsx|mjs|cjs|vue)$
pass_filenames: false
# Formatter (Prettier)
- id: prettier
name: prettier (WebUI)
language: system
entry: bash -c 'cd WebUI && npm run format'

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Inspect the relevant config and package scripts.
git ls-files .pre-commit-config.yaml WebUI/package.json README.md readme.md | sed 's#^`#FILE` #'
printf '\n--- .pre-commit-config.yaml ---\n'
cat -n .pre-commit-config.yaml | sed -n '20,45p'
printf '\n--- WebUI/package.json ---\n'
cat -n WebUI/package.json | sed -n '1,220p'
printf '\n--- README/readme mentions Windows ---\n'
rg -n -i 'windows' README.md readme.md || true

Repository: TNG/AI-Playground

Length of output: 7686


Make the WebUI hooks shell-free. bash -c adds an unnecessary Git Bash dependency here; npm --prefix WebUI run lint:eslint and npm --prefix WebUI run format avoid breaking Windows setups that don’t have bash on PATH.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In @.pre-commit-config.yaml around lines 29 - 36, The WebUI pre-commit hooks are
still invoking bash via the lint and prettier entries, which breaks environments
without Bash. Update the hook definitions to use npm’s prefix-based invocation
instead of the current bash -c wrapper in the lint:eslint and format entries so
WebUI runs directly from the repo root and remains Windows-compatible.

Comment on lines +71 to +83
// Prefer a `pre-commit` on PATH; otherwise run it through uv's `uvx`.
let runner
if (commandAvailable('pre-commit')) {
runner = ['pre-commit']
} else if (commandAvailable('uvx')) {
runner = ['uvx', 'pre-commit']
} else {
log('`pre-commit` is not installed and `uvx` is unavailable, so the hook was not installed.')
log('Install it once with either of:')
log(' uv tool install pre-commit')
log(' pipx install pre-commit')
log('then re-run `npm run setup` (or `pre-commit install`).')
process.exit(0)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

git ls-files WebUI/build/scripts/ensure-precommit-hook.mjs WebUI/package.json WebUI -co --exclude-standard | sed -n '1,200p'

Repository: TNG/AI-Playground

Length of output: 7936


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== ensure-precommit-hook.mjs ==\n'
cat -n WebUI/build/scripts/ensure-precommit-hook.mjs | sed -n '1,220p'

printf '\n== package.json snippets ==\n'
cat -n WebUI/package.json | sed -n '1,260p'

Repository: TNG/AI-Playground

Length of output: 9602


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== ensure-precommit-hook.mjs (1-240) ==\n'
sed -n '1,240p' WebUI/build/scripts/ensure-precommit-hook.mjs

printf '\n== package.json relevant scripts ==\n'
python3 - <<'PY'
import json
from pathlib import Path
p = Path("WebUI/package.json")
data = json.loads(p.read_text())
for k, v in data.get("scripts", {}).items():
    if any(tok in k.lower() for tok in ["setup", "hook", "uv", "precommit", "fetch", "install"]):
        print(f"{k}: {v}")
PY

printf '\n== search for uv/pre-commit installation wiring ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' '\buvx?\b|pre-commit|fetch-external-resources|install-hooks|toolchain' WebUI -g '!WebUI/build/scripts/ensure-precommit-hook.mjs'

Repository: TNG/AI-Playground

Length of output: 14736


🏁 Script executed:

#!/bin/bash
set -euo pipefail

find WebUI -maxdepth 4 \( -name '*uv*' -o -name '*precommit*' -o -name '*hook*' \) | sed -n '1,200p'

Repository: TNG/AI-Playground

Length of output: 295


🏁 Script executed:

#!/bin/bash
set -euo pipefail

python3 - <<'PY'
import json, pathlib
pkg = json.loads(pathlib.Path("WebUI/package.json").read_text())
print("scripts:")
for k in sorted(pkg.get("scripts", {})):
    if any(s in k.lower() for s in ["setup", "hook", "fetch", "uv", "install"]):
        print(f"  {k}: {pkg['scripts'][k]}")
PY

printf '\n== files mentioning fetch-external-resources or install-hooks ==\n'
rg -n --hidden --glob '!**/node_modules/**' --glob '!**/dist/**' --glob '!**/build/**' 'fetch-external-resources|install-hooks|ensure-precommit-hook|uvx|pre-commit' WebUI

Repository: TNG/AI-Playground

Length of output: 1710


🏁 Script executed:

#!/bin/bash
set -euo pipefail

# Narrowly inspect the installer and setup path
sed -n '1,220p' WebUI/build/scripts/ensure-precommit-hook.mjs
printf '\n----\n'
sed -n '1,220p' WebUI/package.json

Repository: TNG/AI-Playground

Length of output: 7992


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch-external-resources.mts ==\n'
sed -n '1,260p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== uv.ts (top section) ==\n'
sed -n '1,120p' WebUI/electron/subprocesses/uvBasedBackends/uv.ts

printf '\n== aipgRoot.ts (resource names section) ==\n'
sed -n '1,80p' WebUI/electron/aipgRoot.ts

Repository: TNG/AI-Playground

Length of output: 15762


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch-external-resources.mts ==\n'
sed -n '1,260p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== build-paths.mts ==\n'
sed -n '1,260p' WebUI/build/scripts/build-paths.mts

printf '\n== uv.ts top ==\n'
sed -n '1,80p' WebUI/electron/subprocesses/uvBasedBackends/uv.ts

printf '\n== aipgRoot.ts top ==\n'
sed -n '1,80p' WebUI/electron/aipgRoot.ts

Repository: TNG/AI-Playground

Length of output: 19937


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== uv resource handling in fetch-external-resources.mts ==\n'
rg -n -A4 -B4 'uvx|uv\.exe|resource.*uv|renameSync|extractAllTo|chmodSync|mv |cpSync|copy' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== PATH wiring / environment setup ==\n'
rg -n -A3 -B3 'PATH=|process\.env\.PATH|append.*PATH|prepend.*PATH|build/resources|uvx\.exe|uv\.exe' WebUI

Repository: TNG/AI-Playground

Length of output: 14491


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch script around the UV resource section ==\n'
sed -n '240,420p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== any PATH edits involving build/resources ==\n'
rg -n -A3 -B3 'process\.env\.PATH|PATH[^\n]*build/resources|build/resources[^\n]*PATH|prepend.*PATH|append.*PATH' WebUI

Repository: TNG/AI-Playground

Length of output: 11066


🏁 Script executed:

#!/bin/bash
set -euo pipefail

printf '\n== fetch-external-resources.mts (UV section) ==\n'
sed -n '260,420p' WebUI/build/scripts/fetch-external-resources.mts

printf '\n== search for PATH wiring in setup/install scripts ==\n'
rg -n -A3 -B3 'PATH|build/resources|uvx|uv\.exe|ensure-electron|install-hooks' WebUI/build WebUI/electron WebUI/package.json

Repository: TNG/AI-Playground

Length of output: 47137


Use the repo-managed uv.exe before the PATH-only fallback. Right now setup downloads uv, but this block still only probes global pre-commit/uvx, so hook installation is skipped on a fresh clone unless those tools are already installed. Resolve build/resources/uv.exe first, then fall back to PATH.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/build/scripts/ensure-precommit-hook.mjs` around lines 71 - 83, The hook
selection logic in ensure-precommit-hook.mjs currently checks only global
pre-commit and uvx, which can skip installation on a fresh clone. Update the
runner resolution in the block that sets runner to first use the repo-managed
build/resources/uv.exe (the uv downloaded by setup), then fall back to
PATH-based pre-commit and uvx. Keep the existing install-failure messaging path
unchanged if none of the runners are available.

Comment on lines +136 to +139
// Records exactly which version/releaseTag was installed. Written at the end of
// set_up(), read by getInstalledVersion(). This is the only reliable source of
// the installed releaseTag (the `ovms --version` output does not report it).
this.versionMarkerPath = path.resolve(path.join(this.serviceDir, 'installed-version.json'))

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Tie the version marker lifecycle to the extracted OVMS directory.

The marker lives in serviceDir, but reinstall deletes only ovmsDir. If an update fails after the old ovmsDir is removed, the stale marker can still make getInstalledVersion() report the previous version/releaseTag through the downstream IPC path.

Suggested fix
-    this.versionMarkerPath = path.resolve(path.join(this.serviceDir, 'installed-version.json'))
+    this.versionMarkerPath = path.resolve(path.join(this.ovmsDir, 'installed-version.json'))

Also applies to: 1611-1617

🧰 Tools
🪛 ast-grep (0.44.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { ChildProcess, spawn } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/electron/subprocesses/openVINOBackendService.ts` around lines 136 -
139, The version marker lifecycle in openVINOBackendService is currently
anchored to serviceDir, which can leave installed-version.json behind after
ovmsDir is deleted during reinstall. Update the version marker handling in the
setup/reinstall flow and getInstalledVersion() so the marker is tied to the
extracted OVMS directory instead of a persistent serviceDir path, and ensure
cleanup/removal happens whenever ovmsDir is removed so stale releaseTag/version
data cannot be returned through IPC.

Comment on lines +1563 to +1567
private async killStrayOvmsProcessesWindows(): Promise<void> {
if (process.platform !== 'win32') return
try {
await execAsync('taskkill /F /IM ovms.exe /T')
this.appLogger.info('Killed stray ovms.exe processes before extraction', this.name)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

Avoid killing unrelated ovms.exe processes system-wide.

taskkill /F /IM ovms.exe /T terminates every OVMS instance on the machine, including user-managed or another app’s model server. Scope cleanup to PIDs/executables under this.ovmsDir, or let removeDirWithRetry() surface the lock error instead of using a blanket image-name kill.

🧰 Tools
🪛 ast-grep (0.44.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { ChildProcess, spawn } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/electron/subprocesses/openVINOBackendService.ts` around lines 1563 -
1567, The Windows cleanup in killStrayOvmsProcessesWindows is too broad because
taskkill /F /IM ovms.exe /T kills every OVMS process on the machine. Update this
method to target only processes tied to this instance by locating and
terminating PIDs or executables under this.ovmsDir, and keep the cleanup scoped
to the current installation. If that is not feasible, remove the blanket kill
and rely on removeDirWithRetry() to report the lock failure instead.

Comment on lines +1701 to +1706
for (const [label, stopFn] of stoppers) {
try {
await stopFn()
} catch (e) {
this.appLogger.warn(`Failed to stop OVMS ${label} server: ${e}`, this.name)
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🩺 Stability & Availability | 🟠 Major | ⚡ Quick win

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='WebUI/electron/subprocesses/openVINOBackendService.ts'

echo '== file size =='
wc -l "$file"

echo
echo '== relevant excerpts around stop helpers =='
sed -n '2060,2165p' "$file"

echo
echo '== search for stopAllModelServers and related stop methods =='
rg -n 'stopAllModelServers|waitForExit|taskkill|SIGTERM|SIGKILL|stoppers|not confirmed exited|Force killing OVMS|stopped' "$file"

echo
echo '== outline =='
ast-grep outline "$file" --view expanded

Repository: TNG/AI-Playground

Length of output: 13556


🏁 Script executed:

#!/bin/bash
set -euo pipefail

file='WebUI/electron/subprocesses/openVINOBackendService.ts'

echo '== stopAllModelServers and stop() =='
sed -n '1688,1730p' "$file"

echo
echo '== stop methods for individual OVMS servers =='
sed -n '2136,2595p' "$file" | sed -n '1,470p'

Repository: TNG/AI-Playground

Length of output: 16658


Keep the Windows tree teardown on the taskkill path, and surface stop failures.
WebUI/electron/subprocesses/openVINOBackendService.ts:1693-1720, 2100-2132

  • terminateProcessTree() returns as soon as proc.kill('SIGTERM') ends the direct PID, which can skip taskkill /T /F on Windows and leave ovms.exe workers alive.
  • stopAllModelServers() swallows stop errors, so stop() can still report stopped even if a server did not fully terminate.
🧰 Tools
🪛 ast-grep (0.44.0)

[warning] Importing child_process exposes a command-execution surface; ensure any command/argument built from input is validated, and prefer execFile/spawn with an argument array over exec.
Context: import { ChildProcess, spawn } from 'node:child_process'
Note: [CWE-78] Improper Neutralization of Special Elements used in an OS Command ('OS Command Injection').

(detect-child-process-typescript)

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/electron/subprocesses/openVINOBackendService.ts` around lines 1701 -
1706, `terminateProcessTree()` is exiting after the direct PID terminates, which
can bypass the Windows `taskkill /T /F` cleanup and leave child OVMS processes
running. Update the `terminateProcessTree` flow to continue through the Windows
tree-teardown path even after `proc.kill('SIGTERM')` succeeds, so `taskkill`
still runs when needed. Also, `stopAllModelServers()` currently only warns on
failures and hides them; change it to collect and propagate stop errors so
`stop()` can fail or report an unsuccessful stop instead of always returning
`stopped`, using the existing `stoppers`, `stopAllModelServers`, and `stop()`
entry points.

Comment thread WebUI/src/lib/utils.ts
Comment on lines +378 to +390
if (!ctx) {
console.error('Failed to get canvas context, using original image')
resolve(null)
return
}

// If image is already <= 1MP, return original file
if (currentPixels <= MAX_PIXELS) {
resolve(file)
return
}
ctx.drawImage(img, 0, 0, newWidth, newHeight)

// Calculate new dimensions maintaining aspect ratio
const scale = Math.sqrt(MAX_PIXELS / currentPixels)
const newWidth = Math.round(img.width * scale)
const newHeight = Math.round(img.height * scale)
const { mimeType, quality } = resolveDownscaleEncoding(sourceType)
canvas.toBlob(
(blob) => {
if (!blob) {
console.error('Failed to create blob from canvas, using original image')

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

📐 Maintainability & Code Quality | 🟠 Major | ⚡ Quick win

Route these failures through the shared error pipeline.

These new fallback branches add bare console.error() calls under WebUI/src, which bypasses the required useErrors/errors.report(...) flow. Please bubble a typed silent error to the caller or inject/report via the errors store instead of logging directly. As per coding guidelines, "All errors must flow through the errors store (useErrors). Report errors via errors.report(err, overrides?) — never use toast.error() directly or rely on bare console.error()."

Also applies to: 451-458

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/src/lib/utils.ts` around lines 378 - 390, The fallback branches in the
canvas downscale flow are logging directly with console.error, which bypasses
the shared WebUI error pipeline. Update the affected logic in utils.ts around
the canvas context and canvas.toBlob failure paths to either throw/return a
typed silent error to the caller or report through the existing errors store via
useErrors/errors.report. Keep the existing helper behavior but remove bare
console.error calls so these failures are routed consistently through the shared
error handling path.

Source: Coding guidelines

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 2

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
WebUI/electron/subprocesses/openVINOBackendService.ts (1)

1281-1284: 🔒 Security & Privacy | 🟠 Major | ⚡ Quick win

Use execFile for the version probe. this.ovmsExePath is passed through a shell here, so install paths with shell metacharacters can change the command. execFile(this.ovmsExePath, ['--version'], { timeout: 5000, env: ... }) avoids shell parsing.

Proposed direction
-      const result = await execAsync(`"${this.ovmsExePath}" --version`, {
+      const result = await execFileAsync(this.ovmsExePath, ['--version'], {
         timeout: 5000,
         env: this.buildOvmsEnv(extraLibPaths),
       })
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/electron/subprocesses/openVINOBackendService.ts` around lines 1281 -
1284, The version probe in the OVMS startup path is invoking `this.ovmsExePath`
through a shell via `execAsync`, which can mis-handle install paths containing
shell metacharacters. Update the probe in `openVINOBackendService.ts` to use
`execFile` with `this.ovmsExePath` and a `['--version']` argument array, keeping
the existing timeout and `this.buildOvmsEnv(extraLibPaths)` env options
unchanged.

Source: Linters/SAST tools

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@WebUI/src/assets/js/store/backendServices.ts`:
- Around line 419-434: The KV-cache sync watcher in backendServices.ts is
swallowing `updateServiceSettings()` failures with only a local console warning,
leaving renderer state and OVMS settings out of sync. Update the
`watch(openvinoKvCacheU4, ...)` callback to route the caught error through
`useErrors().report(...)` instead of `console.warn`, using an appropriate
warn/silent override for the failed OpenVINO KV cache precision sync. Keep the
existing `updateServiceSettings` call and identify the change near the
`openvinoKvCacheU4` watcher and `effectiveOvmsKvCachePrecision` usage.

In `@WebUI/src/components/BackendOptions.vue`:
- Around line 483-488: The new OpenVINO KV cache toggle is being saved in
BackendOptions.vue, but it is not included in the override bookkeeping used by
hasUserOverride and clearOverride. Update the OpenVINO override detection/reset
logic in those paths to treat backendServices.openvinoKvCacheU4 like the other
OpenVINO settings, so enabling it alone shows Clear Override and clearing
overrides also resets this toggle. Refer to the BackendOptions.vue methods/logic
that handle hasUserOverride and clearOverride.

---

Outside diff comments:
In `@WebUI/electron/subprocesses/openVINOBackendService.ts`:
- Around line 1281-1284: The version probe in the OVMS startup path is invoking
`this.ovmsExePath` through a shell via `execAsync`, which can mis-handle install
paths containing shell metacharacters. Update the probe in
`openVINOBackendService.ts` to use `execFile` with `this.ovmsExePath` and a
`['--version']` argument array, keeping the existing timeout and
`this.buildOvmsEnv(extraLibPaths)` env options unchanged.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: defaults

Review profile: CHILL

Plan: Pro

Run ID: 62d0a8f3-184c-4979-8306-2de02313b146

📥 Commits

Reviewing files that changed from the base of the PR and between ae19dad and 016a1b5.

📒 Files selected for processing (5)
  • WebUI/electron/subprocesses/openVINOBackendService.ts
  • WebUI/src/assets/i18n/en-US.json
  • WebUI/src/assets/js/store/backendServices.ts
  • WebUI/src/components/BackendOptions.vue
  • WebUI/src/env.d.ts
✅ Files skipped from review due to trivial changes (1)
  • WebUI/src/env.d.ts

Comment on lines +419 to +434
/** Sync the OpenVINO KV cache precision toggle to the main process so a
* running OVMS service reloads its chat server with the new precision. */
watch(
openvinoKvCacheU4,
async () => {
try {
await updateServiceSettings({
serviceName: 'openvino-backend',
ovmsKvCachePrecision: effectiveOvmsKvCachePrecision.value,
})
} catch (e) {
console.warn('Failed to sync OpenVINO KV cache precision to main process:', e)
}
},
{ flush: 'post' },
)

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🗄️ Data Integrity & Integration | 🟠 Major | ⚡ Quick win

Report KV-cache sync failures through the errors store.

If updateServiceSettings() fails here, the renderer still keeps openvinoKvCacheU4 enabled and persisted, but the main process never receives the new ovmsKvCachePrecision. That leaves the UI and OVMS runtime out of sync with only a console.warn for diagnostics. Please route this through useErrors().report(...) (likely as a warn/silent app error) instead of swallowing it locally.

As per coding guidelines, "All errors must flow through the errors store (useErrors). Report errors via errors.report(err, overrides?) — never use toast.error() directly or rely on bare console.error()"

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/src/assets/js/store/backendServices.ts` around lines 419 - 434, The
KV-cache sync watcher in backendServices.ts is swallowing
`updateServiceSettings()` failures with only a local console warning, leaving
renderer state and OVMS settings out of sync. Update the
`watch(openvinoKvCacheU4, ...)` callback to route the caught error through
`useErrors().report(...)` instead of `console.warn`, using an appropriate
warn/silent override for the failed OpenVINO KV cache precision sync. Keep the
existing `updateServiceSettings` call and identify the change near the
`openvinoKvCacheU4` watcher and `effectiveOvmsKvCachePrecision` usage.

Source: Coding guidelines

Comment on lines +483 to +488
// Save OpenVINO KV cache precision toggle (not part of version override)
if (props.backend === 'openvino-backend' && 'ovmsKvCacheU4' in values) {
backendServices.openvinoKvCacheU4 = !!(values as { ovmsKvCacheU4?: boolean })
.ovmsKvCacheU4
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win

Include the new OpenVINO toggle in the reset path.

This persists openvinoKvCacheU4, but hasUserOverride and clearOverride still ignore it. After enabling the checkbox, "Clear Override" will not appear for that change alone, and it will not reset the toggle when clearing other OpenVINO overrides.

Suggested follow-up
 const hasUserOverride = computed(
   () =>
     !!backendServices.versionState[props.backend].uiOverride ||
     (props.backend === 'comfyui-backend' && backendServices.comfyUiParameters !== null) ||
-    (props.backend === 'llamacpp-backend' && backendServices.llamaCppParameters !== null),
+    (props.backend === 'llamacpp-backend' && backendServices.llamaCppParameters !== null) ||
+    (props.backend === 'openvino-backend' && backendServices.openvinoKvCacheU4),
 )

 const clearOverride = () => {
   backendServices.versionState[props.backend].uiOverride = undefined
   if (props.backend === 'comfyui-backend') {
     backendServices.comfyUiParameters = null
   }
   if (props.backend === 'llamacpp-backend') {
     backendServices.llamaCppParameters = null
   }
+  if (props.backend === 'openvino-backend') {
+    backendServices.openvinoKvCacheU4 = false
+  }
   settingsDialogOpen.value = false
   menuOpen.value = false
 }
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@WebUI/src/components/BackendOptions.vue` around lines 483 - 488, The new
OpenVINO KV cache toggle is being saved in BackendOptions.vue, but it is not
included in the override bookkeeping used by hasUserOverride and clearOverride.
Update the OpenVINO override detection/reset logic in those paths to treat
backendServices.openvinoKvCacheU4 like the other OpenVINO settings, so enabling
it alone shows Clear Override and clearing overrides also resets this toggle.
Refer to the BackendOptions.vue methods/logic that handle hasUserOverride and
clearOverride.

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