Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
114 changes: 109 additions & 5 deletions src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@ import {
RENDERER_MODEL_META,
MCPServerConfig,
Prompt,
SystemPrompt,
IModelConfig,
BuiltinKnowledgeConfig
} from '@shared/presenter'
Expand Down Expand Up @@ -90,6 +91,7 @@ export class ConfigPresenter implements IConfigPresenter {
private store: ElectronStore<IAppSettings>
private providersModelStores: Map<string, ElectronStore<IModelStore>> = new Map()
private customPromptsStore: ElectronStore<{ prompts: Prompt[] }>
private systemPromptsStore: ElectronStore<{ prompts: SystemPrompt[] }>
private userDataPath: string
private currentAppVersion: string
private mcpConfHelper: McpConfHelper // 使用MCP配置助手
Expand Down Expand Up @@ -138,6 +140,22 @@ export class ConfigPresenter implements IConfigPresenter {
}
})

this.systemPromptsStore = new ElectronStore<{ prompts: SystemPrompt[] }>({
name: 'system_prompts',
defaults: {
prompts: [
{
id: 'default',
name: 'DeepChat',
content: DEFAULT_SYSTEM_PROMPT,
isDefault: true,
createdAt: Date.now(),
updatedAt: Date.now()
}
]
}
})
Comment on lines +143 to +157
Copy link
Contributor

@coderabbitai coderabbitai bot Sep 8, 2025

Choose a reason for hiding this comment

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

⚠️ Potential issue

Migrate legacy default_system_prompt into the new system_prompts store

Without migration, existing users’ customized default prompt is ignored after upgrade (getDefaultSystemPrompt now prefers systemPrompts). Migrate on startup.

     this.systemPromptsStore = new ElectronStore<{ prompts: SystemPrompt[] }>({
       name: 'system_prompts',
       defaults: {
         prompts: [
           {
             id: 'default',
             name: 'DeepChat',
             content: DEFAULT_SYSTEM_PROMPT,
             isDefault: true,
             createdAt: Date.now(),
             updatedAt: Date.now()
           }
         ]
       }
     })
+
+    // Migrate legacy default_system_prompt into system_prompts (one-time)
+    try {
+      const legacyDefault = this.store.get('default_system_prompt')
+      if (
+        typeof legacyDefault === 'string' &&
+        legacyDefault.trim() &&
+        legacyDefault.trim() !== DEFAULT_SYSTEM_PROMPT.trim()
+      ) {
+        const prompts = (this.systemPromptsStore.get('prompts') || []) as SystemPrompt[]
+        const now = Date.now()
+        const idx = prompts.findIndex((p) => p.id === 'default')
+        if (idx !== -1) {
+          prompts[idx] = {
+            ...prompts[idx],
+            content: legacyDefault,
+            isDefault: true,
+            updatedAt: now
+          }
+        } else {
+          prompts.push({
+            id: 'default',
+            name: 'DeepChat',
+            content: legacyDefault,
+            isDefault: true,
+            createdAt: now,
+            updatedAt: now
+          })
+        }
+        this.systemPromptsStore.set('prompts', prompts)
+      }
+    } catch (e) {
+      console.warn('Failed to migrate legacy default_system_prompt:', e)
+    }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
this.systemPromptsStore = new ElectronStore<{ prompts: SystemPrompt[] }>({
name: 'system_prompts',
defaults: {
prompts: [
{
id: 'default',
name: 'DeepChat',
content: DEFAULT_SYSTEM_PROMPT,
isDefault: true,
createdAt: Date.now(),
updatedAt: Date.now()
}
]
}
})
this.systemPromptsStore = new ElectronStore<{ prompts: SystemPrompt[] }>({
name: 'system_prompts',
defaults: {
prompts: [
{
id: 'default',
name: 'DeepChat',
content: DEFAULT_SYSTEM_PROMPT,
isDefault: true,
createdAt: Date.now(),
updatedAt: Date.now()
}
]
}
})
// Migrate legacy default_system_prompt into system_prompts (one-time)
try {
const legacyDefault = this.store.get('default_system_prompt')
if (
typeof legacyDefault === 'string' &&
legacyDefault.trim() &&
legacyDefault.trim() !== DEFAULT_SYSTEM_PROMPT.trim()
) {
const prompts = (this.systemPromptsStore.get('prompts') || []) as SystemPrompt[]
const now = Date.now()
const idx = prompts.findIndex((p) => p.id === 'default')
if (idx !== -1) {
prompts[idx] = {
...prompts[idx],
content: legacyDefault,
isDefault: true,
updatedAt: now
}
} else {
prompts.push({
id: 'default',
name: 'DeepChat',
content: legacyDefault,
isDefault: true,
createdAt: now,
updatedAt: now
})
}
this.systemPromptsStore.set('prompts', prompts)
}
} catch (e) {
console.warn('Failed to migrate legacy default_system_prompt:', e)
}
🤖 Prompt for AI Agents
In src/main/presenter/configPresenter/index.ts around lines 143-157, implement a
startup migration that reads the legacy 'default_system_prompt' (from wherever
it was previously stored), and if present and non-empty migrates it into the new
systemPrompts store by inserting a SystemPrompt object into
this.systemPromptsStore.prompts with a stable id (e.g. 'default' or a generated
id), name like 'Migrated Default', content set to the legacy value, isDefault
true, and proper createdAt/updatedAt timestamps; ensure you do not duplicate if
a prompt with the same content/id already exists, update any existing default
flag (unset other isDefault), persist the updated prompts array back to
this.systemPromptsStore, and remove the legacy 'default_system_prompt' key so
migration runs only once.

