Skip to content

Conversation

@Dw9
Copy link
Contributor

@Dw9 Dw9 commented Oct 23, 2025

Add custom providers of the ollama type to support multiple ollama server.

Summary by CodeRabbit

  • New Features

    • Per-provider Ollama model management with independent model lists and pull progress.
    • Delete custom Ollama providers with confirmation dialog.
  • Improvements

    • Ollama selectable as a provider; Groq removed from options.
    • API key optional for Ollama with auto-filled default host when selected.
    • Icon resolution improved to better match provider types.

@coderabbitai
Copy link
Contributor

coderabbitai bot commented Oct 23, 2025

Walkthrough

Refactors Ollama-related APIs to be provider-scoped by adding a required providerId to presenter/store methods, migrates Ollama model state to per-provider records, updates UI components to pass providerId, and propagates provider-scoped progress/events during model pulls.

Changes

Cohort / File(s) Summary
Presenter: Ollama provider methods
src/main/presenter/llmProviderPresenter/index.ts
getOllamaProviderInstance(providerId) added/updated to resolve and type-check provider instances; Ollama methods now require providerId and fetch provider via that helper; non-Ollama or missing providers handled with warnings/empty results or errors.
Presenter typings
src/shared/types/presenters/legacy.presenters.d.ts, src/shared/types/presenters/llmprovider.presenter.d.ts
ILlmProviderPresenter signatures changed to provider-scoped variants: listOllamaModels(providerId), showOllamaModelInfo(providerId, modelName), listOllamaRunningModels(providerId), pullOllamaModels(providerId, modelName).
Renderer store: settings (Ollama state)
src/renderer/src/stores/settings.ts
Migrated Ollama state to per-provider keyed records (ollamaRunningModels, ollamaLocalModels, ollamaPullingModels) and added provider-scoped accessors/mutators (get*, set*, updateOllamaPullingProgress, clearOllamaProviderData). refreshOllamaModels and pullOllamaModel now require providerId; event handling updated to validate and apply provider-scoped progress.
Ollama provider settings UI
src/renderer/settings/components/OllamaProviderSettingsDetail.vue
Added delete-provider button + confirmation dialog; all Ollama model operations and computed state now call store methods with providerId; pullModel, refreshModels, and local-model checks updated to be provider-scoped; delete flow calls removeProvider(providerId) and closes dialog.
Add custom provider dialog
src/renderer/settings/components/AddCustomProviderDialog.vue
Re-enabled Ollama as selectable option (Groq commented out). apiKey made conditionally required (not required for apiType === 'ollama'). Watcher added: switching to ollama sets default baseUrl (http://localhost:11434) and clears apiKey; switching away clears default baseUrl.
Welcome flow
src/renderer/src/views/WelcomeView.vue
nextStep() adjusted to resolve selected provider and skip apiKey/baseUrl validation when provider apiType === 'ollama'.
Model icon resolution
src/renderer/src/components/icons/ModelIcon.vue
Icon lookup enhanced to consider provider apiType as fallback; inversion logic simplified to keyword-based matching using provider.apiType and modelId.

Sequence Diagram(s)

sequenceDiagram
  participant UI as Renderer UI / Component
  participant Store as Settings Store
  participant Presenter as LlmProviderPresenter
  participant Provider as Provider Instance (Ollama)
  UI->>Store: request refresh/pull (providerId, modelName)
  Store->>Presenter: call refreshOllamaModels(providerId) / pullOllamaModel(providerId, modelName)
  Presenter->>Presenter: getOllamaProviderInstance(providerId)
  Presenter->>Provider: perform list/pull/show actions
  Provider-->>Presenter: results / emit PULL_MODEL_PROGRESS(providerId, modelName, progress)
  Presenter->>Store: forward events/results (providerId-scoped)
  Store->>UI: update reactive per-provider state (running/local/pulling)
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested labels

codex

Suggested reviewers

  • zerob13

Poem

🐰 A rabbit hops through provider lands,
Holding a providerId in both paws,
Ollama models lined up in neat bands,
Each pull reports progress with no flaws,
Per-provider gardens sprout applause! 🌱

Pre-merge checks and finishing touches

❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 0.00% which is insufficient. The required threshold is 80.00%. You can run @coderabbitai generate docstrings to improve docstring coverage.
✅ Passed checks (2 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title Check ✅ Passed The PR title "feat(ollama): support custom providers" directly aligns with the primary objective stated in the PR description: "Add custom providers of the ollama type to support multiple ollama server." The changes across all modified files consistently implement this feature by refactoring Ollama operations to be provider-scoped with providerId parameters, updating data structures to track per-provider state, and introducing UI components for adding and managing custom providers. The title is concise, specific, and clearly communicates the main feature being added without unnecessary noise or vagueness.
✨ Finishing touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment

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

❤️ Share

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

@Dw9
Copy link
Contributor Author

Dw9 commented Oct 23, 2025

image

@zerob13
Copy link
Collaborator

zerob13 commented Oct 23, 2025

@codex review

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

Caution

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

⚠️ Outside diff range comments (9)
src/renderer/src/views/WelcomeView.vue (3)

292-299: Use apiType, not provider ID, to control API-key visibility and URL hint

v-if="selectedProvider !== 'gemini'" and v-show="selectedProvider !== 'ollama'" compare IDs, breaking for custom providers (IDs won’t equal 'ollama'/'gemini'). Derive booleans from the selected provider’s apiType.

- <div v-if="selectedProvider !== 'gemini'" class="text-xs text-muted-foreground">
+ <div v-if="isOpenAICompatible" class="text-xs text-muted-foreground">
    {{ `${baseUrl ?? ''}/chat/completions` }}
  </div>

- <div v-show="selectedProvider !== 'ollama'" class="flex flex-col gap-2">
+ <div v-show="!isOllamaApi" class="flex flex-col gap-2">

And add:

+const currentProvider = computed(() =>
+  settingsStore.providers.find(p => p.id === selectedProvider.value)
+)
+const isOllamaApi = computed(() => currentProvider.value?.apiType === 'ollama')
+const isOpenAICompatible = computed(() =>
+  ['openai', 'openai-compatible', 'openai-responses'].includes(currentProvider.value?.apiType ?? '')
+)

As per coding guidelines.


167-175: Guard router push when no models are available to prevent runtime crash

providerModels.value[0] may be undefined (e.g., failed fetch). Add a safe fallback.

- router.push({
-   name: 'chat',
-   query: { modelId: providerModels.value[0].id, providerId: selectedProvider.value }
- })
+ const firstModel = providerModels.value[0]
+ if (!firstModel) {
+   showErrorDialog.value = true
+   dialogMessage.value = t('settings.provider.dialog.verify.missingModels')
+   return
+ }
+ router.push({ name: 'chat', query: { modelId: firstModel.id, providerId: selectedProvider.value } })

265-268: Replace hardcoded user-visible strings with i18n keys

  • Select placeholder, API URL/API Key placeholders, and “同步模型失败...” should use t(...) per renderer i18n rules.

Example:

- <SelectValue placeholder="Select a provider" />
+ <SelectValue :placeholder="t('welcome.provider.selectPlaceholder')" />

- <Input id="api-url" v-model="baseUrl" placeholder="Enter API URL" />
+ <Input id="api-url" v-model="baseUrl" :placeholder="t('welcome.provider.apiUrlPlaceholder')" />

- placeholder="Enter API Key"
+ :placeholder="t('welcome.provider.apiKeyPlaceholder')"

- <div v-if="!providerModelLoading && providerModels.length <= 0">同步模型失败...</div>
+ <div v-if="!providerModelLoading && providerModels.length <= 0">
+   {{ t('welcome.provider.syncModelsFailed') }}
+ </div>

As per coding guidelines.

Also applies to: 292-293, 305-306, 381-381

src/shared/types/presenters/legacy.presenters.d.ts (1)

1119-1121: Fix typo in TypeScript type definition

env: Record<string, unknow> contains an invalid type. Change unknow to unknown.

-  env: Record<string, unknow>
+  env: Record<string, unknown>
src/renderer/settings/components/OllamaProviderSettingsDetail.vue (1)

916-934: Replace Chinese console logs with English messages

Lines 919 and 925 contain Chinese console logs ('验证成功', '验证失败') that violate the coding guideline requiring English for all logs and comments in renderer files. Update to English equivalents:

console.log('Validation successful')
console.log('Validation failed', resp.errorMsg)
src/main/presenter/llmProviderPresenter/index.ts (1)

1647-1686: I need to examine how callers actually handle the return values and error cases to understand the impact of this inconsistency.

Now let me examine the actual call site behavior in settings.ts to confirm the inconsistency is problematic:

Make error handling consistent across Ollama methods

The review is correct. Currently listOllamaModels and listOllamaRunningModels return empty arrays on missing provider, while showOllamaModelInfo and pullOllamaModels throw errors. This inconsistency is problematic because:

  1. Promise.all() immediately rejects when any promise rejects, so callers using Promise.all() (as in settings.ts lines 1261-1262) cannot uniformly handle missing providers
  2. The two patterns prevent consistent error handling—some failures silently return [] while others trigger catch blocks
  3. Per coding guidelines, error-prone operations should uniformly throw, not mix return-empty-array patterns with exceptions

Change all four methods to throw on missing provider:

  listOllamaModels(providerId: string): Promise<OllamaModel[]> {
    const provider = this.getOllamaProviderInstance(providerId)
    if (!provider) {
-     return Promise.resolve([])
+     throw new Error('Ollama provider not found')
    }
    return provider.listModels()
  }
  
  listOllamaRunningModels(providerId: string): Promise<OllamaModel[]> {
    const provider = this.getOllamaProviderInstance(providerId)
    if (!provider) {
-     return Promise.resolve([])
+     throw new Error('Ollama provider not found')
    }
    return provider.listRunningModels()
  }
src/shared/types/presenters/llmprovider.presenter.d.ts (1)

93-113: Type collision: OllamaModel is defined in two places with different shapes

This file declares OllamaModel, but src/shared/types/presenters/legacy.presenters.d.ts also exports an OllamaModel (different fields like model, capabilities, modified_at: Date | string). Barrel @shared/presenter likely re-exports one/both, causing ambiguity and unsafe casts downstream (renderer casts to any for capabilities). Unify to a single canonical type or rename one to avoid collisions.

Suggested direction:

  • Rename this interface to OllamaShowModel (or OllamaModelCore) and update method signatures to use it.
  • Or re-export the legacy one and extend via intersection for optional show fields.

I can prep a minimal refactor if you confirm the intended canonical shape.

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

1274-1391: Type mismatch: writing unknown property provider to RENDERER_MODEL_META

RENDERER_MODEL_META in shared types doesn’t include provider, only providerId. Two places assign provider: providerId, which will fail excess property checks under strict TS.

Fix by removing provider assignments:

@@ refreshStandardModels(...)
-              provider: providerId,
               group: meta.group || 'default',
@@ syncOllamaModelsToGlobal(...)
-          provider: providerId,
           group: existingModel?.group || 'local',

Additionally, consider consolidating capability detection to avoid repeated as any (optional).


374-374: Duplicate listener registration: setupProviderListener called twice

setupProviderListener() is called in initSettings() and again in onMounted(), causing duplicated IPC listeners and repeated work. Call it only once.

-      setupProviderListener()
+      // setupProviderListener() is invoked in onMounted; avoid double registration
🧹 Nitpick comments (4)
src/renderer/settings/components/AddCustomProviderDialog.vue (2)

39-40: Conditional API-key requirement and ollama-specific defaults look good

  • :required="formData.apiType !== 'ollama'" is correct.
  • Auto-filling baseUrl and clearing apiKey when switching to/from Ollama is sensible.

Optional: prefer http://127.0.0.1:11434 to avoid potential IPv6/hosts issues.

Also applies to: 57-58, 178-190


152-169: Translate comments/logs to English

Comments are in Chinese. Project rule: logs/comments in English. Please update for consistency. As per coding guidelines.

Also applies to: 170-176

src/shared/types/presenters/legacy.presenters.d.ts (1)

1085-1107: Two different OllamaModel interfaces exist across shared d.ts files

This file’s OllamaModel differs from src/shared/types/presenters/llmprovider.presenter.d.ts. Consider consolidating or aliasing to avoid confusion and accidental misuse.

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

1108-1124: Minor: order of cleanup vs refresh

You refresh all models then clear Ollama maps for the removed provider. Clearing first slightly reduces transient work; current order is functionally fine.

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between 18d8386 and c948b25.

📒 Files selected for processing (7)
  • src/main/presenter/llmProviderPresenter/index.ts (2 hunks)
  • src/renderer/settings/components/AddCustomProviderDialog.vue (3 hunks)
  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue (6 hunks)
  • src/renderer/src/stores/settings.ts (15 hunks)
  • src/renderer/src/views/WelcomeView.vue (1 hunks)
  • src/shared/types/presenters/legacy.presenters.d.ts (1 hunks)
  • src/shared/types/presenters/llmprovider.presenter.d.ts (1 hunks)
🧰 Additional context used
📓 Path-based instructions (26)
**/*.{js,jsx,ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/development-setup.mdc)

**/*.{js,jsx,ts,tsx}: 使用 OxLint 进行代码检查
Log和注释使用英文书写

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/renderer/src/stores/settings.ts
  • src/main/presenter/llmProviderPresenter/index.ts
**/*.{ts,tsx}

📄 CodeRabbit inference engine (.cursor/rules/error-logging.mdc)

**/*.{ts,tsx}: 始终使用 try-catch 处理可能的错误
提供有意义的错误信息
记录详细的错误日志
优雅降级处理
日志应包含时间戳、日志级别、错误代码、错误描述、堆栈跟踪(如适用)、相关上下文信息
日志级别应包括 ERROR、WARN、INFO、DEBUG
不要吞掉错误
提供用户友好的错误信息
实现错误重试机制
避免记录敏感信息
使用结构化日志
设置适当的日志级别

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/renderer/src/stores/settings.ts
  • src/main/presenter/llmProviderPresenter/index.ts
src/shared/**/*.{ts,tsx,d.ts}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

共享类型定义放在 shared 目录

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use English for all logs and comments

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/renderer/src/stores/settings.ts
  • src/main/presenter/llmProviderPresenter/index.ts
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Enable and adhere to strict TypeScript typing (avoid implicit any, prefer precise types)

Use PascalCase for TypeScript types and classes

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/renderer/src/stores/settings.ts
  • src/main/presenter/llmProviderPresenter/index.ts
src/shared/**/*.ts

📄 CodeRabbit inference engine (CLAUDE.md)

Place shared types, utilities, constants, and IPC contract definitions under src/shared/

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
src/shared/**

📄 CodeRabbit inference engine (AGENTS.md)

Put shared TypeScript types and utilities under src/shared

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
**/*.{ts,tsx,js,jsx,vue,css,scss,md,json,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

Prettier style: single quotes, no semicolons, print width 100; run pnpm run format

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/renderer/src/stores/settings.ts
  • src/main/presenter/llmProviderPresenter/index.ts
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,vue}: Use OxLint for JS/TS code; keep lint clean
Use camelCase for variables and functions
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/renderer/src/stores/settings.ts
  • src/main/presenter/llmProviderPresenter/index.ts
src/renderer/**/*.{vue,ts,js,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

渲染进程代码放在 src/renderer

Files:

  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/renderer/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)

src/renderer/**/*.{ts,tsx,vue}: Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
Use TypeScript for all code; prefer types over interfaces.
Avoid enums; use const objects instead.
Use arrow functions for methods and computed properties.
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements.

Files:

  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/renderer/**/*.{vue,ts}

📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)

Implement lazy loading for routes and components.

Files:

  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/renderer/**/*.{ts,vue}

📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)

