Skip to content

fix: apply DeepSource static analysis fixes + PR review improvements (desktop fork)#11

Closed
vi70x3 wants to merge 5 commits into
mainfrom
fix/deepsource-1
Closed

fix: apply DeepSource static analysis fixes + PR review improvements (desktop fork)#11
vi70x3 wants to merge 5 commits into
mainfrom
fix/deepsource-1

Conversation

@vi70x3

@vi70x3 vi70x3 commented Jun 6, 2026

Copy link
Copy Markdown
Collaborator

Summary

Applies DeepSource static analysis fixes rebased onto the desktop-oriented fork, plus addresses PR review feedback.

DeepSource Fixes (~180 files)

  • Fixed 25 DeepSource rule categories (empty blocks, type issues, for-of suggestions, etc.)
  • Fixed critical Boolean() conversion bugs from automated script
  • Fixed anyunknown type errors in minecraft/twitter services
  • Fixed empty function body regressions (JS-0321)
  • Removed references to source-metadata and voice-packs modules that don't exist on the desktop fork

PR Review Improvements (9 tasks)

  1. resolveVoicePackSpeechInput: Fixed early return bypassing voice pack metadata when params is undefined but voicePack is present; added optional chaining for params access
  2. Empty string normalizers: normalizePercentOption and normalizeRateOption now return undefined for empty strings instead of 0 or throwing
  3. toListVoicesOptions: Now forwards custom fetch from provider instead of discarding it
  4. calculateVolume: Simplified switch to if/else, removing eslint-disable annotations
  5. Promise executor returns: Fixed 3 files (Live2D Canvas, Spine Canvas, preview-stage) to use resolve(); return; pattern
  6. Hearing config promise: Removed redundant return in .then() chain
  7. createVoicePackVoice: Centralized duplicate function into speech.ts
  8. TTS analytics types: Extracted TtsTrigger and TtsSource into shared tts-analytics.ts
  9. CI deduplication: Removed redundant SwiftLint run before pnpm install

Summary by Sourcery

Apply DeepSource static analysis fixes, strengthen speech pipeline and voice pack integration, add TTS analytics and stop-speaking controls, wire up voice pack binding in cards and settings, extend provider metadata handling, and expand workspace, linting, and CI tooling.

New Features:

  • Add voice pack binding to Airi cards and surface selectable Voice Packs in speech settings with cost metadata.
  • Introduce stop-speaking controls across chat surfaces with analytics tracking for TTS stop events and chat history actions.
  • Add TTS analytics wiring for official speech, including trigger/source hints for REST and streaming TTS paths.
  • Expose voice pack-aware speech input resolution to support SSML, adapter prosody, and provider extraBody shaping.
  • Add helper to build provider list voices options that preserve custom fetch implementations.

Bug Fixes:

  • Fix voice provider listVoices calls to forward custom fetch implementations and avoid losing provider-specific HTTP configuration.
  • Correct handling of empty string inputs in voice pack percent/rate normalizers to avoid throwing or misinterpreting values.
  • Ensure Live2D, Spine, and canvas capture utilities resolve promises safely when canvas/context are unavailable.
  • Prevent mid-session speech provider/model changes and stop requests from leaving stale sessions or audio playback running.
  • Stabilize various async teardown paths (providers, widgets, analyzers, recognizers) to guard against double-dispose errors.

Enhancements:

  • Refine speech pipeline to respect card-bound Voice Packs and centralize Voice Pack voice creation logic.
  • Tighten speech configuration validity checks to account for OpenAI-compatible provider models/voices coming from provider config.
  • Improve analytics composable with surface-aware conversation events for deletes, clears, retries, and session selection.
  • Harmonize TypeScript and ESLint style across stores, composables, and workers, including safer boolean checks and error handling.
  • Optimize chunked TTS and tokenizer helpers with Unicode-safe regexes and clearer branching logic.

Build:

  • Expand pnpm workspace configuration with additional package groups, catalog-based dependency versions, overrides, and patches.
  • Add root lint scripts based on a shared ESLint config and wire nano-staged to run lint-based autofix instead of Prettier.
  • Align multiple package.json files to use catalog-driven versions, shared peer constraints, and Electron/version catalogs.

CI:

  • Split CI into dedicated lint, build-test, unit-test, typecheck, and provenance jobs, including Godot/.NET setup for the stage-tamagotchi engine and SwiftLint deduplication.

Documentation:

  • Update contributor guidance in AGENTS.md to reflect ESLint-based formatting and linting workflows and add notes about documented solutions in docs/solutions/.

