Skip to content

Conversation

@zerob13
Copy link
Collaborator

@zerob13 zerob13 commented Jun 15, 2025

Pull Request Description (中文)

你的功能请求是否与某个问题有关?请描述一下。

  • 支持了对于温度,模型基础能力等自定义设置,在自定义供应商的情况更好用

请描述你希望的解决方案

  • 通过持久化用户对于特定供应商特定模型id的配置
  • 匹配规则 用户设置>供应商设置>启发式通用配置
  • 后续可以接入其他供应商的模型配置接口来写入本地,甚至我们可以提供模型配置接口来更新模型配置

桌面应用程序的 UI/UX 更改
image
image

Summary by CodeRabbit

  • New Features

    • Introduced a dialog interface for configuring model parameters, including context length, max tokens, temperature, model type, and advanced capabilities.
    • Added comprehensive APIs to manage, save, reset, import, and export custom model configurations per provider.
    • Enabled batch retrieval and management of model statuses for improved efficiency.
    • Extended localization support for model configuration UI in multiple languages.
  • Improvements

    • Optimized fetching of model statuses using batch requests to enhance performance.
    • Added new event notifications for model configuration changes to keep the UI synchronized.
  • Documentation

    • Simplified and updated development standards documentation.
    • Removed detailed internal design documentation for configuration management.
  • Tests

    • Added thorough test coverage for model configuration management features.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Jun 15, 2025

Warning

Rate limit exceeded

@zerob13 has exceeded the limit for the number of commits or files that can be reviewed per hour. Please wait 12 minutes and 23 seconds before requesting another review.

⌛ How to resolve this issue?

After the wait time has elapsed, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

We recommend that you space out your commits to avoid hitting the rate limit.

🚦 How do rate limits work?

CodeRabbit enforces hourly rate limits for each developer per organization.

Our paid plans have higher rate limits than the trial, open-source and free plans. In all cases, we re-allow further reviews after a brief timeout.

Please see our FAQ for further information.

📥 Commits

Reviewing files that changed from the base of the PR and between 438fd3d and ec4e900.

📒 Files selected for processing (1)
  • src/main/presenter/llmProviderPresenter/index.ts (5 hunks)

Walkthrough

This update introduces a modular and extensible system for managing model configurations, including a new persistent storage helper, APIs for batch operations, and a Vue-based dialog for editing model parameters. The changes add localization support, optimize batch status fetching, and ensure model configuration files are included in backup and sync workflows. Legacy documentation and obsolete configuration files are removed.

Changes

Files/Groups Change Summary
.cursor/rules/build-deploy.mdc, docs/config-presenter-design.md Deleted obsolete configuration and design documentation files.
.cursor/rules/development-setup.mdc Simplified and shortened development standards; always applies now.
src/main/events.ts Added three new model config event constants to CONFIG_EVENTS.
src/main/presenter/configPresenter/index.ts Refactored to use new ModelConfigHelper; added batch status, config management, and event notification methods; removed direct exports of model settings.
src/main/presenter/configPresenter/modelConfig.ts Introduced ModelConfigHelper class for layered, persistent model config management with import/export and batch APIs.
src/main/presenter/syncPresenter/index.ts Included model-config.json in backup/import/restore logic for sync operations.
src/renderer/src/components/settings/ModelConfigDialog.vue Added a new Vue dialog component for editing and resetting model configurations.
src/renderer/src/components/settings/ModelConfigItem.vue Added settings button to open config dialog; emits configChanged event; passes provider ID.
src/renderer/src/components/settings/ProviderModelSettingsDetail.vue Handles config-changed event to reinitialize model data.
src/renderer/src/components/settings/ProviderModelList.vue Passes provider ID prop to model items; emits config-changed event.
src/renderer/src/components/settings/ProviderModelManager.vue Forwards config-changed event; passes provider ID prop.
src/renderer/src/i18n/en-US/settings.json, fa-IR/settings.json, fr-FR/settings.json, ja-JP/settings.json, ko-KR/settings.json, ru-RU/settings.json, zh-CN/settings.json, zh-HK/settings.json, zh-TW/settings.json Added comprehensive modelConfig localization objects for model config UI in all supported languages.
src/renderer/src/stores/settings.ts Optimized model status fetching to batch; added async model config get/set/reset methods.
src/renderer/src/views/WelcomeView.vue Passes provider ID prop to model config items in onboarding.
src/shared/model.ts Added ImageGeneration to ModelType enum.
src/shared/presenter.d.ts Extended IConfigPresenter with batch status and model config management methods.
test/main/presenter/modelConfig.test.ts Added comprehensive tests for ModelConfigHelper covering CRUD, priority, and edge cases.

Sequence Diagram(s)

