实现 system prompt 的设置和保存
This commit is contained in:
parent
27e94efa26
commit
85c26a3cbf
12
renderer/package-lock.json
generated
12
renderer/package-lock.json
generated
@ -11,6 +11,8 @@
|
||||
"core-js": "^3.8.3",
|
||||
"element-plus": "^2.9.7",
|
||||
"katex": "^0.16.21",
|
||||
"loadash": "^1.0.0",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-katex": "^2.0.3",
|
||||
"openai": "^4.93.0",
|
||||
@ -20,6 +22,7 @@
|
||||
"vue-router": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
@ -8620,6 +8623,13 @@
|
||||
"uc.micro": "^2.0.0"
|
||||
}
|
||||
},
|
||||
"node_modules/loadash": {
|
||||
"version": "1.0.0",
|
||||
"resolved": "https://registry.npmjs.org/loadash/-/loadash-1.0.0.tgz",
|
||||
"integrity": "sha512-xlX5HBsXB3KG0FJbJJG/3kYWCfsCyCSus3T+uHVu6QL6YxAdggmm3QeyLgn54N2yi5/UE6xxL5ZWJAAiHzHYEg==",
|
||||
"deprecated": "Package is unsupport. Please use the lodash package instead.",
|
||||
"license": "ISC"
|
||||
},
|
||||
"node_modules/loader-runner": {
|
||||
"version": "4.3.0",
|
||||
"resolved": "https://registry.npmmirror.com/loader-runner/-/loader-runner-4.3.0.tgz",
|
||||
@ -8690,7 +8700,7 @@
|
||||
},
|
||||
"node_modules/lodash": {
|
||||
"version": "4.17.21",
|
||||
"resolved": "https://registry.npmmirror.com/lodash/-/lodash-4.17.21.tgz",
|
||||
"resolved": "https://registry.npmjs.org/lodash/-/lodash-4.17.21.tgz",
|
||||
"integrity": "sha512-v2kDEe57lecTulaDIuNTPy3Ry4gLGJ6Z1O3vE1krgXZNrsQ+LFTGHVxVjcXPs17LhbZVGedAJv8XZ1tvj5FvSg==",
|
||||
"license": "MIT"
|
||||
},
|
||||
|
@ -11,6 +11,7 @@
|
||||
"core-js": "^3.8.3",
|
||||
"element-plus": "^2.9.7",
|
||||
"katex": "^0.16.21",
|
||||
"lodash": "^4.17.21",
|
||||
"markdown-it": "^14.1.0",
|
||||
"markdown-it-katex": "^2.0.3",
|
||||
"openai": "^4.93.0",
|
||||
@ -20,6 +21,7 @@
|
||||
"vue-router": "^4.0.3"
|
||||
},
|
||||
"devDependencies": {
|
||||
"@types/lodash": "^4.17.16",
|
||||
"@types/markdown-it": "^14.1.2",
|
||||
"@typescript-eslint/eslint-plugin": "^5.4.0",
|
||||
"@typescript-eslint/parser": "^5.4.0",
|
||||
|
@ -65,7 +65,6 @@ body::-webkit-scrollbar {
|
||||
}
|
||||
|
||||
.el-textarea__inner {
|
||||
border-radius: .9em !important;
|
||||
padding: 10px !important;
|
||||
box-shadow: 0 0 0 1px var(--main-color) !important;
|
||||
}
|
||||
|
@ -7,6 +7,7 @@
|
||||
:placeholder="placeholder"
|
||||
:resize="resize"
|
||||
:class="customClass"
|
||||
class="k-cute-textarea"
|
||||
@keydown.enter="handleKeydown"
|
||||
@compositionstart="handleCompositionStart"
|
||||
@compositionend="handleCompositionEnd"
|
||||
@ -68,3 +69,11 @@ const handleCompositionEnd = () => {
|
||||
isComposing.value = false;
|
||||
};
|
||||
</script>
|
||||
|
||||
<style>
|
||||
.k-cute-textarea textarea {
|
||||
border-radius: .9em;
|
||||
}
|
||||
|
||||
|
||||
</style>
|
@ -1,4 +1,5 @@
|
||||
import { useMessageBridge } from "@/api/message-bridge";
|
||||
import { pinkLog } from "@/views/setting/util";
|
||||
import { ref } from "vue";
|
||||
|
||||
interface SystemPrompt {
|
||||
@ -6,14 +7,50 @@ interface SystemPrompt {
|
||||
content: string;
|
||||
}
|
||||
|
||||
export const systemPrompt = ref<SystemPrompt>({
|
||||
name: '默认',
|
||||
export const systemPrompts = ref<SystemPrompt[]>([{
|
||||
name: 'Default',
|
||||
content: '你是一个AI助手, 你可以回答任何问题。'
|
||||
});
|
||||
}]);
|
||||
|
||||
export function saveSystemPrompts() {
|
||||
export async function saveSystemPrompts() {
|
||||
const bridge = useMessageBridge();
|
||||
return new Promise(resolve => {
|
||||
|
||||
})
|
||||
const payload = JSON.parse(JSON.stringify(systemPrompts.value));
|
||||
const res = await bridge.commandRequest('system-prompts/save', { prompts: payload });
|
||||
if (res.code === 200) {
|
||||
pinkLog('system prompt 保存成功');
|
||||
}
|
||||
}
|
||||
|
||||
export async function setSystemPrompt(name: string, content: string) {
|
||||
const bridge = useMessageBridge();
|
||||
const res = await bridge.commandRequest('system-prompts/set', { name, content });
|
||||
if (res.code === 200) {
|
||||
pinkLog('system prompt 添加成功');
|
||||
if (!systemPrompts.value.some(prompt => prompt.name === name)) {
|
||||
systemPrompts.value.push({ name, content });
|
||||
}
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function deleteSystemPrompt(name: string) {
|
||||
const bridge = useMessageBridge();
|
||||
const res = await bridge.commandRequest('system-prompts/delete', { name });
|
||||
if (res.code === 200) {
|
||||
pinkLog('system prompt 删除成功');
|
||||
systemPrompts.value = systemPrompts.value.filter((prompt) => prompt.name !== name);
|
||||
}
|
||||
return res;
|
||||
}
|
||||
|
||||
export async function loadSystemPrompts() {
|
||||
const bridge = useMessageBridge();
|
||||
const res = await bridge.commandRequest('system-prompts/load');
|
||||
if (res.code === 200) {
|
||||
pinkLog('system prompt 加载成功');
|
||||
systemPrompts.value = res.msg;
|
||||
}
|
||||
|
||||
return res;
|
||||
}
|
@ -6,33 +6,165 @@
|
||||
</div>
|
||||
</el-tooltip>
|
||||
|
||||
<!-- System Prompt对话框 -->
|
||||
<el-dialog v-model="showSystemPromptDialog" :title="t('system-prompt')" width="600px">
|
||||
<el-input v-model="tabStorage.settings.systemPrompt" type="textarea" :rows="8"
|
||||
:placeholder="t('system-prompt.placeholder')" clearable />
|
||||
<template #footer>
|
||||
|
||||
<div v-if="!showAdd">
|
||||
<el-select v-model="tabStorage.settings.systemPrompt" placeholder="选择预设"
|
||||
style="width: 100%; margin-bottom: 20px;">
|
||||
|
||||
<el-option v-for="prompt in systemPrompts"
|
||||
:value="prompt.name" :key="prompt.name"
|
||||
class="choose-system-prompt"
|
||||
>
|
||||
<span>{{ prompt.name }}</span>
|
||||
<el-dropdown trigger="hover" @command="handleCommand">
|
||||
<span>
|
||||
<span class="iconfont icon-more"></span>
|
||||
</span>
|
||||
<template #dropdown>
|
||||
<el-dropdown-menu>
|
||||
<el-dropdown-item :command="{ type: 'delete', name: prompt.name }" divided>
|
||||
<span class="iconfont icon-delete"> {{ t('delete') }}</span>
|
||||
</el-dropdown-item>
|
||||
</el-dropdown-menu>
|
||||
</template>
|
||||
</el-dropdown>
|
||||
</el-option>
|
||||
|
||||
<el-option label="新建预设" value="$new" @click="showAdd = true; newPromptName = ''; newPromptContent = ''">
|
||||
<span class="iconfont icon-add"></span>
|
||||
</el-option>
|
||||
</el-select>
|
||||
|
||||
<el-input v-model="currentPromptValue" type="textarea" :rows="8"
|
||||
:placeholder="t('system-prompt.placeholder')" clearable />
|
||||
</div>
|
||||
|
||||
<div v-else class="tool-use">
|
||||
<el-input v-model="newPromptName" :placeholder="t('add-system-prompt.name-placeholder')" />
|
||||
<el-input v-model="newPromptContent" type="textarea" :rows="8" :placeholder="t('system-prompt.placeholder')"
|
||||
clearable />
|
||||
</div>
|
||||
|
||||
<template #footer v-if="!showAdd">
|
||||
<el-button @click="showSystemPromptDialog = false">{{ t("cancel") }}</el-button>
|
||||
<el-button type="primary" @click="showSystemPromptDialog = false">{{ t("save") }}</el-button>
|
||||
</template>
|
||||
|
||||
<template #footer v-else>
|
||||
<el-button @click="showAdd = false">{{ t("cancel") }}</el-button>
|
||||
<el-button type="primary" @click="addNewPrompt">{{ t("save") }}</el-button>
|
||||
</template>
|
||||
|
||||
</el-dialog>
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject } from 'vue';
|
||||
import { ref, computed, inject, watch, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ChatStorage } from '../chat';
|
||||
import { systemPrompts, saveSystemPrompts, setSystemPrompt, loadSystemPrompts, deleteSystemPrompt } from './system-prompt';
|
||||
import { ElMessage } from 'element-plus';
|
||||
import { debounce } from 'lodash';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||
|
||||
const showSystemPromptDialog = ref(false);
|
||||
|
||||
const showAdd = ref(false);
|
||||
|
||||
const hasSystemPrompt = computed(() => {
|
||||
return !!tabStorage.settings.systemPrompt?.trim();
|
||||
return !!tabStorage.settings.systemPrompt?.trim();
|
||||
});
|
||||
|
||||
const newPromptName = ref('');
|
||||
const newPromptContent = ref('');
|
||||
|
||||
const currentPromptValue = computed({
|
||||
get() {
|
||||
return systemPrompts.value.find(prompt => prompt.name === tabStorage.settings.systemPrompt)?.content || '';
|
||||
},
|
||||
set(value) {
|
||||
const prompt = systemPrompts.value.find(prompt => prompt.name === tabStorage.settings.systemPrompt);
|
||||
if (prompt) {
|
||||
prompt.content = value;
|
||||
safeSaveSystemPrompts();
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
|
||||
async function addNewPrompt() {
|
||||
const name = newPromptName.value.trim();
|
||||
const content = newPromptContent.value.trim();
|
||||
// 检查是否命名冲突
|
||||
if (systemPrompts.value.some(prompt => prompt.name === name)) {
|
||||
ElMessage.warning('预设名称已存在,请选择其他名称。');
|
||||
return;
|
||||
}
|
||||
|
||||
const res = await setSystemPrompt(name, content);
|
||||
|
||||
if (res.code === 200) {
|
||||
ElMessage.success('预设添加成功。');
|
||||
showAdd.value = false;
|
||||
tabStorage.settings.systemPrompt = name;
|
||||
|
||||
} else {
|
||||
ElMessage.error('添加预设失败。' + res.msg);
|
||||
}
|
||||
}
|
||||
|
||||
const safeSaveSystemPrompts = debounce(async () => {
|
||||
if (!showAdd.value) {
|
||||
const prompt = systemPrompts.value.find(prompt => prompt.name === tabStorage.settings.systemPrompt);
|
||||
if (prompt) {
|
||||
await setSystemPrompt(prompt.name, prompt.content);
|
||||
}
|
||||
}
|
||||
}, 500);
|
||||
|
||||
async function handleCommand(command: {type: string, name: string}) {
|
||||
if (command.type === 'delete') {
|
||||
const res = await deleteSystemPrompt(command.name);
|
||||
if (res.code === 200) {
|
||||
if (tabStorage.settings.systemPrompt === command.name) {
|
||||
tabStorage.settings.systemPrompt = systemPrompts.value[0]?.name || '';
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
onMounted(async () => {
|
||||
await loadSystemPrompts();
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style></style>
|
||||
<style>
|
||||
.setting-button {
|
||||
cursor: pointer;
|
||||
padding: 8px;
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.3s;
|
||||
}
|
||||
|
||||
.setting-button:hover {
|
||||
background-color: rgba(0, 0, 0, 0.1);
|
||||
}
|
||||
|
||||
.setting-button.active {
|
||||
color: var(--el-color-primary);
|
||||
}
|
||||
|
||||
.tool-use .el-input__wrapper {
|
||||
margin-bottom: 20px;
|
||||
border-radius: .5em;
|
||||
}
|
||||
|
||||
.choose-system-prompt {
|
||||
display: flex;
|
||||
justify-content: space-between;
|
||||
align-items: center;
|
||||
width: 100%;
|
||||
}
|
||||
</style>
|
@ -35,10 +35,11 @@
|
||||
</template>
|
||||
|
||||
<script setup lang="ts">
|
||||
import { ref, computed, inject } from 'vue';
|
||||
import { ref, computed, inject, onMounted } from 'vue';
|
||||
import { useI18n } from 'vue-i18n';
|
||||
import { ChatStorage, getToolSchema } from '../chat';
|
||||
import { allTools, ChatStorage, getToolSchema } from '../chat';
|
||||
import { markdownToHtml } from '../markdown/markdown';
|
||||
import { useMessageBridge } from '@/api/message-bridge';
|
||||
|
||||
const { t } = useI18n();
|
||||
|
||||
@ -80,6 +81,22 @@ const disableAllTools = () => {
|
||||
});
|
||||
};
|
||||
|
||||
onMounted(async () => {
|
||||
const bridge = useMessageBridge();
|
||||
const res = await bridge.commandRequest('tools/list');
|
||||
if (res.code === 200) {
|
||||
allTools.value = res.msg.tools || [];
|
||||
tabStorage.settings.enableTools = [];
|
||||
for (const tool of allTools.value) {
|
||||
tabStorage.settings.enableTools.push({
|
||||
name: tool.name,
|
||||
description: tool.description,
|
||||
enabled: true
|
||||
});
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
</script>
|
||||
|
||||
<style></style>
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "محادثة متعددة الجولات",
|
||||
"press-and-run": "اكتب سؤالاً لبدء الاختبار",
|
||||
"connect-sigature": "توقيع الاتصال",
|
||||
"finish-refresh": "تم التحديث"
|
||||
"finish-refresh": "تم التحديث",
|
||||
"add-system-prompt.name-placeholder": "عنوان prompt المخصص"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "Mehrrundengespräch",
|
||||
"press-and-run": "Geben Sie eine Frage ein, um den Test zu starten",
|
||||
"connect-sigature": "Verbindungssignatur",
|
||||
"finish-refresh": "Aktualisierung abgeschlossen"
|
||||
"finish-refresh": "Aktualisierung abgeschlossen",
|
||||
"add-system-prompt.name-placeholder": "Titel für benutzerdefinierte Eingabeaufforderung"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "Multi-turn conversation",
|
||||
"press-and-run": "Type a question to start the test",
|
||||
"connect-sigature": "Connection signature",
|
||||
"finish-refresh": "Refresh completed"
|
||||
"finish-refresh": "Refresh completed",
|
||||
"add-system-prompt.name-placeholder": "Title for custom prompt"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "Conversation multi-tours",
|
||||
"press-and-run": "Tapez une question pour commencer le test",
|
||||
"connect-sigature": "Signature de connexion",
|
||||
"finish-refresh": "Actualisation terminée"
|
||||
"finish-refresh": "Actualisation terminée",
|
||||
"add-system-prompt.name-placeholder": "Titre de l'invite personnalisée"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "マルチターン会話",
|
||||
"press-and-run": "テストを開始するには質問を入力してください",
|
||||
"connect-sigature": "接続署名",
|
||||
"finish-refresh": "更新が完了しました"
|
||||
"finish-refresh": "更新が完了しました",
|
||||
"add-system-prompt.name-placeholder": "カスタムプロンプトのタイトル"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "다중 턴 대화",
|
||||
"press-and-run": "테스트를 시작하려면 질문을 입력하세요",
|
||||
"connect-sigature": "연결 서명",
|
||||
"finish-refresh": "새로 고침 완료"
|
||||
"finish-refresh": "새로 고침 완료",
|
||||
"add-system-prompt.name-placeholder": "사용자 지정 프롬프트 제목"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "Многораундовый разговор",
|
||||
"press-and-run": "Введите вопрос, чтобы начать тест",
|
||||
"connect-sigature": "Подпись соединения",
|
||||
"finish-refresh": "Обновление завершено"
|
||||
"finish-refresh": "Обновление завершено",
|
||||
"add-system-prompt.name-placeholder": "Заголовок пользовательского prompt"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "多轮对话",
|
||||
"press-and-run": "键入问题以开始测试",
|
||||
"connect-sigature": "连接签名",
|
||||
"finish-refresh": "完成刷新"
|
||||
"finish-refresh": "完成刷新",
|
||||
"add-system-prompt.name-placeholder": "输入自定义 prompt 的标题"
|
||||
}
|
@ -145,5 +145,6 @@
|
||||
"multi-dialog": "多輪對話",
|
||||
"press-and-run": "輸入問題以開始測試",
|
||||
"connect-sigature": "連接簽名",
|
||||
"finish-refresh": "刷新完成"
|
||||
"finish-refresh": "刷新完成",
|
||||
"add-system-prompt.name-placeholder": "自定義提示的標題"
|
||||
}
|
@ -146,7 +146,13 @@ interface OcrItem extends Entity {
|
||||
createTime: number;
|
||||
}
|
||||
|
||||
interface SystemPromptItem extends Entity {
|
||||
name: string;
|
||||
content: string;
|
||||
}
|
||||
|
||||
export const diskStorage = new DiskStorage();
|
||||
|
||||
export const settingDB = new LocalDB<SettingItem>('setting');
|
||||
export const ocrDB = new LocalDB<OcrItem>('ocr');
|
||||
export const systemPromptDB = new LocalDB<SystemPromptItem>('systemPrompt');
|
@ -1,5 +1,6 @@
|
||||
import { Controller, RequestClientType } from "../common";
|
||||
import { PostMessageble } from "../hook/adapter";
|
||||
import { systemPromptDB } from "../hook/db";
|
||||
import { loadTabSaveConfig, saveTabSaveConfig } from "./panel.service";
|
||||
|
||||
export class PanelController {
|
||||
@ -25,4 +26,66 @@ export class PanelController {
|
||||
msg: config
|
||||
};
|
||||
}
|
||||
|
||||
@Controller('system-prompts/set')
|
||||
async setSystemPrompt(client: RequestClientType, data: any, webview: PostMessageble) {
|
||||
const { name, content } = data;
|
||||
|
||||
await systemPromptDB.insert({
|
||||
id: name,
|
||||
name,
|
||||
content
|
||||
});
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'Settings saved successfully'
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('system-prompts/delete')
|
||||
async deleteSystemPrompt(client: RequestClientType, data: any, webview: PostMessageble) {
|
||||
const { name } = data;
|
||||
await systemPromptDB.delete(name);
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'Settings saved successfully'
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('system-prompts/save')
|
||||
async saveSystemPrompts(client: RequestClientType, data: any, webview: PostMessageble) {
|
||||
const { prompts } = data;
|
||||
|
||||
await Promise.all(prompts.map((prompt: any) => {
|
||||
systemPromptDB.insert({
|
||||
id: prompt.name,
|
||||
name: prompt.name,
|
||||
content: prompt.content
|
||||
})
|
||||
}));
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
msg: 'Settings saved successfully'
|
||||
}
|
||||
}
|
||||
|
||||
@Controller('system-prompts/load')
|
||||
async loadSystemPrompts(client: RequestClientType, data: any, webview: PostMessageble) {
|
||||
|
||||
const queryPrompts = await systemPromptDB.findAll();
|
||||
const prompts = [];
|
||||
for (const prompt of queryPrompts) {
|
||||
prompts.push({
|
||||
name: prompt.name,
|
||||
content: prompt.content
|
||||
})
|
||||
}
|
||||
|
||||
return {
|
||||
code: 200,
|
||||
msg: prompts
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user