vi70x3 added 3 commits June 6, 2026 14:04
Applied deepsource fixes from fix/deepsource-1 branch, rebased onto the
desktop-oriented fork (main). Only includes files that exist on main.
Excludes files from deleted apps (server, web, pocket, docs, services, etc.).

Fixes include:
- Empty block statements → noop comments
- for-in loops → for-of
- any → unknown type conversions (where safe)
- !!x → Boolean(x) conversions
- Redundant type annotations removed
- ESLint disable comments for intentional patterns

Bug fixes for incorrect automated conversions:
- Boolean(token.value.trim)() → Boolean(token.value.trim())
- Boolean(Object.values)(...) → Object.values(...)
- Boolean(x)?.prop → Boolean(x?.prop) pattern fixes
- Fix resolveVoicePackSpeechInput early return bypassing voice pack metadata
- Add optional chaining for params access in speech input resolution
- Fix empty string handling in normalizePercentOption/normalizeRateOption
- Forward custom fetch in toListVoicesOptions helper
- Simplify calculateVolume switch to if/else (remove eslint-disable)
- Fix Promise executor returns in Live2D/Spine Canvas and preview-stage
- Fix hearing config promise return in controls-island
- Centralize createVoicePackVoice in speech.ts (remove duplication)
- Centralize TtsTrigger/TtsSource types in shared tts-analytics.ts
- Deduplicate SwiftLint run in CI workflow
@mergeguards

mergeguards Bot commented Jun 6, 2026

Copy link
Copy Markdown

MergeGuard — Free plan allows 1 active repository. Upgrade to protect more repositories.

@sourcery-ai

sourcery-ai Bot commented Jun 6, 2026

Copy link
Copy Markdown

Reviewer's Guide

Applies DeepSource static analysis fixes and lint cleanups across the repo, tightens TypeScript/JS correctness and analytics wiring, and introduces voice-pack–aware TTS handling and richer analytics/CI workflows for the desktop-oriented fork.

Sequence diagram for chat TTS with voice packs and analytics

sequenceDiagram
  actor User
  participant ChatUI as ChatOrchestrator
  participant Stage as Stage.vue
  participant SpeechStore as useSpeechStore
  participant Provider as SpeechProvider
  participant TtsServer as Official_TTS_API

  User->>ChatUI: send message
  ChatUI->>Stage: onBeforeMessageComposed()
  Stage->>Stage: openTtsSession()
  ChatUI->>Stage: onTokenLiteral(literal)
  Stage->>Stage: currentSession.appendText(literal)

  ChatUI->>Stage: onStreamEnd()
  Stage->>Stage: currentSession.finishInput()
  Stage->>Stage: speechPipeline.tts(request, signal)

  alt voice pack bound on active card
    Stage->>SpeechStore: resolveVoicePackSpeechInput({ text, voice, providerConfig, params: voicePack.params, voicePack, forceSSML: ssmlEnabled, supportsSSML, supportsAdapterProsody })
    SpeechStore-->>Stage: VoicePackSpeechInput { input, providerConfig }
  else no voice pack
    Stage->>SpeechStore: resolveVoicePackSpeechInput({ text, voice, providerConfig })
    SpeechStore-->>Stage: VoicePackSpeechInput { input, providerConfig }
  end

  Note over Stage: If provider is OFFICIAL_SPEECH_PROVIDER_ID,<br/>inject airi_analytics { trigger: "auto", source: "chat_auto_tts" }

  Stage->>Provider: speech(model, providerConfigWithAnalytics)
  Provider-->>Stage: request options
  Stage->>TtsServer: generateSpeech(input, voice, extra_body.airi_analytics)
  TtsServer-->>Stage: audio bytes
  Stage->>Stage: decodeAudioData
  Stage->>Stage: playbackManager.enqueue(audio)

  User->>Stage: Stop speaking button
  Stage->>Stage: stopSpeechOutput("manual-chat")
  Stage->>TtsServer: cancel / stopAll
Loading

File-Level Changes

Change Details Files
Refactor provider store and speech providers to propagate custom fetch/list-voices options, normalize validation logic, and clean up style to satisfy static analysis.
  • Introduce toListVoicesOptions helper that preserves provider-specific fetch and merges voice options.
  • Simplify multiple validateProviderConfig implementations using Boolean checks and shared error mapping.
  • Normalize various arrow functions, conditional chains, and string building to satisfy DeepSource and lint rules.