sequenceDiagram
    participant User
    participant ModelConfigDialog (Vue)
    participant Settings Store
    participant ConfigPresenter (Main)
    participant ModelConfigHelper
    participant ElectronStore

    User->>ModelConfigDialog (Vue): Open dialog to edit model config
    ModelConfigDialog (Vue)->>Settings Store: getModelConfig(modelId, providerId)
    Settings Store->>ConfigPresenter (Main): getModelConfig(modelId, providerId)
    ConfigPresenter (Main)->>ModelConfigHelper: getModelConfig(modelId, providerId)
    ModelConfigHelper->>ElectronStore: Retrieve config (layered fallback)
    ElectronStore-->>ModelConfigHelper: Return config
    ModelConfigHelper-->>ConfigPresenter (Main): Return config
    ConfigPresenter (Main)-->>Settings Store: Return config
    Settings Store-->>ModelConfigDialog (Vue): Return config

    User->>ModelConfigDialog (Vue): Save changes
    ModelConfigDialog (Vue)->>Settings Store: setModelConfig(modelId, providerId, config)
    Settings Store->>ConfigPresenter (Main): setModelConfig(modelId, providerId, config)
    ConfigPresenter (Main)->>ModelConfigHelper: setModelConfig(modelId, providerId, config)
    ModelConfigHelper->>ElectronStore: Persist config
    ElectronStore-->>ModelConfigHelper: Ack
    ModelConfigHelper-->>ConfigPresenter (Main): Ack
    ConfigPresenter (Main)-->>Settings Store: Ack
    Settings Store-->>ModelConfigDialog (Vue): Ack
    ConfigPresenter (Main)->>All Renderers: Emit MODEL_CONFIG_CHANGED event
Loading

Poem

🐇
A hop, a skip, a config tweak,
Now models speak just how you seek!
With dialogs bright and settings clear,
Batch status comes, the lag will disappear.
Locales abound, from East to West—
This bunny thinks these changes are the best!

✨ Finishing Touches
  • 📝 Generate Docstrings

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
🪧 Tips

Chat

There are 3 ways to chat with CodeRabbit:

  • Review comments: Directly reply to a review comment made by CodeRabbit. Example:
    • I pushed a fix in commit <commit_id>, please review it.
    • Explain this complex logic.
    • Open a follow-up GitHub issue for this discussion.
  • Files and specific lines of code (under the "Files changed" tab): Tag @coderabbitai in a new review comment at the desired location with your query. Examples:
    • @coderabbitai explain this code block.
    • @coderabbitai modularize this function.
  • PR comments: Tag @coderabbitai in a new PR comment to ask questions about the PR branch. For the best results, please provide a very specific query, as very limited context is provided in this mode. Examples:
    • @coderabbitai gather interesting stats about this repository and render them as a table. Additionally, render a pie chart showing the language distribution in the codebase.
    • @coderabbitai read src/utils.ts and explain its main purpose.
    • @coderabbitai read the files in the src/scheduler package and generate a class diagram using mermaid and a README in the markdown format.
    • @coderabbitai help me debug CodeRabbit configuration file.

Support

Need help? Create a ticket on our support page for assistance with any issues or questions.

Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments.

CodeRabbit Commands (Invoked using PR comments)

  • @coderabbitai pause to pause the reviews on a PR.
  • @coderabbitai resume to resume the paused reviews.
  • @coderabbitai review to trigger an incremental review. This is useful when automatic reviews are disabled for the repository.
  • @coderabbitai full review to do a full review from scratch and review all the files again.
  • @coderabbitai summary to regenerate the summary of the PR.
  • @coderabbitai generate docstrings to generate docstrings for this PR.
  • @coderabbitai generate sequence diagram to generate a sequence diagram of the changes in this PR.
  • @coderabbitai resolve resolve all the CodeRabbit review comments.
  • @coderabbitai configuration to show the current CodeRabbit configuration for the repository.
  • @coderabbitai help to get help.

Other keywords and placeholders

  • Add @coderabbitai ignore anywhere in the PR description to prevent this PR from being reviewed.
  • Add @coderabbitai summary to generate the high-level summary at a specific location in the PR description.
  • Add @coderabbitai anywhere in the PR title to generate the title automatically.

CodeRabbit Configuration File (.coderabbit.yaml)

  • You can programmatically configure CodeRabbit by adding a .coderabbit.yaml file to the root of your repository.
  • Please see the configuration documentation for more information.
  • If your editor has YAML language server enabled, you can add the path at the top of this file to enable auto-completion and validation: # yaml-language-server: $schema=https://coderabbit.ai/integrations/schema.v2.json

Documentation and Community

  • Visit our Documentation for detailed information on how to use CodeRabbit.
  • Join our Discord Community to get help, request features, and share feedback.
  • Follow us on X/Twitter for updates and announcements.

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 8

♻️ Duplicate comments (3)
src/renderer/src/i18n/zh-TW/settings.json (1)

104-160: Same newline-escape caveat as zh-HK file.
See previous comment for details.

src/renderer/src/i18n/ru-RU/settings.json (1)

104-160: Same newline-escape caveat as zh-HK file.

src/renderer/src/i18n/fa-IR/settings.json (1)

104-160: Same newline-escape caveat as zh-HK file.

🧹 Nitpick comments (20)
src/shared/model.ts (1)

8-9: Add a trailing comma to avoid noisy future diffs

Every other enum member ends with a comma; keep the pattern for the last entry as well.

-  ImageGeneration = 'imageGeneration'
+  ImageGeneration = 'imageGeneration',
src/renderer/src/components/settings/ModelProviderSettingsDetail.vue (1)

348-352: Consider throttling initData() to prevent excessive refreshes

config-changed may fire repeatedly while a user tweaks multiple fields in the dialog.
Re-running initData() on every emission can cause unnecessary network / store calls.

-const handleConfigChanged = async () => {
-  await initData()
-}
+import { throttle } from 'lodash-es'
+
+const handleConfigChanged = throttle(async () => {
+  await initData()
+}, 500)

A 500 ms throttle (or debounce) keeps the UI in sync without spamming the store.

src/renderer/src/components/settings/ProviderModelList.vue (1)