src/renderer/**/*.{ts,vue}: Use useFetch and useAsyncData for data fetching.
Implement SEO best practices using Nuxt's useHead and useSeoMeta.

Use Pinia for frontend state management (do not introduce alternative state libraries)

Files:

  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/renderer/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

Name Vue component files in PascalCase (e.g., ChatInput.vue)

Files:

  • src/renderer/settings/components/OllamaProviderSettingsDetail.vue
  • src/renderer/settings/components/AddCustomProviderDialog.vue
  • src/renderer/src/views/WelcomeView.vue
src/renderer/src/**/*

📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)

src/renderer/src/**/*: All user-facing strings must use i18n keys (avoid hardcoded user-visible text in code)
Use the 'vue-i18n' framework for all internationalization in the renderer
Ensure all user-visible text in the renderer uses the translation system

Files:

  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/renderer/src/**/*.{vue,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/vue-best-practices.mdc)

src/renderer/src/**/*.{vue,ts,tsx,js,jsx}: Use the Composition API for better code organization and reusability
Implement proper state management with Pinia
Utilize Vue Router for navigation and route management
Leverage Vue's built-in reactivity system for efficient data handling

Files:

  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/renderer/src/**/*.vue

📄 CodeRabbit inference engine (.cursor/rules/vue-best-practices.mdc)