packages/stage-ui/src/stores/providers.ts
Enhance Stage scene TTS pipeline to support voice packs, SSML/prosody routing, stop-speaking control, and analytics tagging for official speech.
  • Wire OFFICIAL_SPEECH_PROVIDER_ID and createVoicePackVoice into Stage.vue and useSpeechStore to derive model/voice from active voice pack.
  • Introduce resetAssistantSpeechSurface/stopSpeechOutput helpers and hook them into chat events and a new stop-speaking control store.
  • Attach airi_analytics trigger/source payloads for both REST and streaming TTS paths and plumb ttsTrigger/ttsSource through streaming snapshot creation.
packages/stage-ui/src/components/scenes/Stage.vue
packages/stage-ui/src/stores/modules/speech.ts
packages/stage-ui/src/libs/speech/streaming-pipeline.ts
packages/stage-ui/src/libs/speech/streaming-session.ts
packages/stage-ui/src/libs/providers/providers/official/shared.ts
packages/stage-ui/src/libs/providers/providers/official/index.ts
Add centralized Voice Pack modeling, binding to Airi cards, and resolution of voice-pack-aware TTS input (prosody, adapter vs SSML).
  • Define VoicePackParams/VoicePackSnapshot models and bindVoicePackToActiveCard helper on airi-card store.
  • Implement resolveVoicePackSpeechInput, percent/rate normalizers, and assertSupportedVoicePackParams in speech store.
  • Add tests covering Voice Pack mapping, prosody routing, billing metadata passthrough, and immutability guarantees.
packages/stage-ui/src/stores/modules/airi-card.ts
packages/stage-ui/src/stores/modules/airi-card.test.ts
packages/stage-ui/src/stores/modules/speech.ts
packages/stage-ui/src/stores/modules/speech.test.ts
packages/stage-ui/src/libs/speech/tts-analytics.ts
Expose Voice Pack selection and usage in Settings UI and wire through to preview generation.
  • Add voice-pack section to speech settings page with cost multiplier formatting and binding into active card.
  • Ensure test-speech generation path uses resolveVoicePackSpeechInput and respects Voice Pack/SSML flags.
  • Add i18n strings for Voice Pack UI states (empty, loading, error).
packages/stage-pages/src/pages/settings/modules/speech.vue
packages/i18n/src/locales/en/settings.yaml
Introduce a reusable stop-speaking UX control across desktop, web, and mobile chat surfaces with analytics.
  • Create useStopSpeakingButton composable (not shown here) and consume it in desktop, mobile, and tamagotchi chat UIs to render a conditional stop button.
  • Wire stop button to a speech-output-control store that signals latestStopRequest consumed by Stage.vue stopSpeechOutput.
  • Emit tts_stop_clicked and other chat action analytics via extended useAnalytics helpers.
packages/stage-layouts/src/components/Layouts/MobileInteractiveArea.vue
apps/stage-tamagotchi/src/renderer/components/InteractiveArea.vue
packages/stage-layouts/src/components/Widgets/ChatArea.vue
packages/stage-layouts/src/components/Widgets/ChatActionButtons.vue
packages/stage-layouts/src/composables/useStopSpeakingButton.ts
packages/stage-ui/src/stores/speech-output-control.ts
packages/stage-ui/src/composables/use-analytics.ts
Expand chat action analytics for deletions, clear, retry, and session selection on various surfaces.
  • Add ConversationAnalyticsSurface/source helpers and new track* methods to useAnalytics.
  • Emit analytics events when deleting messages, clearing history, retrying messages, and selecting sessions from drawers across layouts.
  • Tag events with message_count, message_role, cloud_synced, and surface hints.
packages/stage-ui/src/composables/use-analytics.ts
packages/stage-layouts/src/components/Layouts/InteractiveArea.vue
packages/stage-layouts/src/components/Layouts/MobileInteractiveArea.vue
packages/stage-layouts/src/components/Widgets/ChatActionButtons.vue
apps/stage-tamagotchi/src/renderer/components/InteractiveArea.vue
packages/stage-ui/src/components/scenarios/chat/components/sessions-drawer.vue
Align package.json dependencies with pnpm catalog/overrides, enable workspace-wide linting, and add provenance checks in CI.
  • Switch many app/package dependencies to use pnpm catalog: and shared catalogs blocks, add new workspaces (plugins, services, examples, docs, engines).
  • Replace Prettier-based formatting scripts with moeru-lint ESLint wrapper and nano-staged hook; add ESLint-related devDeps and oxlint integration.
  • Extend CI with a dedicated lint job, Godot C# linting, Godot build steps for stage-tamagotchi-godot, and provenance verification via danielroe/provenance-action.
  • Add catalogMode, overrides, patchedDependencies, and ignored/onlyBuiltDependencies updates in pnpm-workspace.yaml.