111-119: Same note for provider models – duplication of inline arrow fn.

Nice symmetry with the custom-model block.
Minor: @enabled-change="(enabled) => handleModelEnabledChange(model, enabled)" plus the
new @config-changed arrow recreates two closures per row on every render.
If repaint frequency becomes a concern, consider hoisting those handlers
(handleConfigChanged) outside the template.

src/renderer/src/i18n/zh-HK/settings.json (1)

103-160: Large localisation block added – only one nitpick.

JSON includes explicit line-breaks (\n) within the resetConfirm.message.
If your i18n extraction tooling strips escapes, the newline may render as \n
instead of an actual break in some UI libraries.
Consider using a real line break or separate paragraphs.

src/renderer/src/i18n/en-US/settings.json (1)

125-133: Refine wording & drop superfluous repetition
Sentence reads a bit awkwardly (“The higher the higher the randomness.”). Consider tightening:

-        "description": "Control the randomness of the output. Most models are 0-1, and some support between 0-2. The higher the higher the randomness.",
+        "description": "Controls output randomness. Typical range is 0-1 (some models allow 0-2); higher values yield more random results.",
src/renderer/src/components/settings/ModelConfigItem.vue (2)

42-50: Great UX touch – settings button added

Button wiring, i18n title and icon all check out.
Small nitpick: lucide:settings icon colour could respond to hover: to match other action buttons.

-<Icon icon="lucide:settings" class="w-4 h-4 text-muted-foreground" />
+<Icon icon="lucide:settings" class="w-4 h-4 text-muted-foreground hover:text-foreground" />

104-114: Emit payload for clarity

configChanged currently emits no data. Passing at least the modelId (and maybe the updated config) helps parents update state without having to refetch.

-const onConfigSaved = () => {
-  emit('configChanged')
-}
+const onConfigSaved = (payload?: unknown) => {
+  emit('configChanged', { modelId, ...payload })
+}

You’ll then need to extend defineEmits accordingly.

src/main/presenter/syncPresenter/index.ts (7)

163-169: Remove unnecessary empty lines.

The empty lines break the flow of backup operations. Consider removing them to maintain consistency with the rest of the backup logic.

      // 备份模型配置文件
      if (fs.existsSync(this.MODEL_CONFIG_PATH)) {
        fs.copyFileSync(this.MODEL_CONFIG_PATH, tempModelConfigPath)
      }
-
-
-
      // 如果 provider_models 目录存在,备份整个目录

224-231: Remove unnecessary empty lines for consistency.

        // 导入模型配置文件
        if (fs.existsSync(modelConfigBackupPath)) {
          fs.copyFileSync(modelConfigBackupPath, this.MODEL_CONFIG_PATH)
        }
-
-
-
        eventBus.send(SYNC_EVENTS.IMPORT_COMPLETED, SendTarget.ALL_WINDOWS)

256-262: Remove unnecessary empty lines for consistency.

        // 恢复模型配置文件
        if (fs.existsSync(tempModelConfigPath)) {
          fs.copyFileSync(tempModelConfigPath, this.MODEL_CONFIG_PATH)
        }
-
-
-
        eventBus.send(SYNC_EVENTS.IMPORT_ERROR, SendTarget.ALL_WINDOWS, (error as Error).message || 'sync.error.unknown')

334-336: Remove unnecessary empty line.

      const finalMcpSettingsBackupPath = path.join(syncFolderPath, 'mcp-settings.json')
      const finalModelConfigBackupPath = path.join(syncFolderPath, 'model-config.json')
-

      // 确保数据库文件存在

370-382: Fix formatting issues.

Remove unnecessary empty lines and maintain consistent formatting.

      }
-
      // 备份 MCP 设置
      if (fs.existsSync(this.MCP_SETTINGS_PATH)) {
        fs.copyFileSync(this.MCP_SETTINGS_PATH, tempMcpSettingsBackupPath)
      }
-
      // 备份模型配置文件
      if (fs.existsSync(this.MODEL_CONFIG_PATH)) {
        fs.copyFileSync(this.MODEL_CONFIG_PATH, tempModelConfigBackupPath)
      }
-
-
-
      // 备份 provider_models 目录

399-403: Remove unnecessary empty line.

      if (!fs.existsSync(tempAppSettingsBackupPath)) {
        throw new Error('sync.error.tempConfigFailed')
      }
-
      if (!fs.existsSync(tempMcpSettingsBackupPath)) {
        throw new Error('sync.error.tempMcpSettingsFailed')
      }

422-436: Fix comment indentation and remove empty lines.

The comment indentation is incorrect and there are unnecessary empty lines.

      }

-            // 清理之前的模型配置文件备份
+      // 清理之前的模型配置文件备份
      if (fs.existsSync(finalModelConfigBackupPath)) {
        fs.unlinkSync(finalModelConfigBackupPath)
      }
-
      // 确保临时文件存在后再执行重命名
      fs.renameSync(tempDbBackupPath, finalDbBackupPath)
      fs.renameSync(tempAppSettingsBackupPath, finalAppSettingsBackupPath)
      fs.renameSync(tempMcpSettingsBackupPath, finalMcpSettingsBackupPath)
-
-            // 重命名模型配置文件
+      // 重命名模型配置文件
      if (fs.existsSync(tempModelConfigBackupPath)) {
        fs.renameSync(tempModelConfigBackupPath, finalModelConfigBackupPath)
      }
-
      // 重命名 provider_models 临时目录
