V3.1.2 beta.rc4#253
Conversation
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>
- OVMS pinned to weekly build 2026.3.0.8022ddae3 (releaseTag 39717a69b → 8022ddae3). - Added a sync helper to read a backend's version/releaseTag from the shipped backend-versions.json. - Removed OVMS's hardcoded version/releaseTag defaults, sourcing them from the JSON instead, and added a guard against an unset version during download. - Fixed package author name/email (Intel® Corporation/TODO → Intel Corporation/webadmin@linux.intel.com). - Lowered the Phison preset contextSize from 64000 to 8192. 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>
Signed-off-by: Leslie Lazzarino <leslie.lazzarino@tngtech.com>
📝 WalkthroughWalkthroughThis PR adds pre-commit tooling (config, hook installer script, npm scripts), hardens backend service reinstall/uninstall with permission restoration and process-tree termination for ComfyUI, LlamaCPP, and OpenVINO, introduces an OpenVINO INT4 KV cache precision setting with version marker persistence, adds a live chat reasoning streaming indicator, adds remote image downscaling before persistence, and updates a preset context size. ChangesDeveloper Tooling and Pre-commit Setup
Backend Service Reinstall and Process Termination Hardening
OpenVINO KV Cache Precision Setting
Chat Reasoning Streaming Indicator
Remote Image Downscaling
Misc
Estimated code review effort: 4 (Complex) | ~60 minutes Sequence Diagram(s)sequenceDiagram
participant User
participant BackendOptions as BackendOptions.vue
participant BackendServicesStore as backendServices.ts store
participant OpenVINOService as OpenVINOBackendService
participant OVMS as OVMS process
User->>BackendOptions: toggle INT4 KV cache checkbox
BackendOptions->>BackendServicesStore: set openvinoKvCacheU4
BackendServicesStore->>BackendServicesStore: compute effectiveOvmsKvCachePrecision
BackendServicesStore->>OpenVINOService: updateServiceSettings(ovmsKvCachePrecision)
OpenVINOService->>OpenVINOService: stop chat sub-servers if changed
User->>BackendServicesStore: startService(openvino-backend)
BackendServicesStore->>OpenVINOService: updateServiceSettings before start
OpenVINOService->>OVMS: launch with --kv_cache_precision arg
sequenceDiagram
participant ChatView as Chat.vue
participant ChatStore as openAiCompatibleChat.ts
participant ReasoningDisplay as ChatReasoningDisplay.vue
ChatStore->>ChatStore: onChunk reasoning-delta -> reasoningInProgress=true
ChatStore->>ChatStore: set reasoningStartedAt on new block
ChatView->>ChatView: isReasoningStreaming(message, partIndex)
ChatView->>ReasoningDisplay: pass streaming, liveStartedAt
ReasoningDisplay->>ReasoningDisplay: tick interval, update elapsed time
ChatStore->>ChatStore: onChunk text/tool -> reasoningInProgress=false
ChatStore->>ChatStore: onFinish -> reasoningInProgress=false
ChatView->>ReasoningDisplay: streaming=false, show final elapsed time
Possibly related PRs
Suggested reviewers: 🚥 Pre-merge checks | ✅ 2 | ❌ 3❌ Failed checks (2 warnings, 1 inconclusive)
✅ Passed checks (2 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
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. Comment |
There was a problem hiding this comment.
Actionable comments posted: 4
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)
1119-1139: 🗄️ Data Integrity & Integration | 🟡 Minor | ⚡ Quick win
mediaType/extension can mismatch the actually-persisted (downscaled) image bytes.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) thenext/mediaTypeon the resultingFileUIPartare still derived from the pre-downscaleimg.mime(Line 1133, 1136).downscaleDataUriTo1MPre-encodes non-jpeg/webp source images (e.g. gif, bmp, tiff) as PNG, so when downscaling actually triggers on such a source, the persisted file's real bytes/mime no longer match the reportedmediaType/filename extension.Derive the extension/mediaType from the downscaled data URI's own mime header instead of the original
img.mime.🛠️ Proposed fix
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 aipgUrl = await saveImageToMediaInput(downscaledUri) - const ext = img.mime === 'image/jpeg' ? 'jpg' : img.mime.replace('image/', '') + const finalMime = downscaledUri.match(/^data:([^;,]+)[;,]/)?.[1] ?? img.mime + const ext = finalMime === 'image/jpeg' ? 'jpg' : finalMime.replace('image/', '') parts.push({ type: 'file', - mediaType: img.mime, + mediaType: finalMime, 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 1119 - 1139, The FileUIPart metadata in prepareRemoteFiles is still using img.mime to set mediaType and the filename extension even after downscaleDataUriTo1MP may re-encode the image. Update prepareRemoteFiles to derive both mediaType and ext from the returned downscaledUri’s data URI header before calling saveImageToMediaInput and pushing the FileUIPart, so the persisted bytes, reported mime, and extension stay consistent.
🧹 Nitpick comments (3)
.pre-commit-config.yaml (2)
16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low valueRuff version pinned twice (
v0.15.20andruff@0.15.20); risk of drift.The mirror
revand the local hook'suvx ruff@...pin the same version in two independent places. If one is bumped without the other, lint and format will silently run against different Ruff versions, producing inconsistent results.Also applies to: 30-30
🤖 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 at line 16, The Ruff version is pinned in two places, so update the pre-commit hook configuration to use a single source of truth for the version. In .pre-commit-config.yaml, keep the mirror hook `rev` and the local `uvx ruff@...` pin aligned by refactoring the Ruff-related hook entries together so they cannot drift independently. Make sure the configuration for the Ruff hooks stays consistent across the `rev` and the `ruff@` version reference.
15-22: 📐 Maintainability & Code Quality | 🔵 Trivial | 💤 Low value
id: ruffis now the legacy alias; current docs recommendruff-check.astral-sh/ruff-pre-commit's
.pre-commit-hooks.yamldocumentsruff-checkas the current linter hook id andruffas a "legacy alias" retained for compatibility. Functionally equivalent today, but worth switching toruff-checkto stay aligned with the actively-documented interface and avoid relying on a deprecated alias in a brand-new config.♻️ Suggested update
- - id: ruff + - id: ruff-check name: ruff lint (Python) args: [--fix] files: ^(service|home-agent)/.*\.py$🤖 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 15 - 22, The pre-commit hook in the ruff configuration still uses the legacy `id: ruff` alias. Update the hook entry in the config to use the current `ruff-check` hook id instead, keeping the existing `name`, `args`, and `files` settings unchanged so the linter behavior stays the same while matching the documented interface.WebUI/src/assets/js/store/backendServices.ts (1)
419-435: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winRoute the sync failure through the errors store instead of
console.warn.A failed OVMS settings sync here is silently swallowed — the KV-cache toggle simply won't take effect and nothing surfaces it beyond a console line. Per coding guidelines, errors should flow through
useErrors().report()(e.g. withsurface: 'silent'to preserve today's no-toast behavior) rather than a bare console call, even though this mirrors the pre-existingllamaCppBuildVariantwatch'sconsole.warn.As per coding guidelines, "All errors must flow through the
errorsstore (useErrors). Report errors viaerrors.report(err, overrides?)— never usetoast.error()directly or rely on bareconsole.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 - 435, The OpenVINO KV cache sync watch is swallowing update failures with a bare console warning, so route the catch path through the errors store instead. In the watch on openvinoKvCacheU4, use useErrors().report() for the updateServiceSettings failure, preserving silent behavior with an override like surface: 'silent' so no toast appears. Keep the existing sync logic intact and replace the console.warn fallback with the store-based error reporting.Source: Coding guidelines
🤖 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 10-13: The comment in the pre-commit config still implies
formatter hooks complete the commit in one pass, but the actual flow requires
rerunning the commit after `ruff-format` or `prettier` rewrites and restages
files. Update the comment near the formatter hook configuration to describe the
re-run workflow accurately, using the formatter hook section and the git
add/restage behavior as the reference point.
In `@WebUI/electron/subprocesses/openVINOBackendService.ts`:
- Around line 1594-1598: The Windows cleanup in killStrayOvmsProcessesWindows is
too broad because it uses taskkill on every ovms.exe process, which can
terminate unrelated OVMS servers. Update this logic in openVINOBackendService to
first discover running ovms.exe PIDs and inspect their ExecutablePath, then only
kill processes whose path is under this.ovmsDir. Keep the existing logging in
killStrayOvmsProcessesWindows but make the termination target-owned instances
only.
- Around line 2166-2168: The shutdown path in openVINOBackendService’s process
cleanup returns too early after proc.kill('SIGTERM') and waitForExit, which can
skip the Windows tree-kill cleanup. Update the same kill/exit flow so that on
Windows it still runs the taskkill /T /F branch before any early return, and
keep the logic centered around proc.kill and waitForExit to ensure child OVMS
worker processes are always terminated.
In `@WebUI/src/assets/js/store/openAiCompatibleChat.ts`:
- Around line 96-109: The reasoning streaming state is shared across all chats
because `reasoningInProgress` and `reasoningStartedAt` live in the store setup
scope and are written by the shared `customFetch` closure. Scope this state per
conversation key instead, similar to `turnErrors`, by storing a keyed reasoning
state map and reading/writing it from `customFetch` using
`requestConversationKey` or `activityScope.conversationKey`. Update the
UI-facing accessors so the active chat reads only its own conversation’s
reasoning state, preventing concurrent turns from overwriting each other.
---
Outside diff comments:
In `@WebUI/src/assets/js/store/homeAgent.ts`:
- Around line 1119-1139: The FileUIPart metadata in prepareRemoteFiles is still
using img.mime to set mediaType and the filename extension even after
downscaleDataUriTo1MP may re-encode the image. Update prepareRemoteFiles to
derive both mediaType and ext from the returned downscaledUri’s data URI header
before calling saveImageToMediaInput and pushing the FileUIPart, so the
persisted bytes, reported mime, and extension stay consistent.
---
Nitpick comments:
In @.pre-commit-config.yaml:
- Line 16: The Ruff version is pinned in two places, so update the pre-commit
hook configuration to use a single source of truth for the version. In
.pre-commit-config.yaml, keep the mirror hook `rev` and the local `uvx ruff@...`
pin aligned by refactoring the Ruff-related hook entries together so they cannot
drift independently. Make sure the configuration for the Ruff hooks stays
consistent across the `rev` and the `ruff@` version reference.
- Around line 15-22: The pre-commit hook in the ruff configuration still uses
the legacy `id: ruff` alias. Update the hook entry in the config to use the
current `ruff-check` hook id instead, keeping the existing `name`, `args`, and
`files` settings unchanged so the linter behavior stays the same while matching
the documented interface.
In `@WebUI/src/assets/js/store/backendServices.ts`:
- Around line 419-435: The OpenVINO KV cache sync watch is swallowing update
failures with a bare console warning, so route the catch path through the errors
store instead. In the watch on openvinoKvCacheU4, use useErrors().report() for
the updateServiceSettings failure, preserving silent behavior with an override
like surface: 'silent' so no toast appears. Keep the existing sync logic intact
and replace the console.warn fallback with the store-based error reporting.
🪄 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: 7d21ad8d-482d-42fe-acf9-f1a9b6a0a0cb
📒 Files selected for processing (20)
.pre-commit-config.yamlWebUI/build/scripts/ensure-precommit-hook.mjsWebUI/electron/remoteUpdates.tsWebUI/electron/subprocesses/comfyUIBackendService.tsWebUI/electron/subprocesses/llamaCppBackendService.tsWebUI/electron/subprocesses/openVINOBackendService.tsWebUI/electron/subprocesses/tools.tsWebUI/external/backend-versions.jsonWebUI/package.jsonWebUI/src/assets/i18n/en-US.jsonWebUI/src/assets/js/store/backendServices.tsWebUI/src/assets/js/store/homeAgent.tsWebUI/src/assets/js/store/openAiCompatibleChat.tsWebUI/src/components/BackendOptions.vueWebUI/src/components/ChatReasoningDisplay.vueWebUI/src/env.d.tsWebUI/src/lib/utils.tsWebUI/src/views/Chat.vuemodes/base/presets/phison.jsonreadme.md
| # Formatters (ruff-format, prettier) reformat in place and then `git add` what they | ||
| # changed, so they never abort the commit just because formatting was applied — the | ||
| # reformatted files are restaged and included in the same commit. The linters still | ||
| # fail on genuine, unfixable problems. |
There was a problem hiding this comment.
🎯 Functional Correctness | 🟡 Minor | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Sanity-check pre-commit's file-modification detection with the local ruff-format hook.
# (Run manually / locally; not runnable in this sandbox since pre-commit/uvx/ruff binaries
# and a git identity are not guaranteed to be available here.)
echo 'x=1' > /tmp/badly_formatted.py
git -C . add -f /tmp/badly_formatted.py 2>/dev/null || true
pre-commit run ruff-format --files /tmp/badly_formatted.py
echo "Exit code: $?"Repository: TNG/AI-Playground
Length of output: 214
🏁 Script executed:
#!/bin/bash
set -euo pipefail
# Inspect the relevant config section and nearby hook definitions.
git ls-files .pre-commit-config.yaml
wc -l .pre-commit-config.yaml
sed -n '1,120p' .pre-commit-config.yamlRepository: TNG/AI-Playground
Length of output: 2216
🌐 Web query:
pre-commit modifying files hook failed restage git add official docs formatting hook abort commit
💡 Result:
When a pre-commit hook modifies a file, the pre-commit framework intentionally fails the commit [1][2]. This design is deliberate to ensure that no changes are committed to your repository without being explicitly reviewed and staged by you [1][3]. If a hook modifies a file, it will cause the commit to abort [1][4]. To resolve this, you can follow these steps: 1. Inspect the changes made by the hook to ensure they are correct [1]. 2. Add the modified files to the staging area using git add or git add -u [1][3][4]. 3. Re-run your commit command [1][4]. Since the files are now in the index, the commit should proceed successfully [4][5]. The framework does not automatically stage these changes for you because it considers modifying the staging area an unsafe operation that could lead to unintended consequences, such as overwriting unstaged changes or committing broken code if a hook behaves unexpectedly [1][3][2]. If you find this workflow creates too much friction, you have a few options: - Manually stage the changes and re-run the commit [1][3]. - If you are confident in the hook, you can bypass the pre-commit checks using the --no-verify flag (e.g., git commit --no-verify -m "message"), though this is generally discouraged as it skips all configured hooks [4]. - If you prefer a different approach, you may consider other tools that integrate with git-commit differently, or accept that manual re-staging is the standard, safe behavior for the pre-commit framework [3][2].
Citations:
- 1: Hook that updates files pre-commit/pre-commit#532
- 2: Add changes generated by pre-commit pre-commit/pre-commit#879
- 3: Automatically stage files changed by hook pre-commit/pre-commit#806
- 4: https://stackoverflow.com/questions/58398995/black-as-pre-commit-hook-always-fails-my-commits
- 5: https://stackoverflow.com/questions/31057527/git-pre-commit-hook-to-format-and-re-add-files-at-the-same-time
Formatter hooks still abort the first commit after rewriting files
Pre-commit still fails when ruff-format or prettier changes tracked files; git add only restages the output so the next git commit succeeds. Update the comment to reflect that this is a re-run workflow, not a one-shot commit.
🤖 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 10 - 13, The comment in the pre-commit
config still implies formatter hooks complete the commit in one pass, but the
actual flow requires rerunning the commit after `ruff-format` or `prettier`
rewrites and restages files. Update the comment near the formatter hook
configuration to describe the re-run workflow accurately, using the formatter
hook section and the git add/restage behavior as the reference point.
| 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) |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
Avoid killing unrelated OVMS instances.
taskkill /IM ovms.exe terminates every ovms.exe on the machine, including servers not owned by this app. Filter by ExecutablePath under this.ovmsDir before killing PIDs.
🧰 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 1594 -
1598, The Windows cleanup in killStrayOvmsProcessesWindows is too broad because
it uses taskkill on every ovms.exe process, which can terminate unrelated OVMS
servers. Update this logic in openVINOBackendService to first discover running
ovms.exe PIDs and inspect their ExecutablePath, then only kill processes whose
path is under this.ovmsDir. Keep the existing logging in
killStrayOvmsProcessesWindows but make the termination target-owned instances
only.
| proc.kill('SIGTERM') | ||
| let exited = await waitForExit(2000) | ||
| if (exited) return |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | ⚡ Quick win
🧩 Analysis chain
🏁 Script executed:
#!/bin/bash
# Verify the Windows path cannot return before the taskkill /T /F branch.
ast-grep run --lang typescript --pattern $'proc.kill("SIGTERM")
let exited = await waitForExit(2000)
if (exited) return' WebUI/electron/subprocesses/openVINOBackendService.tsRepository: TNG/AI-Playground
Length of output: 523
🏁 Script executed:
#!/bin/bash
set -euo pipefail
file='WebUI/electron/subprocesses/openVINOBackendService.ts'
# Show the relevant section with line numbers.
sed -n '2135,2195p' "$file" | cat -n
# Find the enclosing method and any other process-kill branches in this file.
rg -n "waitForExit|taskkill|SIGTERM|SIGKILL|process.platform|proc.kill" "$file"Repository: TNG/AI-Playground
Length of output: 5075
Run taskkill /T /F before the early return on Windows.
proc.kill('SIGTERM') can let the parent exit before the Windows tree-kill branch runs, leaving OVMS worker processes alive and reinstall cleanup flaky.
🧰 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 2166 -
2168, The shutdown path in openVINOBackendService’s process cleanup returns too
early after proc.kill('SIGTERM') and waitForExit, which can skip the Windows
tree-kill cleanup. Update the same kill/exit flow so that on Windows it still
runs the taskkill /T /F branch before any early return, and keep the logic
centered around proc.kill and waitForExit to ensure child OVMS worker processes
are always terminated.
| // True while the model is actively emitting reasoning (i.e. the last content | ||
| // chunk was a reasoning delta and no text/tool chunk has followed). Driven | ||
| // straight off the chunk stream so the UI never has to infer "is reasoning | ||
| // still going?" from part positions or the per-delta `reasoningFinished` | ||
| // timestamp (which is bumped to "now" on every delta and so always looks | ||
| // recent). Cleared when the turn ends (see the `processing` safety-net watch). | ||
| const reasoningInProgress = ref(false) | ||
| // Wall-clock start of the reasoning block currently in progress. The part's | ||
| // own `reasoningStarted` metadata isn't attached to the live (still- | ||
| // streaming) UI part, so the chat view needs this to drive an increasing | ||
| // "Reasoned for X.Xs" timer; once the block finishes the view falls back to | ||
| // the part metadata. Updated to the block start whenever a new block begins. | ||
| const reasoningStartedAt = ref(0) | ||
|
|
There was a problem hiding this comment.
🩺 Stability & Availability | 🟠 Major | 🏗️ Heavy lift
reasoningInProgress/reasoningStartedAt are store-global, but streaming is per-conversation.
Both refs are declared once in the store's setup scope and captured by the single customFetch closure that every Chat instance shares (getOrCreateChat wires the same customFetch for every conversationKey). Nothing scopes these to requestConversationKey/conversations.activeKey.
This store supports genuinely concurrent turns: Home Agent channels (telegram/slack/discord/mock) each drain and call chatStore.generate() with their own conversationKey, and the desktop user can be actively chatting in a different conversation at the same time (see homeAgent.ts drainCommonQueue/handleChatMessage/handleAgenticMessage). Two concurrent onChunk invocations (one per turn) both read/write the same reasoningInProgress/reasoningStartedAt refs, so:
- A side-channel turn's reasoning can flip
reasoningInProgresstotruewhile the desktop's active conversation is idle, making its last (already-finished) reasoning part re-render as "streaming" with a live timer sourced from the unrelated side-channel'sreasoningStartedAt. - Conversely, the side-channel's
onFinish/non-reasoning chunk can flipreasoningInProgresstofalsemid-stream for the desktop's genuinely-active reasoning block.
Since turnErrors in this same file already solves an analogous per-conversation problem with a Map<string, AppError>, the same pattern (e.g. a reactive<Record<string, { inProgress: boolean; startedAt: number }>> keyed by conversation) would fix this without changing the streaming/timing algorithm itself.
♻️ Suggested direction (sketch, not a drop-in diff)
- const reasoningInProgress = ref(false)
- const reasoningStartedAt = ref(0)
+ // Keyed by conversationKey so concurrent turns (desktop + Home Agent
+ // side-channels) don't clobber each other's reasoning indicator.
+ const reasoningStateByKey = reactive<Record<string, { inProgress: boolean; startedAt: number }>>({})
+ function reasoningStateFor(key: string) {
+ return reasoningStateByKey[key] ?? { inProgress: false, startedAt: 0 }
+ }Then thread activityScope.conversationKey (already computed in customFetch) through the onChunk/onFinish writes instead of the bare refs, and expose reasoningStateFor/a computed keyed on conversations.activeKey for the UI to consume (Chat.vue would look up by the active key instead of reading a bare reasoningInProgress).
🤖 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/openAiCompatibleChat.ts` around lines 96 - 109, The
reasoning streaming state is shared across all chats because
`reasoningInProgress` and `reasoningStartedAt` live in the store setup scope and
are written by the shared `customFetch` closure. Scope this state per
conversation key instead, similar to `turnErrors`, by storing a keyed reasoning
state map and reading/writing it from `customFetch` using
`requestConversationKey` or `activityScope.conversationKey`. Update the
UI-facing accessors so the active chat reads only its own conversation’s
reasoning state, preventing concurrent turns from overwriting each other.
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:
Summary by CodeRabbit
New Features
Bug Fixes
Documentation