pnpm-workspace.yaml
package.json
apps/stage-tamagotchi/package.json
packages/stage-ui/package.json
packages/stage-layouts/package.json
packages/stage-pages/package.json
packages/stage-ui-three/package.json
packages/stage-ui-live2d/package.json
packages/stage-ui-spine/package.json
packages/audio/package.json
packages/ui/package.json
packages/core-agent/package.json
packages/core-character/package.json
packages/server-runtime/package.json
packages/server-sdk/package.json
packages/plugin-sdk/package.json
packages/ccc/package.json
packages/model-driver-lipsync/package.json
packages/pipelines-audio/package.json
packages/stage-shared/package.json
packages/electron-screen-capture/package.json
packages/electron-vueuse/package.json
packages/stage-pages/src/pages/settings/providers/index.vue
.github/workflows/ci.yml
Apply DeepSource-guided safety and style fixes across runtime and worker code: error handling, loops, regexes, type guards, and logging annotations.
  • Replace for-index loops with for-of where appropriate, add /u flags to regexes, and simplify boolean casts to Boolean().
  • Wrap many try/catch blocks with explicit no-empty comments and normalize early returns/consistent-return cases for TS/ESLint.
  • Tighten type narrowing and delete operations with // eslint-disable-next-line annotations, especially in stores manipulating dynamic maps.
  • Annotate console usage in adapters/CLI utilities where logging is intentional (Whisper/Kokoro adapters, Live2D report tool, ensure-electron scripts).