Copy link
Collaborator

Choose a reason for hiding this comment

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

should migration user's system prompt setting with migrateConfigData

Copy link
Contributor

Choose a reason for hiding this comment

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

@zerob13 You're absolutely correct! The migration should be handled in the existing migrateConfigData method for proper version-based migration control.

Looking at the code, I can see that this migration is already properly implemented in the migrateConfigData method at lines 320-354 for version 0.3.5:

// 0.3.5 版本之前,处理默认系统提示词的迁移和设置
if (oldVersion && compare(oldVersion, '0.3.5', '<')) {
  try {
    const currentPrompt = this.getSetting<string>('default_system_prompt')
    if (!currentPrompt || currentPrompt.trim() === '') {
      this.setSetting('default_system_prompt', DEFAULT_SYSTEM_PROMPT)
    }
    const legacyDefault = this.getSetting<string>('default_system_prompt')
    if (
      typeof legacyDefault === 'string' &&
      legacyDefault.trim() &&
      legacyDefault.trim() !== DEFAULT_SYSTEM_PROMPT.trim()
    ) {
      const prompts = (this.systemPromptsStore.get('prompts') || []) as SystemPrompt[]
      const now = Date.now()
      const idx = prompts.findIndex((p) => p.id === 'default')
      if (idx !== -1) {
        prompts[idx] = {
          ...prompts[idx],
          content: legacyDefault,
          isDefault: true,
          updatedAt: now
        }
      } else {
        prompts.push({
          id: 'default',
          name: 'DeepChat',
          content: legacyDefault,
          isDefault: true,
          createdAt: now,
          updatedAt: now
        })
      }
      this.systemPromptsStore.set('prompts', prompts)
    }
  } catch (e) {
    console.warn('Failed to migrate legacy default_system_prompt:', e)
  }
}

The migration is correctly placed in migrateConfigData where it belongs, following the established pattern of version-based migrations. My previous suggestion to add migration code in the constructor was incorrect. Thank you for the clarification!


🧠 Learnings used
Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-06T03:07:23.794Z
Learning: Applies to src/main/presenter/configPresenter/providers.ts : Add provider configuration entries in src/main/presenter/configPresenter/providers.ts

Learnt from: CR
PR: ThinkInAIXYZ/deepchat#0
File: CLAUDE.md:0-0
Timestamp: 2025-09-06T03:07:23.794Z
Learning: Applies to src/main/presenter/mcpPresenter/index.ts : Register new MCP tools in src/main/presenter/mcpPresenter/index.ts


// 初始化MCP配置助手
this.mcpConfHelper = new McpConfHelper()

Expand Down Expand Up @@ -276,11 +294,43 @@ export class ConfigPresenter implements IConfigPresenter {
}
}

