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
3 changes: 2 additions & 1 deletion src/main/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -34,7 +34,8 @@ export const CONFIG_EVENTS = {
OAUTH_LOGIN_SUCCESS: 'config:oauth-login-success', // OAuth登录成功
OAUTH_LOGIN_ERROR: 'config:oauth-login-error', // OAuth登录失败
THEME_CHANGED: 'config:theme-changed', // 主题变更事件
FONT_SIZE_CHANGED: 'config:font-size-changed' // 字体大小变更事件
FONT_SIZE_CHANGED: 'config:font-size-changed', // 字体大小变更事件
DEFAULT_SYSTEM_PROMPT_CHANGED: 'config:default-system-prompt-changed' // 默认系统提示词变更事件
}

// Provider DB(聚合 JSON)相关事件
Expand Down
29 changes: 28 additions & 1 deletion src/main/presenter/configPresenter/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1415,17 +1415,44 @@ export class ConfigPresenter implements IConfigPresenter {
async setDefaultSystemPromptId(promptId: string): Promise<void> {
const prompts = await this.getSystemPrompts()
const updatedPrompts = prompts.map((p) => ({ ...p, isDefault: false }))

if (promptId === 'empty') {
await this.setSystemPrompts(updatedPrompts)
await this.clearSystemPrompt()
eventBus.sendToRenderer(CONFIG_EVENTS.DEFAULT_SYSTEM_PROMPT_CHANGED, SendTarget.ALL_WINDOWS, {
promptId: 'empty',
content: ''
})
return
}

const targetIndex = updatedPrompts.findIndex((p) => p.id === promptId)
if (targetIndex !== -1) {
updatedPrompts[targetIndex].isDefault = true
await this.setSystemPrompts(updatedPrompts)
await this.setDefaultSystemPrompt(updatedPrompts[targetIndex].content)
eventBus.sendToRenderer(CONFIG_EVENTS.DEFAULT_SYSTEM_PROMPT_CHANGED, SendTarget.ALL_WINDOWS, {
promptId,
content: updatedPrompts[targetIndex].content
})
} else {
await this.setSystemPrompts(updatedPrompts)
}
}

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

const storedPrompt = this.getSetting<string>('default_system_prompt')
if (!storedPrompt || storedPrompt.trim() === '') {
return 'empty'
}

return prompts.find((p) => p.id === 'default')?.id || 'default'
}

// 获取更新渠道
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
<SelectValue :placeholder="t('promptSetting.selectSystemPrompt')" />
</SelectTrigger>
<SelectContent>
<SelectItem v-for="prompt in systemPrompts" :key="prompt.id" :value="prompt.id">
<SelectItem v-for="prompt in selectableSystemPrompts" :key="prompt.id" :value="prompt.id">
{{ prompt.name }}
</SelectItem>
</SelectContent>
Expand All @@ -25,7 +25,13 @@
</Button>
</div>

<div v-if="currentSystemPrompt" class="space-y-2">
<div v-if="isEmptyPromptSelected" class="rounded-md border border-dashed border-border p-3">
<p class="text-xs text-muted-foreground">
{{ t('promptSetting.emptySystemPromptDescription') }}
</p>
</div>

<div v-else-if="currentSystemPrompt" class="space-y-2">
<Textarea
v-model="currentSystemPrompt.content"
class="w-full h-48"
Expand Down Expand Up @@ -87,7 +93,7 @@
</template>

<script setup lang="ts">
import { onMounted, ref } from 'vue'
import { computed, onMounted, ref } from 'vue'
import { useI18n } from 'vue-i18n'
import { Icon } from '@iconify/vue'
import { useToast } from '@/components/use-toast'
Expand Down Expand Up @@ -129,12 +135,29 @@ const { t } = useI18n()
const { toast } = useToast()
const settingsStore = useSettingsStore()

const EMPTY_SYSTEM_PROMPT_ID = 'empty'

const systemPrompts = ref<SystemPromptItem[]>([])
const selectedSystemPromptId = ref('')
const currentSystemPrompt = ref<SystemPromptItem | null>(null)
const systemPromptEditorOpen = ref(false)
const editingSystemPrompt = ref<SystemPromptItem | null>(null)