packages/stage-ui/src/stores/audio.ts
packages/stage-ui/src/composables/audio/audio-analyzer.ts
packages/stage-ui/src/libs/audio/manager.ts
packages/pipelines-audio/src/processors/tts-chunker.ts
packages/pipelines-audio/src/llm-streaming-control/controller.ts
packages/stage-ui-live2d/src/utils/eye-motions.ts
packages/stage-ui-three/src/composables/vrm/utils/eye-motions.ts
packages/stage-ui-live2d/src/utils/live2d-structure-report.ts
packages/stage-ui-live2d/src/utils/live2d-preview.ts
packages/stage-ui-spine/src/utils/spine-zip-loader.ts
packages/stage-ui-spine/src/utils/spine-runtime.ts
packages/stage-ui-three/src/composables/vrm/lip-sync.ts
packages/stage-ui-three/src/composables/vrm/animation.ts
packages/stage-ui-three/src/utils/vrm-preview.ts
packages/stage-ui/src/libs/inference/adapters/kokoro.ts
packages/stage-ui/src/libs/inference/adapters/whisper.ts
packages/stage-ui/src/libs/inference/worker-manager.ts
apps/stage-tamagotchi/scripts/ensure-electron.mjs
apps/stage-tamagotchi/scripts/update-test/start-server.ts
packages/electron-screen-capture/src/main/index.ts
packages/stage-ui/src/workers/background-removal/worker.ts
packages/stage-ui/src/workers/vad/vad.ts
packages/stage-ui/src/libs/workers/worker.ts
packages/stage-ui/src/libs/providers/providers/azure-openai/index.ts
packages/stage-ui/src/libs/providers/providers/official/shared.ts
packages/stage-ui/src/libs/chat-sync/ws-client.ts
packages/stage-ui/src/libs/providers/providers/aliyun/utils.ts
packages/stage-ui/src/stores/providers/web-speech-api/index.ts
packages/stage-ui/src/stores/modules/hearing.ts
packages/stage-ui/src/stores/chat/session-store.ts
packages/stage-ui/src/stores/mods/api/channel-gateway.ts
packages/stage-ui/src/stores/mods/api/channel-server.ts
packages/stage-ui/src/stores/provider-catalog.ts
packages/stage-ui/src/stores/background.ts
packages/stage-ui/src/stores/characters.ts
packages/stage-ui/src/stores/auth.ts
packages/stage-ui/src/stores/chat.ts
packages/stage-ui/src/stores/modules/artistry.ts
packages/stage-ui/src/stores/modules/consciousness.ts
packages/stage-ui/src/stores/modules/discord.ts
packages/stage-ui/src/stores/modules/gaming-minecraft.ts
packages/stage-ui/src/stores/modules/vision/store.ts
packages/stage-ui/src/stores/settings/general.test.ts
packages/stage-ui/src/stores/llm-streaming-control.ts
packages/stage-ui/src/stores/mods/api/context-bridge.contract.browser.test.ts
packages/stage-ui/src/stores/mods/api/context-bridge.ts
packages/stage-ui/src/stores/mods/api/context-bridge.ts
packages/stage-ui/src/stores/providers/converters.ts
packages/stage-ui/src/stores/providers/aliyun/utils.ts
packages/stage-ui/src/components/misc/alert.vue
packages/stage-ui/src/components/data-pane/color-picker.vue
packages/stage-ui/src/components/data-pane/property-point.vue
packages/stage-ui/src/components/scenes/ViewControlSlider.vue
packages/stage-ui/src/components/scenarios/settings/model-settings/preview-stage.vue
packages/stage-ui/src/components/scenarios/dialogs/audio-input/hearing-config.vue
packages/stage-ui-three/src/stores/view-control.ts
packages/stage-ui-three/src/composables/vrm/core.ts
packages/stage-ui-three/src/composables/vrm/loader.ts
packages/stage-ui-live2d/src/stores/view-control.ts
packages/stage-ui-spine/src/stores/view-control.ts
packages/stage-ui/src/utils/tts.ts
packages/stage-shared/src/auth/pkce.ts
packages/audio/src/audio-context/index.ts
apps/stage-tamagotchi/src/main/services/electron/window.ts
apps/stage-tamagotchi/src/main/services/electron/global-shortcut-uiohook.ts
apps/stage-tamagotchi/src/main/services/electron/mock-auto-updater.ts
apps/stage-tamagotchi/src/main/services/airi/widgets/index.ts
apps/stage-tamagotchi/src/main/services/airi/widgets/providers/comfyui.ts
apps/stage-tamagotchi/src/main/services/airi/widgets/providers/replicate.ts
apps/stage-tamagotchi/src/main/services/electron/auto-updater.ts
apps/stage-tamagotchi/src/main/services/airi/godot-stage/index.ts
apps/stage-tamagotchi/src/main/windows/caption/index.ts
apps/stage-tamagotchi/src/main/windows/shared/display.ts
apps/stage-tamagotchi/src/main/tray/index.ts
apps/stage-tamagotchi/src/renderer/pages/caption.vue
apps/stage-tamagotchi/src/renderer/pages/devtools/vision.vue
apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/indicator-mic-volume.vue
apps/stage-tamagotchi/src/renderer/components/stage-islands/controls-island/controls-island-hearing-config.vue
apps/stage-tamagotchi/src/renderer/pages/settings/models/index.vue
apps/stage-tamagotchi/src/renderer/pages/widgets.vue
apps/stage-tamagotchi/src/renderer/pages/index.vue
packages/server-runtime/src/index.ts
packages/server-runtime/src/server/index.ts
packages/server-runtime/src/server-ws/airi/index.ts
packages/server-runtime/src/server-ws/core/index.ts
packages/server-sdk/src/client.ts
packages/server-sdk/src/client.ts
packages/server-sdk/src/client.ts
packages/server-sdk/src/client.ts
packages/server-sdk/src/client.ts
packages/server-sdk/src/client.ts
Tighten i18n content and AGENTS documentation to reflect new lint workflow and fix minor text issues.
  • Update AGENTS.md to mention ESLint-based linting instead of Prettier/DeepSource, and reference docs/solutions.
  • Fix minor wording/translation issues in ru, es, zh-Hans settings/auth locales (quotes and titles).
AGENTS.md
packages/i18n/src/locales/ru/settings.yaml
packages/i18n/src/locales/es/server/auth.yaml
packages/i18n/src/locales/zh-Hans/settings.yaml

Tips and commands