src/renderer/src/components/settings/ModelConfigDialog.vue (3)

13-31: Consider making max token limits configurable per model.

The hardcoded maximum value of 1,000,000 tokens may not be appropriate for all models. Some models support much higher limits (e.g., 100M tokens for certain context windows), while others have lower limits.

Consider deriving these limits from the model's actual capabilities or making them configurable per provider/model.


217-226: Consider loading model-specific defaults.

The hardcoded default values may not be appropriate for all models. Different models have different optimal defaults (e.g., embedding models don't need high maxTokens).

Consider initializing these values based on the model type or loading them from the model's default configuration:

 // 配置数据
-const config = ref<ModelConfig>({
-  maxTokens: 4096,
-  contextLength: 8192,
-  temperature: 0.7,
-  vision: false,
-  functionCall: false,
-  reasoning: false,
-  type: ModelType.Chat
-})
+const config = ref<ModelConfig>({
+  maxTokens: 0,
+  contextLength: 0,
+  temperature: 0,
+  vision: false,
+  functionCall: false,
+  reasoning: false,
+  type: ModelType.Chat
+})
+
+// Load defaults in loadConfig() based on model type

308-319: Potential duplicate config loading.

The watch with immediate: true and the onMounted hook both call loadConfig() when the dialog opens, which could cause redundant API calls.

 // 监听props变化,重新加载配置
 watch(() => [props.modelId, props.providerId, props.open], () => {
   if (props.open) {
     loadConfig()
   }
-}, { immediate: true })
+})

 onMounted(() => {
   if (props.open) {
     loadConfig()
   }
 })
test/main/presenter/modelConfig.test.ts (1)

197-198: Remove debug console.log statements.

Debug logging should be removed from test files.

-      console.log('Default config maxTokens:', defaultConfig.maxTokens)
-      console.log('Provider config maxTokens:', providerConfig.maxTokens)
src/renderer/src/stores/settings.ts (1)

1391-1409: Good implementation of model config methods. Remove empty lines.

The model configuration methods are well-implemented and properly refresh model data after changes.

   const resetModelConfig = async (modelId: string, providerId: string): Promise<void> => {
     await configP.resetModelConfig(modelId, providerId)
     // 配置重置后刷新相关模型数据
     await refreshProviderModels(providerId)
   }