Use scoped styles to prevent CSS conflicts between components

Files:

  • src/renderer/src/views/WelcomeView.vue
src/renderer/{src,shell,floating}/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

src/renderer/{src,shell,floating}/**/*.vue: Use Vue 3 Composition API for all components
All user-facing strings must use i18n keys via vue-i18n (no hard-coded UI strings)
Use Tailwind CSS utilities and ensure styles are scoped in Vue components

Files:

  • src/renderer/src/views/WelcomeView.vue
src/renderer/src/**

📄 CodeRabbit inference engine (AGENTS.md)

Place Vue 3 app source under src/renderer/src (components, stores, views, i18n, lib)

Files:

  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/renderer/src/**/*.{vue,ts}

📄 CodeRabbit inference engine (AGENTS.md)

All user-facing strings must use vue-i18n ($t/keys) rather than hardcoded literals

Files:

  • src/renderer/src/views/WelcomeView.vue
  • src/renderer/src/stores/settings.ts
src/{main,renderer}/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)

src/{main,renderer}/**/*.ts: Use context isolation for improved security
Implement proper inter-process communication (IPC) patterns
Optimize application startup time with lazy loading
Implement proper error handling and logging for debugging

Files:

  • src/renderer/src/stores/settings.ts
  • src/main/presenter/llmProviderPresenter/index.ts