Interacting with Sourcery

  • Trigger a new review: Comment @sourcery-ai review on the pull request.
  • Continue discussions: Reply directly to Sourcery's review comments.
  • Generate a GitHub issue from a review comment: Ask Sourcery to create an
    issue from a review comment by replying to it. You can also reply to a
    review comment with @sourcery-ai issue to create an issue from it.
  • Generate a pull request title: Write @sourcery-ai anywhere in the pull
    request title to generate a title at any time. You can also comment
    @sourcery-ai title on the pull request to (re-)generate the title at any time.
  • Generate a pull request summary: Write @sourcery-ai summary anywhere in
    the pull request body to generate a PR summary at any time exactly where you
    want it. You can also comment @sourcery-ai summary on the pull request to
    (re-)generate the summary at any time.
  • Generate reviewer's guide: Comment @sourcery-ai guide on the pull
    request to (re-)generate the reviewer's guide at any time.
  • Resolve all Sourcery comments: Comment @sourcery-ai resolve on the
    pull request to resolve all Sourcery comments. Useful if you've already
    addressed all the comments and don't want to see them anymore.
  • Dismiss all Sourcery reviews: Comment @sourcery-ai dismiss on the pull
    request to dismiss all existing Sourcery reviews. Especially useful if you
    want to start fresh with a new review - don't forget to comment
    @sourcery-ai review to trigger a new review!

Customizing Your Experience

Access your dashboard to:

  • Enable or disable review features such as the Sourcery-generated pull request
    summary, the reviewer's guide, and others.
  • Change the review language.
  • Add, remove or edit custom review instructions.
  • Adjust other review settings.

Getting Help

@sourcery-ai sourcery-ai 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.

Hey - I've found 3 issues, and left some high level feedback:

  • In providers.ts there is a for (const metadata of Object.values(providerMetadata)) loop that only checks definedProviderIds.has(metadata.id) and then immediately continues, making the whole loop a no-op; either remove it or implement the intended behavior to avoid confusion.
  • The WebSocket URL builders for streaming TTS (toWebSocketUrl in streaming-pipeline.ts and streaming-session.ts) now share the same analytics parameters; consider extracting this into a shared helper to reduce duplication and keep future query-parameter changes in one place.
Prompt for AI Agents
Please address the comments from this code review:

## Overall Comments
- In `providers.ts` there is a `for (const metadata of Object.values(providerMetadata))` loop that only checks `definedProviderIds.has(metadata.id)` and then immediately continues, making the whole loop a no-op; either remove it or implement the intended behavior to avoid confusion.
- The WebSocket URL builders for streaming TTS (`toWebSocketUrl` in `streaming-pipeline.ts` and `streaming-session.ts`) now share the same analytics parameters; consider extracting this into a shared helper to reduce duplication and keep future query-parameter changes in one place.

## Individual Comments

### Comment 1
<location path="packages/stage-ui/src/stores/providers.ts" line_range="2271-2272" />
<code_context>
     providerMetadata[providerId] = translated
   }

+  for (const metadata of Object.values(providerMetadata)) {
+    if (definedProviderIds.has(metadata.id))
+      continue
+  }
</code_context>
<issue_to_address>
**nitpick:** Remove the no-op loop over `providerMetadata` or implement the intended behavior.

This loop iterates over `Object.values(providerMetadata)` but never does anything in either branch, so it has no effect and appears to be leftover scaffolding. Please either remove it or implement the intended filtering/cleanup logic to keep the code clear.
</issue_to_address>

### Comment 2
<location path="packages/stage-ui/src/stores/modules/airi-card.ts" line_range="20" />
<code_context>
 import { useConsciousnessStore } from './consciousness'
 import { useSpeechStore } from './speech'