-
-
-
   return {
src/main/presenter/configPresenter/modelConfig.ts (1)

13-13: Consider using a more unique delimiter to avoid potential conflicts

The delimiter -_- could potentially exist in provider or model IDs. Consider using a more unique separator like :: or |:| that is less likely to appear in IDs.

-const SPECIAL_CONCAT_CHAR = '-_-'
+const SPECIAL_CONCAT_CHAR = '|:|'
📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 8e4807a and 0fa7eb1.

📒 Files selected for processing (26)
  • .cursor/rules/build-deploy.mdc (0 hunks)
  • .cursor/rules/development-setup.mdc (1 hunks)
  • docs/config-presenter-design.md (0 hunks)
  • src/main/events.ts (1 hunks)
  • src/main/presenter/configPresenter/index.ts (6 hunks)
  • src/main/presenter/configPresenter/modelConfig.ts (1 hunks)
  • src/main/presenter/syncPresenter/index.ts (11 hunks)
  • src/renderer/src/components/settings/ModelConfigDialog.vue (1 hunks)
  • src/renderer/src/components/settings/ModelConfigItem.vue (3 hunks)
  • src/renderer/src/components/settings/ModelProviderSettingsDetail.vue (2 hunks)
  • src/renderer/src/components/settings/ProviderModelList.vue (4 hunks)
  • src/renderer/src/components/settings/ProviderModelManager.vue (2 hunks)
  • src/renderer/src/i18n/en-US/settings.json (1 hunks)
  • src/renderer/src/i18n/fa-IR/settings.json (1 hunks)
  • src/renderer/src/i18n/fr-FR/settings.json (1 hunks)
  • src/renderer/src/i18n/ja-JP/settings.json (1 hunks)
  • src/renderer/src/i18n/ko-KR/settings.json (1 hunks)
  • src/renderer/src/i18n/ru-RU/settings.json (1 hunks)
  • src/renderer/src/i18n/zh-CN/settings.json (1 hunks)
  • src/renderer/src/i18n/zh-HK/settings.json (1 hunks)
  • src/renderer/src/i18n/zh-TW/settings.json (1 hunks)
  • src/renderer/src/stores/settings.ts (4 hunks)
  • src/renderer/src/views/WelcomeView.vue (1 hunks)
  • src/shared/model.ts (1 hunks)
  • src/shared/presenter.d.ts (2 hunks)
  • test/main/presenter/modelConfig.test.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • .cursor/rules/build-deploy.mdc
  • docs/config-presenter-design.md
🧰 Additional context used
🧬 Code Graph Analysis (1)
src/main/presenter/configPresenter/modelConfig.ts (3)
src/shared/presenter.d.ts (1)
  • ModelConfig (120-128)
src/main/presenter/configPresenter/providerModelSettings.ts (1)
  • getProviderSpecificModelConfig (1115-1146)
src/main/presenter/configPresenter/modelDefaultSettings.ts (1)
  • defaultModelsSettings (3-1669)
⏰ Context from checks skipped due to timeout of 90000ms (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (20)
.cursor/rules/development-setup.mdc (1)

4-4: Clarify alwaysApply scope
The globs: list is currently empty, so turning on alwaysApply: true may either match all files or none, depending on the engine’s defaults. Please specify the intended file patterns under globs: or confirm that a global application is desired.

src/main/events.ts (1)

27-30: Event-key naming fits conventions – ensure counterpart wiring exists

The new constants follow the existing config:* naming style and look collision-free.
Just double-check that renderer processes (and any IPC listeners) have matching handlers, otherwise the events will never be observed.

src/renderer/src/views/WelcomeView.vue (1)

366-367: Prop binding is correct — good use of template ref unwrapping

Passing :provider-id="selectedProvider" cleanly supplies context to each ModelConfigItem. No issues spotted.

src/renderer/src/components/settings/ModelProviderSettingsDetail.vue (1)

41-42: Bubble-up listener added — OK

Listening for config-changed and delegating to a local handler plugs the new config workflow into this view.

src/renderer/src/components/settings/ProviderModelManager.vue (3)

39-40: Prop passthrough verified

Forwarding provider.id ensures downstream items know which provider they belong to.


47-48: Event forwarding keeps parent in sync

Re-emitting config-changed upward is the right move and keeps component boundaries thin.


74-75: Emit declaration updated — good

Adding 'config-changed' to defineEmits avoids TS complaints and documents the contract.

src/renderer/src/components/settings/ProviderModelList.vue (2)

14-24: Propagating provider-id and config-changed looks correct – verify downstream usage.

Passing the new provider-id prop and re-emitting config-changed will only work if
ModelConfigItem.vue declares the prop and the event exactly as used here.
Please double-check its prop name / casing and the emitted event string to prevent
runtime warnings such as “extraneous non-prop attributes” or “event not declared”.


161-164: defineEmits signature is type-safe – LGTM.

Literal key 'config-changed' keeps TS happy and prevents typos. No issues spotted.

src/renderer/src/i18n/ja-JP/settings.json (1)

103-160: Localization additions look good

All new keys follow the existing naming pattern and placeholders ({name} etc.) are preserved. Nice work keeping the Japanese copy concise and consistent.

src/renderer/src/i18n/zh-CN/settings.json (1)

103-160: Strings are consistent & complete

No placeholder or punctuation inconsistencies spotted. 👍

src/renderer/src/i18n/fr-FR/settings.json (1)

103-160: French translations LGTM

Keys mirror the English reference and diacritics / plural forms look correct.

src/renderer/src/i18n/en-US/settings.json (1)

124-126: Avoid embedded newline in translation string

\\n inside JSON may render as literal \n in some UI libraries. Prefer \\n\\n (double newline) or split string into two i18n entries and handle line-break in the template.

src/main/presenter/syncPresenter/index.ts (1)

28-28: LGTM!

The MODEL_CONFIG_PATH declaration follows the established pattern for other configuration file paths.

src/renderer/src/i18n/ko-KR/settings.json (1)

103-160: LGTM!

The Korean translations for the model configuration UI are comprehensive and well-structured, covering all necessary labels, descriptions, validation messages, and UI elements.

test/main/presenter/modelConfig.test.ts (1)

6-52: Well-implemented mock for electron-store.

The in-memory mock correctly simulates electron-store behavior and properly isolates tests from file system dependencies.

src/renderer/src/stores/settings.ts (1)

336-348: Good optimization with batch status fetching.

The implementation efficiently fetches all model statuses in a single call and properly handles the empty array case.

src/main/presenter/configPresenter/index.ts (3)

310-320: Good optimization with batch status retrieval

The implementation of getBatchModelStatus provides an efficient way to retrieve multiple model statuses in a single operation, avoiding repeated method calls.


411-413: Excellent use of batch retrieval for performance

Great job using the new getBatchModelStatus method to optimize the getAllEnabledModels method. This reduces the number of individual status lookups significantly.


822-826:

Details

✅ Verification successful

Verify that MODEL_CONFIG_CHANGED event is defined

The code emits a MODEL_CONFIG_CHANGED event. Please ensure this event constant is properly defined in the events file.


🏁 Script executed:

#!/bin/bash
# Check if MODEL_CONFIG_CHANGED is defined in events.ts
rg "MODEL_CONFIG_CHANGED" src/main/events.ts -A 2 -B 2

Length of output: 380


No action needed: MODEL_CONFIG_CHANGED is defined

A quick search confirms that MODEL_CONFIG_CHANGED is declared in src/main/events.ts:

// 模型配置相关事件
MODEL_CONFIG_CHANGED: 'config:model-config-changed', // 模型配置变更事件

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🔭 Outside diff range comments (1)
src/main/presenter/llmProviderPresenter/providers/siliconcloudProvider.ts (1)

123-129: 🛠️ Refactor suggestion

parseFloat may yield NaN, bypassing the exhausted-quota guard

If totalBalance is "--" or any non-numeric string, parseFloat returns NaN.
Later you compare with <= 0, but NaN <= 0 is false, so an unusable key would be treated as valid. Consider guarding for Number.isNaN:

const totalBalance = parseFloat(keyResponse.data.totalBalance)

-if (keyStatus.remainNum !== undefined && keyStatus.remainNum <= 0) {
+if (
+  keyStatus.remainNum === undefined ||
+  Number.isNaN(keyStatus.remainNum) ||
+  keyStatus.remainNum <= 0
+) {

This prevents silent acceptance of malformed balances.

♻️ Duplicate comments (1)
src/main/presenter/configPresenter/index.ts (1)

859-896: 🛠️ Refactor suggestion

Replace any with the correct interface (issue previously raised)

The return/parameter types still use Record<string, any> despite earlier feedback.
Switch to Record<string, IModelConfig> (or ModelConfig) to regain type safety.

-importModelConfigs(configs: Record<string, any>, overwrite: boolean = false): void {
+importModelConfigs(configs: Record<string, IModelConfig>, overwrite: boolean = false): void {

-exportModelConfigs(): Record<string, any> {
+exportModelConfigs(): Record<string, IModelConfig> {

-getAllModelConfigs(): Record<string, any> {
+getAllModelConfigs(): Record<string, IModelConfig> {

Remember to:

  1. import { IModelConfig } from './modelConfig' (or from the correct module).
  2. Update ModelConfigHelper API signatures accordingly.

This change prevents accidental misuse and improves autocompletion in consumers.

🧹 Nitpick comments (10)
src/main/presenter/llmProviderPresenter/providers/siliconcloudProvider.ts (2)

105-107: Keep header-key quoting consistent & drop redundant Content-Type on GET

Mixing quoted and unquoted object keys is perfectly legal, but it creates an inconsistent style within the same literal (Authorization is now unquoted while Content-Type remains quoted). Either quote both or neither to avoid diff-noise in future style passes.

Additionally, Content-Type: application/json is unnecessary on a GET request with no body—some servers even treat it as a hint that a body is coming. Consider removing it altogether:

-      headers: {
-        Authorization: `Bearer ${this.provider.apiKey}`,
-        'Content-Type': 'application/json'
-      }
+      headers: {
+        Authorization: `Bearer ${this.provider.apiKey}`
+      }

112-114: Multi-line template string embeds hard newlines in the error message

The new formatting improves readability in code, but it injects \n characters into the thrown string, producing a multi-line error that can be awkward in logs or UI surfaces.
If single-line output is preferred, wrap the template or join parts explicitly:

-      throw new Error(
-        `SiliconCloud API key check failed: ${response.status} ${response.statusText} - ${errorText}`
-      )
+      throw new Error(
+        `SiliconCloud API key check failed: ${response.status} ${response.statusText} - ${errorText}`
+      )

(or use [].join(' ')).

Purely stylistic—feel free to keep as-is if multi-line output is desired.

src/main/presenter/llmProviderPresenter/providers/ppioProvider.ts (1)

87-90: Consider formatting the currency amount for readability

'¥' + keyResponse.credit_balance / 10000 returns long floating-point values such as ¥123.456789. A small formatting tweak improves UX:

-const remaining = '¥' + keyResponse.credit_balance / 10000
+const remaining = `¥${(keyResponse.credit_balance / 10000).toFixed(2)}`

If fractions of a cent matter, adjust precision accordingly.

test/main/presenter/llmProviderPresenter.test.ts (1)

403-448: Concurrent-stream test still runs streams sequentially

The loop awaits each generator before moving to the next, so at most one stream is active.
To exercise the concurrency limiter, start the handlers without awaiting them and await Promise.all later:

-      for (const { eventId, stream } of streams) {
-        try {
-          let count = 0
-          for await (const event of stream) {
-            ...
-          }
-        } catch (error) {
-          // 预期的错误
-        }
-      }
+      await Promise.all(
+        streams.map(async ({ eventId, stream }) => {
+          try {
+            let count = 0
+            for await (const event of stream) {
+              ...
+              if (count >= 2) {
+                await llmProviderPresenter.stopStream(eventId)
+                break
+              }
+            }
+          } catch {
+            /* expected */ 
+          }
+        })
+      )

This keeps the three streams truly concurrent, giving the limiter a chance to reject the third one.

src/main/presenter/configPresenter/index.ts (2)

310-320: getBatchModelStatus does unnecessary store look-ups; consider bulk access

The method loops over modelIds and calls this.getSetting for every single model key.
Because ElectronStore persists data synchronously, this results in as many synchronous disk reads as there are models. Fetching all keys once (e.g. this.store.store) and resolving the status in memory would reduce I/O overhead, especially for providers that expose dozens of models.

-    for (const modelId of modelIds) {
-      const statusKey = this.getModelStatusKey(providerId, modelId)
-      const status = this.getSetting<boolean>(statusKey)
-      result[modelId] = typeof status === 'boolean' ? status : true
-    }
+    const cache = this.store.store                 // in-memory snapshot
+    for (const modelId of modelIds) {
+      const statusKey = this.getModelStatusKey(providerId, modelId)
+      const raw      = cache[statusKey] as unknown
+      result[modelId] = typeof raw === 'boolean' ? raw : true
+    }

417-424: Minor: keep modelStatusMap creation outside map callback

getAllEnabledModels assembles a modelIds array only to call getBatchModelStatus, which in turn rebuilds the keys.
Pre-computing the map once per provider is fine, but placing it outside the map callback would avoid creating a large temporary array (modelIds) altogether.

test/main/eventbus/eventbus.test.ts (1)

186-188: Prefer fake timers over setTimeout for faster, deterministic tests

Using real timers (await new Promise((resolve) => setTimeout(resolve, 0))) slows the suite and can introduce flakiness on CI.
Switching to Vitest’s fake-timer utilities makes the flush instant and deterministic:

-beforeEach(() => {
-  vi.useFakeTimers()
-})
-
-// …
-await new Promise((resolve) => setTimeout(resolve, 0))
+await vi.runOnlyPendingTimersAsync()

Remember to pair vi.useFakeTimers() in the relevant beforeEach/afterEach.

Also applies to: 200-202, 214-216, 281-283, 302-304, 319-321

test/main/presenter/modelConfig.test.ts (3)

197-199: Remove noisy console.log statements from tests

These logs clutter CI output and offer no assertion value.
Simply delete them or convert to comments.

-      console.log('Default config maxTokens:', defaultConfig.maxTokens)
-      console.log('Provider config maxTokens:', providerConfig.maxTokens)

118-127: Hard-coding default numbers couples tests to implementation details

Asserting exact defaults (4096, 8192, etc.) makes the suite brittle when defaults legitimately change.
Prefer semantic checks:

-      expect(defaultConfig).toEqual({
-        maxTokens: 4096,
-        contextLength: 8192,
-
-      })
+      expect(defaultConfig.maxTokens).toBeGreaterThan(0)
+      expect(defaultConfig.contextLength).toBeGreaterThan(defaultConfig.maxTokens)
+      // retain specific assertions only when they convey behaviour rather than constants

60-67: { ...value } is a shallow copy – consider deep-cloning for safety

If any stored config objects gain nested structures, mutations inside a test could leak to other tests despite the copy.
Using structuredClone (Node 18+) or JSON.parse(JSON.stringify(value)) ensures isolation.

📜 Review details

Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 0fa7eb1 and f4cc34b.

📒 Files selected for processing (40)
  • .prettierignore (1 hunks)
  • src/main/presenter/configPresenter/index.ts (6 hunks)
  • src/main/presenter/configPresenter/modelConfig.ts (1 hunks)
  • src/main/presenter/llmProviderPresenter/index.ts (2 hunks)
  • src/main/presenter/llmProviderPresenter/oauthHelper.ts (1 hunks)
  • src/main/presenter/llmProviderPresenter/providers/anthropicProvider.ts (3 hunks)
  • src/main/presenter/llmProviderPresenter/providers/deepseekProvider.ts (2 hunks)
  • src/main/presenter/llmProviderPresenter/providers/githubCopilotProvider.ts (0 hunks)
  • src/main/presenter/llmProviderPresenter/providers/openAIResponsesProvider.ts (1 hunks)
  • src/main/presenter/llmProviderPresenter/providers/openRouterProvider.ts (4 hunks)
  • src/main/presenter/llmProviderPresenter/providers/ppioProvider.ts (1 hunks)
  • src/main/presenter/llmProviderPresenter/providers/siliconcloudProvider.ts (1 hunks)
  • src/main/presenter/mcpPresenter/inMemoryServers/customPromptsServer.ts (1 hunks)
  • src/main/presenter/mcpPresenter/index.ts (1 hunks)
  • src/main/presenter/mcpPresenter/mcpClient.ts (1 hunks)
  • src/main/presenter/notifactionPresenter.ts (1 hunks)
  • src/main/presenter/shortcutPresenter.ts (1 hunks)
  • src/main/presenter/syncPresenter/index.ts (13 hunks)
  • src/main/presenter/tabPresenter.ts (1 hunks)
  • src/main/presenter/upgradePresenter/index.ts (4 hunks)
  • src/main/presenter/windowPresenter/index.ts (0 hunks)
  • src/renderer/src/assets/style.css (2 hunks)
  • src/renderer/src/components/editor/mention/MentionList.vue (4 hunks)
  • src/renderer/src/components/editor/mention/PromptParamsDialog.vue (1 hunks)
  • src/renderer/src/components/editor/mention/suggestion.ts (1 hunks)
  • src/renderer/src/components/settings/ModelConfigDialog.vue (1 hunks)
  • src/renderer/src/components/settings/PromptSetting.vue (7 hunks)
  • src/renderer/src/components/settings/ProviderApiConfig.vue (3 hunks)
  • src/renderer/src/i18n/en-US/contextMenu.json (1 hunks)
  • src/renderer/src/i18n/fr-FR/contextMenu.json (1 hunks)
  • src/renderer/src/i18n/fr-FR/index.ts (1 hunks)
  • src/renderer/src/i18n/ja-JP/contextMenu.json (1 hunks)
  • src/renderer/src/i18n/ko-KR/contextMenu.json (1 hunks)
  • src/renderer/src/i18n/zh-CN/contextMenu.json (1 hunks)
  • src/renderer/src/i18n/zh-TW/contextMenu.json (1 hunks)
  • src/renderer/src/stores/settings.ts (4 hunks)
  • test/main/eventbus/eventbus.test.ts (12 hunks)
  • test/main/presenter/llmProviderPresenter.test.ts (12 hunks)
  • test/main/presenter/modelConfig.test.ts (1 hunks)
  • test/setup.renderer.ts (1 hunks)
💤 Files with no reviewable changes (2)
  • src/main/presenter/windowPresenter/index.ts
  • src/main/presenter/llmProviderPresenter/providers/githubCopilotProvider.ts
✅ Files skipped from review due to trivial changes (27)
  • src/renderer/src/i18n/zh-TW/contextMenu.json
  • src/main/presenter/shortcutPresenter.ts
  • src/renderer/src/i18n/en-US/contextMenu.json
  • src/renderer/src/i18n/ko-KR/contextMenu.json
  • src/renderer/src/i18n/fr-FR/contextMenu.json
  • src/renderer/src/i18n/ja-JP/contextMenu.json
  • test/setup.renderer.ts
  • src/renderer/src/i18n/zh-CN/contextMenu.json
  • src/renderer/src/components/editor/mention/PromptParamsDialog.vue
  • src/main/presenter/notifactionPresenter.ts
  • src/main/presenter/llmProviderPresenter/oauthHelper.ts
  • src/renderer/src/components/editor/mention/suggestion.ts
  • src/renderer/src/i18n/fr-FR/index.ts
  • src/main/presenter/llmProviderPresenter/providers/openAIResponsesProvider.ts
  • src/main/presenter/mcpPresenter/mcpClient.ts
  • src/main/presenter/mcpPresenter/inMemoryServers/customPromptsServer.ts
  • src/main/presenter/llmProviderPresenter/providers/deepseekProvider.ts
  • src/renderer/src/assets/style.css
  • src/main/presenter/mcpPresenter/index.ts
  • src/renderer/src/components/editor/mention/MentionList.vue
  • src/renderer/src/components/settings/ProviderApiConfig.vue
  • .prettierignore
  • src/main/presenter/upgradePresenter/index.ts
  • src/main/presenter/llmProviderPresenter/index.ts
  • src/renderer/src/components/settings/PromptSetting.vue
  • src/main/presenter/llmProviderPresenter/providers/openRouterProvider.ts
  • src/main/presenter/llmProviderPresenter/providers/anthropicProvider.ts
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/renderer/src/components/settings/ModelConfigDialog.vue
  • src/main/presenter/configPresenter/modelConfig.ts
  • src/main/presenter/syncPresenter/index.ts
  • src/renderer/src/stores/settings.ts
🧰 Additional context used
🧬 Code Graph Analysis (2)
src/main/presenter/tabPresenter.ts (2)
src/main/eventbus.ts (1)
  • eventBus (151-151)
src/main/events.ts (1)
  • WINDOW_EVENTS (67-85)
test/main/presenter/llmProviderPresenter.test.ts (3)
src/main/presenter/configPresenter/index.ts (1)
  • ConfigPresenter (72-1018)
src/main/presenter/llmProviderPresenter/index.ts (1)
  • LLMProviderPresenter (50-1051)
src/shared/presenter.d.ts (2)
  • ChatMessage (1057-1069)
  • LLMAgentEvent (1105-1108)
🔇 Additional comments (2)
src/main/presenter/tabPresenter.ts (1)

54-56: Change is purely stylistic – good to merge

Only splits the inline handler into multiple lines; no functional impact.

test/main/eventbus/eventbus.test.ts (1)

71-71: Assertions look good

The argument ordering and expectations accurately reflect the API contract after the formatting tweaks. No further action required.

Also applies to: 99-100, 108-109, 117-118, 152-153, 161-162

Copy link
Contributor

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

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

Actionable comments posted: 0

🧹 Nitpick comments (2)
.cursor/rules/development-setup.mdc (2)

7-11: Add quick install hints for OxLint & pnpm

The list states the required tools/versions but omits how to get them. A one-liner per item improves first-time onboarding, e.g.:

- 使用 OxLint 进行代码检查(`npm i -g @oxlint/cli`- 使用 pnpm 包管理(启用 corepack: `corepack enable`,然后 `corepack prepare pnpm@9 --activate`

This tiny addition saves contributors a round-trip to external docs.


13-26: Group dev commands in a fenced block & include tests

Inline back-ticked commands are harder to copy en masse. Present them as a single shell block and add the missing test script for completeness:

```bash
pnpm install       # install deps
pnpm lint          # lint with OxLint
pnpm format        # run Prettier
pnpm dev           # start electron/vue dev server
pnpm test          # run unit & e2e tests
pnpm build         # build production bundles

Makes the section copy-paste friendly and surfaces the testing workflow.

</blockquote></details>

</blockquote></details>

<details>
<summary>📜 Review details</summary>

**Configuration used: CodeRabbit UI**
**Review profile: CHILL**
**Plan: Pro**


<details>
<summary>📥 Commits</summary>

Reviewing files that changed from the base of the PR and between f4cc34b6b2d1e1315543bb1261b518e3731be580 and 438fd3d3186be15d358c0e9edbff10c564b49714.

</details>

<details>
<summary>📒 Files selected for processing (5)</summary>

* `.cursor/rules/development-setup.mdc` (1 hunks)
* `src/main/presenter/configPresenter/index.ts` (7 hunks)
* `src/main/presenter/configPresenter/modelConfig.ts` (1 hunks)
* `src/renderer/src/components/settings/ModelConfigDialog.vue` (1 hunks)
* `src/shared/presenter.d.ts` (3 hunks)

</details>

<details>
<summary>🚧 Files skipped from review as they are similar to previous changes (4)</summary>

* src/renderer/src/components/settings/ModelConfigDialog.vue
* src/main/presenter/configPresenter/modelConfig.ts
* src/main/presenter/configPresenter/index.ts
* src/shared/presenter.d.ts

</details>

<details>
<summary>🔇 Additional comments (1)</summary><blockquote>

<details>
<summary>.cursor/rules/development-setup.mdc (1)</summary>

`4-4`: **Confirm `alwaysApply: true` is intentional**

Setting `alwaysApply` to `true` forces this rule on every project clone, including forks and CI jobs.  
Double-check that such global scope is desired; otherwise keep it `false` and let teams opt-in.

</details>

</blockquote></details>

</details>

<!-- This is an auto-generated comment by CodeRabbit for review status -->

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