src/renderer/src/stores/**/*.{vue,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/pinia-best-practices.mdc)

src/renderer/src/stores/**/*.{vue,ts,tsx,js,jsx}: Use modules to organize related state and actions
Implement proper state persistence for maintaining data across sessions
Use getters for computed state properties
Utilize actions for side effects and asynchronous operations
Keep the store focused on global state, not component-specific data

Files:

  • src/renderer/src/stores/settings.ts
src/main/**/*.ts

📄 CodeRabbit inference engine (.cursor/rules/electron-best-practices.mdc)

Use Electron's built-in APIs for file system and native dialogs

Files:

  • src/main/presenter/llmProviderPresenter/index.ts
src/main/presenter/llmProviderPresenter/index.ts

📄 CodeRabbit inference engine (.cursor/rules/llm-agent-loop.mdc)

src/main/presenter/llmProviderPresenter/index.ts: src/main/presenter/llmProviderPresenter/index.ts should manage the overall Agent loop, conversation history, tool execution via McpPresenter, and frontend communication via eventBus.
The main Agent loop in llmProviderPresenter/index.ts should handle multi-round LLM calls and tool usage, maintaining conversation state and controlling the loop with needContinueConversation and toolCallCount.
The main Agent loop should send standardized STREAM_EVENTS (RESPONSE, END, ERROR) to the frontend via eventBus.
The main Agent loop should buffer text content, handle tool call events, format tool results for the next LLM call, and manage conversation continuation logic.