+export type VoicePackParams = Record<string, string | number | boolean | null>
+
+export interface VoicePackBindingInput {
</code_context>
<issue_to_address>
**issue (bug_risk):** VoicePackParams allows booleans but normalization treats them as an error; either narrow the type or handle booleans explicitly.

`VoicePackParams` is `Record<string, string | number | boolean | null>`, and `normalizePercentOption`/`normalizeRateOption` accept `boolean` in their types but then throw for non-string/number. Any legitimate boolean params (e.g., feature flags) would hit this path and cause a runtime error.

Either tighten the type (e.g., `Record<string, string | number | null>` for these keys) or keep `boolean` but explicitly handle it in the normalizers (e.g., ignore, map, or bypass). This keeps the runtime behavior aligned with the declared types.
</issue_to_address>

### Comment 3
<location path="packages/i18n/src/locales/ru/settings.yaml" line_range="1626-1627" />
<code_context>
     skybox-intensity: Интенсивность SkyBox
     skybox-specular-mix: Причудливый микс
 spine:
-  title: Spine Settings
+  title: Настройки голоса
   scale-and-position:
     title: Масштаб и позиция
</code_context>
<issue_to_address>
**suggestion (bug_risk):** Russian translation for `spine.title` now talks about voice settings, which likely misrepresents the Spine model settings section.

`spine.title` was changed from `Spine Settings` to `Настройки голоса` (“Voice settings”), but this section configures Spine 2D rigs/animation, not audio. This mismatch may confuse users expecting display/animation options.

Unless the UI meaning has changed to voice-related settings, please use a translation closer to "Spine-настройки" or "Настройки Spine" to reflect the actual feature domain.

```suggestion
spine:
  title: Настройки Spine
```
</issue_to_address>

Sourcery is free for open source - if you like our reviews please consider sharing them ✨
Help me be more useful! Please click 👍 or 👎 on each comment and I'll use the feedback to improve your reviews.

Comment thread packages/stage-ui/src/stores/providers.ts Outdated
Comment thread packages/stage-ui/src/stores/modules/airi-card.ts Outdated
Comment thread packages/i18n/src/locales/ru/settings.yaml

@gemini-code-assist gemini-code-assist 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.

Code Review

This pull request introduces curated Voice Packs to bind specific voices to active characters, integrates analytics tracking for chat actions, and transitions the monorepo from Prettier to ESLint for formatting and linting. Additionally, dependencies are updated to use pnpm workspace catalogs. Feedback on the changes highlights a logic bug in resolveVoicePackSpeechInput where prosody validation is bypassed, a parsing issue in normalizePercentOption with empty percent strings, an empty loop in the providers store, and redundant destructuring of settingsStore in Stage.vue.

Important

The consumer version of Gemini Code Assist on GitHub is being sunset. Starting June 18, 2026, new organization installations will be blocked, and all code review activity will officially cease on July 17, 2026.
For more details on the timeline and next steps, please review the Help Documentation.

Comment on lines +516 to +536
if (options.voicePack) {
providerConfig.extraBody = {
...(providerConfig.extraBody as Record<string, unknown> | undefined),
voice_pack: {
pack_id: options.voicePack.packId,
cost_multiplier: options.voicePack.costMultiplier,
...(needsProsody && options.supportsAdapterProsody
? { pitch, volume }
: {}),
},
}
}
else if (needsProsody && options.supportsAdapterProsody) {
providerConfig.extraBody = {
...(providerConfig.extraBody as Record<string, unknown> | undefined),
voice_pack: { pitch, volume },
}
}
else if (needsProsody && !options.forceSSML && !options.supportsSSML) {
throw new Error('Voice Pack pitch and volume parameters require an SSML-capable speech provider.')
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

high

There is a logic bug here: if options.voicePack is present, the function enters the first if block and completely bypasses the else if checks for prosody support. If the provider supports neither adapter prosody nor SSML, it will fall through and return SSML input anyway, which will cause the TTS engine to fail or speak raw SSML tags. We should validate prosody support upfront before applying the voice pack configuration.

    if (needsProsody && !options.supportsAdapterProsody && !options.forceSSML && !options.supportsSSML) {
      throw new Error('Voice Pack pitch and volume parameters require an SSML-capable speech provider.')
    }

    if (options.voicePack) {
      providerConfig.extraBody = {
        ...(providerConfig.extraBody as Record<string, unknown> | undefined),
        voice_pack: {
          pack_id: options.voicePack.packId,
          cost_multiplier: options.voicePack.costMultiplier,
          ...(needsProsody && options.supportsAdapterProsody
            ? { pitch, volume }
            : {}),
        },
      }
    }
    else if (needsProsody && options.supportsAdapterProsody) {
      providerConfig.extraBody = {
        ...(providerConfig.extraBody as Record<string, unknown> | undefined),
        voice_pack: { pitch, volume },
      }
    }

Comment on lines +58 to +81
function normalizePercentOption(value: string | number | boolean | null | undefined, name: string): number | undefined {
if (value == null)
return undefined

if (typeof value === 'number') {
if (Number.isFinite(value))
return value
throw new Error(`Voice Pack parameter "${name}" must be a finite number.`)
}

if (typeof value !== 'string')
throw new Error(`Voice Pack parameter "${name}" must be a number or percent string.`)

const trimmed = value.trim()
if (trimmed === '')
return undefined

const normalized = trimmed.endsWith('%') ? trimmed.slice(0, -1) : trimmed
const parsed = Number(normalized)
if (!Number.isFinite(parsed))
throw new Error(`Voice Pack parameter "${name}" must be a number or percent string.`)

return parsed
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

If trimmed is an empty percent string like "%" or " %", trimmed.endsWith('%') is true, and trimmed.slice(0, -1) returns "". In JavaScript, Number("") evaluates to 0, meaning invalid inputs like "%" will be silently parsed as 0 instead of throwing an error. We should check if the sliced string is empty after trimming.

function normalizePercentOption(value: string | number | boolean | null | undefined, name: string): number | undefined {
  if (value == null)
    return undefined

  if (typeof value === 'number') {
    if (Number.isFinite(value))
      return value
    throw new Error(`Voice Pack parameter "${name}" must be a finite number.`)
  }

  if (typeof value !== 'string')
    throw new Error(`Voice Pack parameter "${name}" must be a number or percent string.`)

  const trimmed = value.trim()
  if (trimmed === '')
    return undefined

  const normalized = trimmed.endsWith('%') ? trimmed.slice(0, -1).trim() : trimmed
  if (normalized === '')
    throw new Error(`Voice Pack parameter "${name}" must be a number or percent string.`)

  const parsed = Number(normalized)
  if (!Number.isFinite(parsed))
    throw new Error(`Voice Pack parameter "${name}" must be a number or percent string.`)

  return parsed
}

Comment on lines +2271 to +2274
for (const metadata of Object.values(providerMetadata)) {
if (definedProviderIds.has(metadata.id))
continue
}

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

This loop is completely empty and does nothing. It should be removed to keep the codebase clean.

Comment on lines 73 to 89
stageModelSelected,
themeColorsHue,
themeColorsHueDynamic,

} = storeToRefs(settingsStore)
const {
live2dShadowEnabled,
live2dMaxFps,
live2dRenderScale,
} = storeToRefs(useSettingsLive2d())
const {
spinePremultipliedAlpha,
spineDefaultMixDuration,
spineIdleAnimationEnabled,
spineMaxFps,
spineRenderScale,
} = storeToRefs(settingsStore)

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

medium

The settingsStore is destructured twice into refs. We can combine these into a single destructuring block to improve readability and avoid redundant storeToRefs calls.

const {
  stageModelSelected,
  themeColorsHue,
  themeColorsHueDynamic,
  spinePremultipliedAlpha,
  spineDefaultMixDuration,
  spineIdleAnimationEnabled,
  spineMaxFps,
  spineRenderScale,
} = storeToRefs(settingsStore)
const {
  live2dShadowEnabled,
  live2dMaxFps,
  live2dRenderScale,
} = storeToRefs(useSettingsLive2d())

@deepsource-io

deepsource-io Bot commented Jun 6, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in c7aa947...c8cdc2e on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Jun 6, 2026 11:58a.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

- Move prosody validation before voice pack payload setup in resolveVoicePackSpeechInput
- Fix percentage normalization: throw on empty string after % strip (e.g. '%' input)
- Narrow VoicePackParams type to exclude boolean (normalizers reject booleans)
- Remove no-op loop over providerMetadata in providers.ts
- Consolidate duplicate storeToRefs(settingsStore) in Stage.vue
- Fix Russian i18n: spine.title 'Настройки голоса' → 'Настройки Spine'
- Remove unused VoiceInfo imports in Stage.vue and speech.vue
@mergeguards

mergeguards Bot commented Jun 6, 2026

Copy link
Copy Markdown

MergeGuard — Free plan allows 1 active repository. Upgrade to protect more repositories.

@deepsource-io

deepsource-io Bot commented Jun 6, 2026

Copy link
Copy Markdown

DeepSource Code Review

We reviewed changes in c7aa947...0667751 on this pull request. Below is the summary for the review, and you can see the individual issues we found as inline review comments.

See full review on DeepSource ↗

Important

Some issues found as part of this review are outside of the diff in this pull request and aren't shown in the inline review comments due to GitHub's API limitations. You can see those issues on the DeepSource dashboard.

PR Report Card

Overall Grade   Security  

Reliability  

Complexity  

Hygiene  

Code Review Summary

Analyzer Status Updated (UTC) Details
JavaScript Jun 6, 2026 1:46p.m. Review ↗

Important

AI Review is run only on demand for your team. We're only showing results of static analysis review right now. To trigger AI Review, comment @deepsourcebot review on this thread.

Deduplicate the toWebSocketUrl helper that was copied between
streaming-pipeline.ts and streaming-session.ts. The shared function
in tts-analytics.ts keeps future query-parameter changes in one place.
@mergeguards

mergeguards Bot commented Jun 6, 2026

Copy link
Copy Markdown

MergeGuard — Free plan allows 1 active repository. Upgrade to protect more repositories.

@vi70x4 vi70x4 closed this Jun 6, 2026
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