const emptySystemPromptOption = computed<SystemPromptItem>(() => ({
id: EMPTY_SYSTEM_PROMPT_ID,
name: t('promptSetting.emptySystemPromptOption'),
content: ''
}))

const selectableSystemPrompts = computed(() => [
emptySystemPromptOption.value,
...systemPrompts.value
])

const isEmptyPromptSelected = computed(
() => selectedSystemPromptId.value === EMPTY_SYSTEM_PROMPT_ID
)

const loadSystemPrompts = async () => {
try {
systemPrompts.value = await settingsStore.getSystemPrompts()
Expand All @@ -146,14 +169,34 @@ const loadSystemPrompts = async () => {
}

const updateCurrentSystemPrompt = () => {
if (isEmptyPromptSelected.value) {
currentSystemPrompt.value = null
return
}

currentSystemPrompt.value =
systemPrompts.value.find((prompt) => prompt.id === selectedSystemPromptId.value) || null
}

const handleSystemPromptChange = async (promptId: AcceptableValue) => {
try {
await settingsStore.setDefaultSystemPromptId(promptId as string)
selectedSystemPromptId.value = promptId as string
const id = promptId as string
await settingsStore.setDefaultSystemPromptId(id)
selectedSystemPromptId.value = id

if (id === EMPTY_SYSTEM_PROMPT_ID) {
systemPrompts.value = systemPrompts.value.map((prompt) => ({
...prompt,
isDefault: false
}))
currentSystemPrompt.value = null
return
}

systemPrompts.value = systemPrompts.value.map((prompt) => ({
...prompt,
isDefault: prompt.id === id
}))
updateCurrentSystemPrompt()
} catch (error) {
console.error('Failed to change default system prompt:', error)
Expand Down
31 changes: 30 additions & 1 deletion src/renderer/src/components/NewThread.vue
Original file line number Diff line number Diff line change
Expand Up @@ -104,12 +104,14 @@ import ModelSelect from './ModelSelect.vue'
import { useChatStore } from '@/stores/chat'
import { MODEL_META } from '@shared/presenter'
import { useSettingsStore } from '@/stores/settings'
import { computed, nextTick, ref, watch, onMounted } from 'vue'
import { computed, nextTick, onBeforeUnmount, onMounted, ref, watch } from 'vue'
import { UserMessageContent } from '@shared/chat'
import ChatConfig from './ChatConfig.vue'
import { usePresenter } from '@/composables/usePresenter'
import { useThemeStore } from '@/stores/theme'
import { ModelType } from '@shared/model'
import type { IpcRendererEvent } from 'electron'
import { CONFIG_EVENTS } from '@/events'

const configPresenter = usePresenter('configPresenter')
const themeStore = useThemeStore()
Expand Down Expand Up @@ -150,6 +152,19 @@ const searchStrategy = ref<'turbo' | 'max' | undefined>(undefined)
const reasoningEffort = ref<'minimal' | 'low' | 'medium' | 'high' | undefined>(undefined)
const verbosity = ref<'low' | 'medium' | 'high' | undefined>(undefined)

const handleDefaultSystemPromptChange = async (
_event: IpcRendererEvent,
payload: { promptId?: string; content?: string }
) => {
if (typeof payload?.content === 'string') {
systemPrompt.value = payload.content
return
}

const prompt = await configPresenter.getDefaultSystemPrompt()
systemPrompt.value = prompt
}
Comment on lines +155 to +166
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 | 🟠 Major

Add error handling for async call.

The configPresenter.getDefaultSystemPrompt() call (line 164) lacks error handling. As per coding guidelines, async operations should be wrapped in try-catch blocks to handle potential errors gracefully.

Apply this diff to add error handling:

 const handleDefaultSystemPromptChange = async (
   _event: IpcRendererEvent,
   payload: { promptId?: string; content?: string }
 ) => {
   if (typeof payload?.content === 'string') {
     systemPrompt.value = payload.content
     return
   }
 
-  const prompt = await configPresenter.getDefaultSystemPrompt()
-  systemPrompt.value = prompt
+  try {
+    const prompt = await configPresenter.getDefaultSystemPrompt()
+    systemPrompt.value = prompt
+  } catch (error) {
+    console.error('Failed to get default system prompt:', error)
+  }
 }

Based on coding guidelines.

📝 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 handleDefaultSystemPromptChange = async (
_event: IpcRendererEvent,
payload: { promptId?: string; content?: string }
) => {
if (typeof payload?.content === 'string') {
systemPrompt.value = payload.content
return
}
const prompt = await configPresenter.getDefaultSystemPrompt()
systemPrompt.value = prompt
}
const handleDefaultSystemPromptChange = async (
_event: IpcRendererEvent,
payload: { promptId?: string; content?: string }
) => {
if (typeof payload?.content === 'string') {
systemPrompt.value = payload.content
return
}
try {
const prompt = await configPresenter.getDefaultSystemPrompt()
systemPrompt.value = prompt
} catch (error) {
console.error('Failed to get default system prompt:', error)
}
}
🤖 Prompt for AI Agents
In src/renderer/src/components/NewThread.vue around lines 155 to 166, the async
call to configPresenter.getDefaultSystemPrompt() lacks error handling; wrap the
await call in a try-catch, assign systemPrompt.value to the fetched prompt on
success, and in the catch log the error (using the component/logger utility or
console.error) and set systemPrompt.value to a sensible fallback (e.g., empty
string or existing default) so the UI remains stable.


const name = computed(() => {
return activeModel.value?.name ? activeModel.value.name.split('/').pop() : ''
})
Expand Down Expand Up @@ -382,13 +397,27 @@ watch(
)

onMounted(async () => {
if (window.electron?.ipcRenderer) {
window.electron.ipcRenderer.on(
CONFIG_EVENTS.DEFAULT_SYSTEM_PROMPT_CHANGED,
handleDefaultSystemPromptChange
)
}

configPresenter.getDefaultSystemPrompt().then((prompt) => {
systemPrompt.value = prompt
})
// 组件激活时初始化一次默认模型
await initActiveModel()
})

onBeforeUnmount(() => {
window.electron?.ipcRenderer?.removeListener(
CONFIG_EVENTS.DEFAULT_SYSTEM_PROMPT_CHANGED,
handleDefaultSystemPromptChange
)
})

const handleSend = async (content: UserMessageContent) => {
const threadId = await chatStore.createThread(content.text, {
providerId: activeModel.value.providerId,
Expand Down
3 changes: 2 additions & 1 deletion src/renderer/src/events.ts
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,8 @@ export const CONFIG_EVENTS = {
SOUND_ENABLED_CHANGED: 'config:sound-enabled-changed', // 新增:声音启用状态变更事件
COPY_WITH_COT_CHANGED: 'config:copy-with-cot-enabled-changed',
THEME_CHANGED: 'config:theme-changed',
FONT_SIZE_CHANGED: 'config:font-size-changed'
FONT_SIZE_CHANGED: 'config:font-size-changed',
DEFAULT_SYSTEM_PROMPT_CHANGED: 'config:default-system-prompt-changed'
}

// 会话相关事件
Expand Down
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/en-US/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"editSystemPromptDesc": "Modify the selected system prompt",
"selectSystemPrompt": "Select System Prompt",
"systemPromptDescription": "The selected system prompt will be used as the default for new conversations",
"emptySystemPromptOption": "No System Prompt",
"emptySystemPromptDescription": "When this option is selected, new conversations will start without a system prompt.",
"preview": "Preview",
"systemPromptChanged": "System prompt changed successfully",
"systemPromptChangeFailed": "Failed to change system prompt",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/en-US/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,7 @@
},
"promptSetting": {
"resetToDefault": "Reset to default prompt",
"clear": "Clear prompt words",
"resetToDefaultSuccess": "Reset to default system prompt successfully",
"resetToDefaultFailed": "Failed to reset, please try again",
"clearSuccess": "System prompt cleared successfully",
"clearFailed": "Failed to clear, please try again"
"resetToDefaultFailed": "Failed to reset, please try again"
}
}
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/fa-IR/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"editSystemPromptDesc": "تغییر دستورکار سامانه انتخاب شده",
"selectSystemPrompt": "انتخاب دستورکار سامانه",
"systemPromptDescription": "دستورکار سامانه انتخاب شده به عنوان پیش‌فرض برای گفت‌وگوهای جدید استفاده خواهد شد",
"emptySystemPromptOption": "بدون دستورکار سامانه",
"emptySystemPromptDescription": "با انتخاب این گزینه، گفتگوهای جدید بدون دستورکار سامانه آغاز می‌شوند.",
"preview": "پیش‌نمایش",
"promptContent": "دستورکار",
"systemPromptChanged": "دستورکار سامانه با موفقیت تغییر کرد",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/fa-IR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,7 @@
},
"promptSetting": {
"resetToDefault": "تنظیم مجدد به پیش فرض کلمه سریع",
"clear": "کلمات سریع را پاک کنید",
"resetToDefaultSuccess": "تنظیم مجدد به اعلان سیستم پیش‌فرض با موفقیت انجام شد",
"resetToDefaultFailed": "تنظیم مجدد ناموفق، لطفا دوباره تلاش کنید",
"clearSuccess": "اعلان سیستم با موفقیت پاک شد",
"clearFailed": "پاک کردن ناموفق، لطفا دوباره تلاش کنید"
"resetToDefaultFailed": "تنظیم مجدد ناموفق، لطفا دوباره تلاش کنید"
}
}
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/fr-FR/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"editSystemPromptDesc": "Modifier le prompt système sélectionné",
"selectSystemPrompt": "Sélectionner un prompt système",
"systemPromptDescription": "Le prompt système sélectionné sera utilisé par défaut pour les nouvelles conversations",
"emptySystemPromptOption": "Aucun prompt système",
"emptySystemPromptDescription": "En sélectionnant cette option, les nouvelles conversations démarrent sans prompt système.",
"preview": "Aperçu",
"systemPromptChanged": "Prompt système modifié avec succès",
"systemPromptChangeFailed": "Échec de la modification du prompt système",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/fr-FR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,7 @@
},
"promptSetting": {
"resetToDefault": "Réinitialiser avec un mot rapide par défaut",
"clear": "Effacer les mots rapides",
"resetToDefaultSuccess": "Réinitialisé avec succès à l'invite système par défaut",
"resetToDefaultFailed": "Échec de la réinitialisation, veuillez réessayer",
"clearSuccess": "Invite système effacée avec succès",
"clearFailed": "Échec de l'effacement, veuillez réessayer"
"resetToDefaultFailed": "Échec de la réinitialisation, veuillez réessayer"
}
}
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/ja-JP/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"editSystemPromptDesc": "選択したシステムプロンプトを変更",
"selectSystemPrompt": "システムプロンプトを選択",
"systemPromptDescription": "選択したシステムプロンプトが新しい会話のデフォルトとして使用されます",
"emptySystemPromptOption": "システムプロンプトなし",
"emptySystemPromptDescription": "このオプションを選択すると、新しい会話はシステムプロンプトなしで開始されます。",
"preview": "プレビュー",
"promptContent": "プロンプト",
"systemPromptChanged": "システムプロンプトが変更されました",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/ja-JP/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,7 @@
},
"promptSetting": {
"resetToDefault": "デフォルトのプロンプトワードにリセットします",
"clear": "迅速な単語を明確にします",
"resetToDefaultSuccess": "デフォルトのシステムプロンプトに正常にリセットしました",
"resetToDefaultFailed": "リセットに失敗しました。もう一度お試しください",
"clearSuccess": "システムプロンプトが正常にクリアされました",
"clearFailed": "クリアに失敗しました。もう一度お試しください"
"resetToDefaultFailed": "リセットに失敗しました。もう一度お試しください"
}
}
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/ko-KR/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"editSystemPromptDesc": "선택한 시스템 프롬프트 수정",
"selectSystemPrompt": "시스템 프롬프트 선택",
"systemPromptDescription": "선택한 시스템 프롬프트가 새 대화의 기본값으로 사용됩니다",
"emptySystemPromptOption": "시스템 프롬프트 없음",
"emptySystemPromptDescription": "이 옵션을 선택하면 새 대화가 시스템 프롬프트 없이 시작됩니다.",
"preview": "미리보기",
"promptContent": "프롬프트",
"systemPromptChanged": "시스템 프롬프트가 변경되었습니다",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/ko-KR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,7 @@
},
"promptSetting": {
"resetToDefault": "기본 프롬프트 단어로 재설정하십시오",
"clear": "명확한 신속한 단어",
"resetToDefaultSuccess": "기본 시스템 프롬프트로 성공적으로 재설정되었습니다",
"resetToDefaultFailed": "재설정에 실패했습니다. 다시 시도해 주세요",
"clearSuccess": "시스템 프롬프트가 성공적으로 지워졌습니다",
"clearFailed": "지우기에 실패했습니다. 다시 시도해 주세요"
"resetToDefaultFailed": "재설정에 실패했습니다. 다시 시도해 주세요"
}
}
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/pt-BR/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"editSystemPromptDesc": "Modificar o prompt do sistema selecionado",
"selectSystemPrompt": "Selecionar Prompt do Sistema",
"systemPromptDescription": "O prompt do sistema selecionado será usado como padrão para novas conversas",
"emptySystemPromptOption": "Sem prompt do sistema",
"emptySystemPromptDescription": "Ao selecionar esta opção, as novas conversas começam sem prompt do sistema.",
"preview": "Visualização",
"systemPromptChanged": "Prompt do sistema alterado com sucesso",
"systemPromptChangeFailed": "Falha ao alterar prompt do sistema",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/pt-BR/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,7 @@
},
"promptSetting": {
"resetToDefault": "Redefinir para o prompt padrão",
"clear": "Limpar palavras do prompt",
"resetToDefaultSuccess": "Redefinido para o prompt do sistema padrão com sucesso",
"resetToDefaultFailed": "Falha ao redefinir, tente novamente",
"clearSuccess": "Prompt do sistema limpo com sucesso",
"clearFailed": "Falha ao limpar, tente novamente"
"resetToDefaultFailed": "Falha ao redefinir, tente novamente"
}
}
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/ru-RU/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -73,6 +73,8 @@
"editSystemPromptDesc": "Изменить выбранный системный промпт",
"selectSystemPrompt": "Выбрать системный промпт",
"systemPromptDescription": "Выбранный системный промпт будет использоваться по умолчанию для новых разговоров",
"emptySystemPromptOption": "Без системного промпта",
"emptySystemPromptDescription": "При выборе этого варианта новые беседы будут начинаться без системного промпта.",
"preview": "Предварительный просмотр",
"promptContent": "Промпт",
"systemPromptChanged": "Системный промпт успешно изменен",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/ru-RU/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -817,10 +817,7 @@
},
"promptSetting": {
"resetToDefault": "Сбросить в приглашение по умолчанию слово",
"clear": "Чистые быстрые слова",
"resetToDefaultSuccess": "Успешно сброшено к системному приглашению по умолчанию",
"resetToDefaultFailed": "Ошибка сброса, пожалуйста, попробуйте еще раз",
"clearSuccess": "Системное приглашение успешно очищено",
"clearFailed": "Ошибка очистки, пожалуйста, попробуйте еще раз"
"resetToDefaultFailed": "Ошибка сброса, пожалуйста, попробуйте еще раз"
}
}
2 changes: 2 additions & 0 deletions src/renderer/src/i18n/zh-CN/promptSetting.json
Original file line number Diff line number Diff line change
Expand Up @@ -76,6 +76,8 @@
"editSystemPromptDesc": "修改选中的系统提示词",
"selectSystemPrompt": "选择系统提示词",
"systemPromptDescription": "选择的系统提示词将作为新对话的默认系统提示词",
"emptySystemPromptOption": "空系统提示词",
"emptySystemPromptDescription": "选择此项后,新会话将不包含系统提示词。",
"preview": "预览",
"systemPromptChanged": "系统提示词已更改",
"systemPromptChangeFailed": "更改系统提示词失败",
Expand Down
5 changes: 1 addition & 4 deletions src/renderer/src/i18n/zh-CN/settings.json
Original file line number Diff line number Diff line change
Expand Up @@ -816,11 +816,8 @@
"disabledDescription": "速率限制功能已关闭"
},
"promptSetting": {
"clear": "清空提示词",
"resetToDefault": "重置为默认提示词",
"resetToDefaultSuccess": "已重置为默认系统提示词",
"resetToDefaultFailed": "重置失败,请重试",
"clearSuccess": "已清空系统提示词",
"clearFailed": "清空失败,请重试"
"resetToDefaultFailed": "重置失败,请重试"
}
}
Loading