Files:

  • src/main/presenter/llmProviderPresenter/index.ts
src/main/**/*.{ts,js,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

主进程代码放在 src/main

Files:

  • src/main/presenter/llmProviderPresenter/index.ts
src/main/presenter/**/*.ts

📄 CodeRabbit inference engine (AGENTS.md)

Place Electron main-process presenters under src/main/presenter/ (Window, Tab, Thread, Mcp, Config, LLMProvider)

Files:

  • src/main/presenter/llmProviderPresenter/index.ts
🧠 Learnings (7)
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Each file in `src/main/presenter/llmProviderPresenter/providers/*.ts` should handle interaction with a specific LLM API, including request/response formatting, tool definition conversion, native/non-native tool call management, and standardizing output streams to a common event format.

Applied to files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/main/presenter/llmProviderPresenter/index.ts
📚 Learning: 2025-09-06T03:07:23.817Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-06T03:07:23.817Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : New LLM providers must be added under src/main/presenter/llmProviderPresenter/providers/ as separate files

Applied to files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/main/presenter/llmProviderPresenter/index.ts
📚 Learning: 2025-10-14T08:02:59.495Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: AGENTS.md:0-0
Timestamp: 2025-10-14T08:02:59.495Z
Learning: Applies to src/main/presenter/LLMProvider/**/*.ts : Implement the two-layer LLM provider (Agent Loop + Provider) under src/main/presenter/LLMProvider

Applied to files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/main/presenter/llmProviderPresenter/index.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Provider files should implement helper methods such as `formatMessages`, `convertToProviderTools`, `parseFunctionCalls`, and `prepareFunctionCallPrompt` as needed for provider-specific logic.

Applied to files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/main/presenter/llmProviderPresenter/index.ts
📚 Learning: 2025-09-04T11:03:30.184Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/provider-guidelines.mdc:0-0
Timestamp: 2025-09-04T11:03:30.184Z
Learning: Integrate via the llmProviderPresenter entry point (src/main/presenter/llmProviderPresenter/index.ts) as the related implementation entry

Applied to files:

  • src/shared/types/presenters/legacy.presenters.d.ts
  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/main/presenter/llmProviderPresenter/index.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : All provider implementations must parse provider-specific data chunks and yield standardized events for text, reasoning, tool calls, usage, errors, stop reasons, and image data.

Applied to files:

  • src/shared/types/presenters/llmprovider.presenter.d.ts
  • src/main/presenter/llmProviderPresenter/index.ts
📚 Learning: 2025-07-21T01:46:52.880Z
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: .cursor/rules/llm-agent-loop.mdc:0-0
Timestamp: 2025-07-21T01:46:52.880Z
Learning: Applies to src/main/presenter/llmProviderPresenter/providers/*.ts : Provider implementations should yield tool call events (`tool_call_start`, `tool_call_chunk`, `tool_call_end`) in the standardized format.

Applied to files:

  • src/shared/types/presenters/llmprovider.presenter.d.ts
🧬 Code graph analysis (4)
src/shared/types/presenters/legacy.presenters.d.ts (1)
src/shared/types/presenters/llmprovider.presenter.d.ts (1)
  • OllamaModel (93-113)
src/shared/types/presenters/llmprovider.presenter.d.ts (2)
src/shared/types/presenters/legacy.presenters.d.ts (1)
  • OllamaModel (1085-1107)
src/shared/types/presenters/index.d.ts (1)
  • OllamaModel (17-17)
src/renderer/src/stores/settings.ts (2)
src/shared/types/presenters/legacy.presenters.d.ts (2)
  • OllamaModel (1085-1107)
  • RENDERER_MODEL_META (550-564)
src/shared/types/presenters/llmprovider.presenter.d.ts (2)
  • OllamaModel (93-113)
  • RENDERER_MODEL_META (10-25)
src/main/presenter/llmProviderPresenter/index.ts (3)
src/main/presenter/llmProviderPresenter/providers/ollamaProvider.ts (1)
  • OllamaProvider (40-1186)
src/shared/types/presenters/legacy.presenters.d.ts (1)
  • OllamaModel (1085-1107)
src/shared/types/presenters/llmprovider.presenter.d.ts (1)
  • OllamaModel (93-113)
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (18)
src/main/presenter/llmProviderPresenter/index.ts (1)

1633-1644: Provider-scoped Ollama instance lookup looks good

Type-guarding with instanceof OllamaProvider and graceful null fallback is solid.

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

142-146: Correctly skip API key/base URL validation for Ollama

Using provider.apiType === 'ollama' is the right gate.

src/shared/types/presenters/legacy.presenters.d.ts (1)

688-692: Type signatures updated to provider-scoped: LGTM

Interface now requires providerId for all Ollama operations. Matches presenter changes.

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

7-16: Provider-scoped Ollama store calls and delete dialog are on point

  • getOllamaRunningModels/getOllamaLocalModels/pullOllamaModel/refreshOllamaModels now all pass provider.id — correct for multi-provider.
  • Delete-provider flow with confirmation dialog looks clean.

Also applies to: 250-267, 307-315, 846-848, 852-855, 891-895, 940-947

src/shared/types/presenters/llmprovider.presenter.d.ts (1)

177-180: Interface update aligns with multi‑provider Ollama

Adding providerId first in all Ollama methods is consistent with the PR direction. No issues.

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

39-41: Good: provider‑scoped Ollama state

Switching to Record<providerId, ...> removes cross‑provider bleed and simplifies lookups.


367-369: Eager refresh for disabled Ollama providers

Intent is clear and wrapped by refreshOllamaModels try/catch. Keep as is.

If many Ollama providers are configured, consider deferring these calls until first use to reduce startup latency.


568-571: Ollama‑specific refresh path looks correct

Special‑casing Ollama to use its dedicated fetch + custom models pipeline is appropriate.


676-676: Cleanup on provider removal

clearOllamaProviderData on single‑provider change avoids stale progress/state.


678-685: Targeted refresh on Ollama provider updates

Scoped refresh avoids full reloads. Looks good.


705-705: Batch removal: Ollama state cleanup

Good catch to clear Ollama maps when a provider is removed.


707-713: Batch updates: provider‑type aware refresh

Per‑provider branching for Ollama vs others is correct.


1194-1256: Accessor/mutator helpers for Ollama state

API surface is clear and cohesive.


1258-1271: Parallel fetch for running/local models + robust error logging

Good use of Promise.all and scoped provider logs.


1412-1437: Progress handler is defensive and id‑scoped

Validates providerId and computes progress safely; also handles 'manifest' edge to show activity.


1453-1459: Helper predicates per provider

Simple, correct lookups against provider‑scoped maps.


1827-1835: Exporting provider‑scoped Ollama helpers

Good to expose getters and cleanup for UI/state consumers.


1393-1409: Pull flow + progress tracking verified

Event wiring is correct. The main process presenter emits events with eventId: 'pullOllamaModels' via OLLAMA_EVENTS.PULL_MODEL_PROGRESS, matching the renderer's listener and handler filter. Flow is sound: optimistic progress initialization → presenter invocation → event-driven state updates on pull completion or failure.

<div class="w-full h-full p-2 flex flex-col gap-2 overflow-y-auto">
<div class="flex flex-col items-start p-2 gap-2">
<div class="flex justify-between items-center w-full">
<Label :for="`${provider.id}-url`" class="flex-1 cursor-pointer">API URL</Label>
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟠 Major

Localize labels

“API URL” and “API Key” labels are hardcoded. Replace with i18n keys.

- <Label ...>API URL</Label>
+ <Label ...>{{ t('settings.provider.apiUrl') }}</Label>

- <Label ...>API Key</Label>
+ <Label ...>{{ t('settings.provider.apiKey') }}</Label>

As per coding guidelines.

Also applies to: 35-35

🤖 Prompt for AI Agents
In src/renderer/settings/components/OllamaProviderSettingsDetail.vue around
lines 6 and 35, the labels "API URL" and "API Key" are hardcoded — replace them
with i18n keys (e.g. settings.apiUrl, settings.apiKey) by using the project's
i18n call in the template (so the Label text comes from $t('...')/t('...') per
project convention) and add corresponding entries to the locale JSON/YAML files;
ensure keys follow existing naming conventions and run a quick lint/compile to
verify the translations resolve.

@chatgpt-codex-connector
Copy link

Codex Review: Didn't find any major issues. Chef's kiss.

ℹ️ About Codex in GitHub

Your team has set up Codex to review pull requests in this repo. Reviews are triggered when you

  • Open a pull request for review
  • Mark a draft as ready
  • Comment "@codex review".

If Codex has suggestions, it will comment; otherwise it will react with 👍.

Codex can also answer questions or update the PR. Try commenting "@codex address that feedback".

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

📜 Review details

Configuration used: CodeRabbit UI

Review profile: CHILL

Plan: Pro

📥 Commits

Reviewing files that changed from the base of the PR and between c948b25 and 49d4fb7.

📒 Files selected for processing (1)
  • src/renderer/src/components/icons/ModelIcon.vue (3 hunks)
🧰 Additional context used
📓 Path-based instructions (16)
src/renderer/src/**/*

📄 CodeRabbit inference engine (.cursor/rules/i18n.mdc)

src/renderer/src/**/*: All user-facing strings must use i18n keys (avoid hardcoded user-visible text in code)
Use the 'vue-i18n' framework for all internationalization in the renderer
Ensure all user-visible text in the renderer uses the translation system

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/**/*.{vue,ts,js,tsx,jsx}

📄 CodeRabbit inference engine (.cursor/rules/project-structure.mdc)

渲染进程代码放在 src/renderer

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/src/**/*.{vue,ts,tsx,js,jsx}

📄 CodeRabbit inference engine (.cursor/rules/vue-best-practices.mdc)

src/renderer/src/**/*.{vue,ts,tsx,js,jsx}: Use the Composition API for better code organization and reusability
Implement proper state management with Pinia
Utilize Vue Router for navigation and route management
Leverage Vue's built-in reactivity system for efficient data handling

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/src/**/*.vue

📄 CodeRabbit inference engine (.cursor/rules/vue-best-practices.mdc)

Use scoped styles to prevent CSS conflicts between components

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)