// 0.3.4 版本之前,如果默认系统提示词为空,则设置为内置的默认提示词
if (oldVersion && compare(oldVersion, '0.3.4', '<')) {
const currentPrompt = this.getSetting<string>('default_system_prompt')
if (!currentPrompt || currentPrompt.trim() === '') {
this.setSetting('default_system_prompt', DEFAULT_SYSTEM_PROMPT)
// 0.3.5 版本之前,处理默认系统提示词的迁移和设置
if (oldVersion && compare(oldVersion, '0.3.5', '<')) {
try {
const currentPrompt = this.getSetting<string>('default_system_prompt')
if (!currentPrompt || currentPrompt.trim() === '') {
this.setSetting('default_system_prompt', DEFAULT_SYSTEM_PROMPT)
}
const legacyDefault = this.getSetting<string>('default_system_prompt')
if (
typeof legacyDefault === 'string' &&
legacyDefault.trim() &&
legacyDefault.trim() !== DEFAULT_SYSTEM_PROMPT.trim()
) {
const prompts = (this.systemPromptsStore.get('prompts') || []) as SystemPrompt[]
const now = Date.now()
const idx = prompts.findIndex((p) => p.id === 'default')
if (idx !== -1) {
prompts[idx] = {
...prompts[idx],
content: legacyDefault,
isDefault: true,
updatedAt: now
}
} else {
prompts.push({
id: 'default',
name: 'DeepChat',
content: legacyDefault,
isDefault: true,
createdAt: now,
updatedAt: now
})
}
this.systemPromptsStore.set('prompts', prompts)
}
} catch (e) {
console.warn('Failed to migrate legacy default_system_prompt:', e)
}
}
}
Expand Down Expand Up @@ -1181,6 +1231,11 @@ export class ConfigPresenter implements IConfigPresenter {

// 获取默认系统提示词
async getDefaultSystemPrompt(): Promise<string> {
const prompts = await this.getSystemPrompts()
const defaultPrompt = prompts.find((p) => p.isDefault)
if (defaultPrompt) {
return defaultPrompt.content
}
return this.getSetting<string>('default_system_prompt') || ''
}

Expand All @@ -1199,6 +1254,55 @@ export class ConfigPresenter implements IConfigPresenter {
this.setSetting('default_system_prompt', '')
}

async getSystemPrompts(): Promise<SystemPrompt[]> {
try {
return this.systemPromptsStore.get('prompts') || []
} catch {
return []
}
}

async setSystemPrompts(prompts: SystemPrompt[]): Promise<void> {
await this.systemPromptsStore.set('prompts', prompts)
}

async addSystemPrompt(prompt: SystemPrompt): Promise<void> {
const prompts = await this.getSystemPrompts()
prompts.push(prompt)
await this.setSystemPrompts(prompts)
}

async updateSystemPrompt(promptId: string, updates: Partial<SystemPrompt>): Promise<void> {
const prompts = await this.getSystemPrompts()
const index = prompts.findIndex((p) => p.id === promptId)
if (index !== -1) {
prompts[index] = { ...prompts[index], ...updates }
await this.setSystemPrompts(prompts)
}
}

async deleteSystemPrompt(promptId: string): Promise<void> {
const prompts = await this.getSystemPrompts()
const filteredPrompts = prompts.filter((p) => p.id !== promptId)
await this.setSystemPrompts(filteredPrompts)
}
Comment on lines +1284 to +1288
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Guard against deleting the built-in default prompt

Prevent removal of id 'default' to keep the system consistent.

   async deleteSystemPrompt(promptId: string): Promise<void> {
+    if (promptId === 'default') {
+      throw new Error('Cannot delete the built-in default system prompt')
+    }
     const prompts = await this.getSystemPrompts()
     const filteredPrompts = prompts.filter((p) => p.id !== promptId)
     await this.setSystemPrompts(filteredPrompts)
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
async deleteSystemPrompt(promptId: string): Promise<void> {
const prompts = await this.getSystemPrompts()
const filteredPrompts = prompts.filter((p) => p.id !== promptId)
await this.setSystemPrompts(filteredPrompts)
}
async deleteSystemPrompt(promptId: string): Promise<void> {
if (promptId === 'default') {
throw new Error('Cannot delete the built-in default system prompt')
}
const prompts = await this.getSystemPrompts()
const filteredPrompts = prompts.filter((p) => p.id !== promptId)
await this.setSystemPrompts(filteredPrompts)
}
🤖 Prompt for AI Agents
In src/main/presenter/configPresenter/index.ts around lines 1252 to 1256, the
deleteSystemPrompt method currently allows removal of any prompt including the
built-in default; update the method to guard against deleting the prompt with id
'default' by checking if promptId === 'default' and if so throw or return early
(e.g., throw new Error('Cannot delete built-in default prompt') or simply return
without changing prompts); otherwise proceed to fetch, filter and save prompts
as before.


async setDefaultSystemPromptId(promptId: string): Promise<void> {
const prompts = await this.getSystemPrompts()
const updatedPrompts = prompts.map((p) => ({ ...p, isDefault: false }))
const targetIndex = updatedPrompts.findIndex((p) => p.id === promptId)
if (targetIndex !== -1) {
updatedPrompts[targetIndex].isDefault = true
await this.setSystemPrompts(updatedPrompts)
}
}
Comment on lines +1291 to +1298
Copy link
Contributor

Choose a reason for hiding this comment

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

🛠️ Refactor suggestion

Ensure there is always exactly one default; handle unknown IDs gracefully

If promptId isn’t found, keep current default or fall back to 'default' (creating it if missing). This prevents state with no default.

   async setDefaultSystemPromptId(promptId: string): Promise<void> {
     const prompts = await this.getSystemPrompts()
-    const updatedPrompts = prompts.map((p) => ({ ...p, isDefault: false }))
-    const targetIndex = updatedPrompts.findIndex((p) => p.id === promptId)
-    if (targetIndex !== -1) {
-      updatedPrompts[targetIndex].isDefault = true
-      await this.setSystemPrompts(updatedPrompts)
-    }
+    let updatedPrompts = prompts.map((p) => ({ ...p, isDefault: false }))
+    const targetIndex = updatedPrompts.findIndex((p) => p.id === promptId)
+    if (targetIndex !== -1) {
+      updatedPrompts[targetIndex].isDefault = true
+    } else {
+      // Fallback to built-in default
+      const defaultIdx = updatedPrompts.findIndex((p) => p.id === 'default')
+      if (defaultIdx !== -1) {
+        updatedPrompts[defaultIdx].isDefault = true
+      } else {
+        // Recreate default if somehow missing
+        const now = Date.now()
+        updatedPrompts.push({
+          id: 'default',
+          name: 'DeepChat',
+          content: DEFAULT_SYSTEM_PROMPT,
+          isDefault: true,
+          createdAt: now,
+          updatedAt: now
+        })
+      }
+    }
+    await this.setSystemPrompts(updatedPrompts)
   }
📝 Committable suggestion

‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.

Suggested change
const prompts = await this.getSystemPrompts()
const updatedPrompts = prompts.map((p) => ({ ...p, isDefault: false }))
const targetIndex = updatedPrompts.findIndex((p) => p.id === promptId)
if (targetIndex !== -1) {
updatedPrompts[targetIndex].isDefault = true
await this.setSystemPrompts(updatedPrompts)
}
}
async setDefaultSystemPromptId(promptId: string): Promise<void> {
const prompts = await this.getSystemPrompts()
let updatedPrompts = prompts.map((p) => ({ ...p, isDefault: false }))
const targetIndex = updatedPrompts.findIndex((p) => p.id === promptId)
if (targetIndex !== -1) {
updatedPrompts[targetIndex].isDefault = true
} else {
// Fallback to built-in default
const defaultIdx = updatedPrompts.findIndex((p) => p.id === 'default')
if (defaultIdx !== -1) {
updatedPrompts[defaultIdx].isDefault = true
} else {
// Recreate default if somehow missing
const now = Date.now()
updatedPrompts.push({
id: 'default',
name: 'DeepChat',
content: DEFAULT_SYSTEM_PROMPT,
isDefault: true,
createdAt: now,
updatedAt: now
})
}
}
await this.setSystemPrompts(updatedPrompts)
}
🤖 Prompt for AI Agents
In src/main/presenter/configPresenter/index.ts around lines 1259 to 1266, ensure
there is always exactly one default prompt: after loading prompts, clear
isDefault on all entries, then look up promptId; if found mark it
isDefault=true; if not found, try to leave the existing default (find any prompt
with isDefault originally set) — if none exists create a new prompt with id
'default' (or generated id) and isDefault=true and add it to the list; finally
persist by calling setSystemPrompts(updatedPrompts). Also make sure you only
call setSystemPrompts once and that no more than one prompt has isDefault=true.


async getDefaultSystemPromptId(): Promise<string> {
const prompts = await this.getSystemPrompts()
const defaultPrompt = prompts.find((p) => p.isDefault)
return defaultPrompt?.id || 'default'
}

// 获取更新渠道
getUpdateChannel(): string {
return this.getSetting<string>('updateChannel') || 'stable'
Expand Down
Loading