src/renderer/**/*.{ts,tsx,vue}: Use descriptive variable names with auxiliary verbs (e.g., isLoading, hasError).
Use TypeScript for all code; prefer types over interfaces.
Avoid enums; use const objects instead.
Use arrow functions for methods and computed properties.
Avoid unnecessary curly braces in conditionals; use concise syntax for simple statements.

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/**/*.{vue,ts}

📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)

Implement lazy loading for routes and components.

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/**/*.{ts,vue}

📄 CodeRabbit inference engine (.cursor/rules/vue-shadcn.mdc)

src/renderer/**/*.{ts,vue}: Use useFetch and useAsyncData for data fetching.
Implement SEO best practices using Nuxt's useHead and useSeoMeta.

Use Pinia for frontend state management (do not introduce alternative state libraries)

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
**/*.{ts,tsx,js,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Use English for all logs and comments

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
**/*.{ts,tsx,vue}

📄 CodeRabbit inference engine (CLAUDE.md)

Enable and adhere to strict TypeScript typing (avoid implicit any, prefer precise types)

Use PascalCase for TypeScript types and classes

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/{src,shell,floating}/**/*.vue

📄 CodeRabbit inference engine (CLAUDE.md)

src/renderer/{src,shell,floating}/**/*.vue: Use Vue 3 Composition API for all components
All user-facing strings must use i18n keys via vue-i18n (no hard-coded UI strings)
Use Tailwind CSS utilities and ensure styles are scoped in Vue components

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/src/components/**/*

📄 CodeRabbit inference engine (CLAUDE.md)

Organize UI components by feature within src/renderer/src/

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/src/**

📄 CodeRabbit inference engine (AGENTS.md)

Place Vue 3 app source under src/renderer/src (components, stores, views, i18n, lib)

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/src/**/*.{vue,ts}

📄 CodeRabbit inference engine (AGENTS.md)

All user-facing strings must use vue-i18n ($t/keys) rather than hardcoded literals

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
**/*.{ts,tsx,js,jsx,vue,css,scss,md,json,yml,yaml}

📄 CodeRabbit inference engine (AGENTS.md)

Prettier style: single quotes, no semicolons, print width 100; run pnpm run format

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
**/*.{ts,tsx,js,jsx,vue}

📄 CodeRabbit inference engine (AGENTS.md)

**/*.{ts,tsx,js,jsx,vue}: Use OxLint for JS/TS code; keep lint clean
Use camelCase for variables and functions
Use SCREAMING_SNAKE_CASE for constants

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
src/renderer/**/*.vue

📄 CodeRabbit inference engine (AGENTS.md)

Name Vue component files in PascalCase (e.g., ChatInput.vue)

Files:

  • src/renderer/src/components/icons/ModelIcon.vue
⏰ Context from checks skipped due to timeout of 90000ms. You can increase the timeout in your CodeRabbit configuration to a maximum of 15 minutes (900000ms). (1)
  • GitHub Check: build-check (x64)
🔇 Additional comments (3)
src/renderer/src/components/icons/ModelIcon.vue (3)

3-3: LGTM! Store import added appropriately.

The settings store import is necessary to access provider data for the enhanced icon resolution logic.


170-182: Good enhancement: apiType-based icon fallback.

The fallback logic that uses provider.apiType to find a matching icon is a sensible addition. This allows custom providers (e.g., multiple Ollama servers) to inherit the appropriate icon based on their API type when they don't have a direct modelId match.


189-206: Excellent refactor of the inversion logic.

The refactored inversion logic using arrays and array methods is much more maintainable than explicit string checks. It's easier to extend with new keywords and more declarative. The addition of provider.apiType to the check targets also aligns well with the provider-scoped changes.

Comment on lines +157 to +160
const provider = computed(() => {
if (!props.modelId) return undefined
return settingsStore.providers.find((item) => item.id === props.modelId)
})
Copy link
Contributor

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

Verify the provider lookup logic.

The provider lookup matches item.id === props.modelId, which semantically suggests a modelId prop is being used to search for providers by their ID. This could indicate either:

  1. A naming inconsistency where modelId can actually hold provider IDs in some contexts
  2. A logic error where providers should be looked up differently

Please verify this is the intended behavior. If modelId can represent both model and provider IDs, consider renaming the prop to something more generic (e.g., itemId or identifier) or adding a comment explaining the dual usage.


🏁 Script executed:

#!/bin/bash
# Description: Check how ModelIcon component is used across the codebase to verify if modelId is passed provider IDs

# Search for ModelIcon usage patterns to see what values are passed to modelId prop
rg -nP --type=vue -A3 -B3 '<ModelIcon\s+.*?modelId'

# Also search for modelId prop bindings
rg -nP --type=vue -A2 'modelId\s*='

Length of output: 55


🏁 Script executed:

#!/bin/bash
# Description: Check how ModelIcon component is used and how modelId prop is set

# Search for ModelIcon usage patterns in .vue files
echo "=== ModelIcon Usage Patterns ==="
rg -n '<ModelIcon' -g '*.vue' -A3 -B1

echo -e "\n=== modelId Prop Bindings ==="
# Search for modelId prop assignments/bindings
rg -n 'modelId\s*=' -g '*.vue' -A2 -B1

echo -e "\n=== File containing ModelIcon.vue definition ==="
# Verify the ModelIcon.vue file location
fd -e vue -x grep -l 'ModelIcon' {}

Length of output: 9425


🏁 Script executed:

#!/bin/bash
# Description: Verify the structure of model objects passed to ModelIcon to understand if their .id is provider ID or model ID

# Search for selectedImageModel definition/assignment
echo "=== selectedImageModel definition ==="
rg -n 'selectedImageModel' -g '*.vue' -B2 -A2 | head -40

# Search for selectEmbeddingModel definition/assignment  
echo -e "\n=== selectEmbeddingModel definition ==="
rg -n 'selectEmbeddingModel' -g '*.vue' -B2 -A2 | head -40

# Check the settingsStore to understand provider structure
echo -e "\n=== settingsStore providers structure ==="
rg -n 'providers' -g '*.ts' -B3 -A3 | grep -A5 -B5 'interface.*Provider\|type.*Provider' | head -50

Length of output: 10211


🏁 Script executed:

#!/bin/bash
# Description: Find RENDERER_MODEL_META type definition

echo "=== RENDERER_MODEL_META type definition ==="
rg -n 'RENDERER_MODEL_META' -g '*.ts' -B2 -A8 | head -60

echo -e "\n=== MODEL_META type definition ==="
rg -n 'type MODEL_META\|interface MODEL_META' -g '*.ts' -B2 -A10 | head -60

Length of output: 4682


Pass provider ID instead of model ID to ModelIcon component.

The ModelIcon component searches the providers array by ID, but several components are incorrectly passing model IDs instead of provider IDs. This causes the provider lookup to fail.

Affected locations:

  • src/renderer/src/components/mcp-config/mcpServerForm.vue:879 — should pass selectedImageModelProvider instead of selectedImageModel?.id
  • src/renderer/settings/components/BuiltinKnowledgeSettings.vue:187, 227 — should pass selectEmbeddingModel?.providerId and selectRerankModel?.providerId
  • src/renderer/settings/components/common/SearchAssistantModelSection.vue:16 — should pass selectedSearchModel?.providerId

All model objects (RENDERER_MODEL_META) have a providerId field available for this lookup.

@zerob13 zerob13 merged commit 5d451b3 into ThinkInAIXYZ:dev Oct 23, 2025
2 checks passed
@Dw9 Dw9 deleted the feat-multi-ollama branch January 16, 2026 01:46
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