重构 chat 模块的 setting 面板
This commit is contained in:
parent
a535690bc6
commit
27e94efa26
@ -7,6 +7,11 @@ export interface VSCodeMessage {
|
|||||||
callbackId?: string;
|
callbackId?: string;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
export interface RestFulResponse {
|
||||||
|
code: number;
|
||||||
|
msg: any;
|
||||||
|
}
|
||||||
|
|
||||||
export type MessageHandler = (message: VSCodeMessage) => void;
|
export type MessageHandler = (message: VSCodeMessage) => void;
|
||||||
export type CommandHandler = (data: any) => void;
|
export type CommandHandler = (data: any) => void;
|
||||||
|
|
||||||
@ -135,6 +140,25 @@ class MessageBridge {
|
|||||||
return () => commandHandlers.delete(wrapperCommandHandler);
|
return () => commandHandlers.delete(wrapperCommandHandler);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @description do as axios does
|
||||||
|
* @param command
|
||||||
|
* @param data
|
||||||
|
* @returns
|
||||||
|
*/
|
||||||
|
public commandRequest(command: string, data?: any) {
|
||||||
|
return new Promise<RestFulResponse>((resolve, reject) => {
|
||||||
|
this.addCommandListener(command, (data) => {
|
||||||
|
resolve(data as RestFulResponse);
|
||||||
|
}, { once: true });
|
||||||
|
|
||||||
|
this.postMessage({
|
||||||
|
command,
|
||||||
|
data
|
||||||
|
});
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
public destroy() {
|
public destroy() {
|
||||||
this.ws?.close();
|
this.ws?.close();
|
||||||
this.handlers.clear();
|
this.handlers.clear();
|
||||||
@ -151,6 +175,7 @@ export function useMessageBridge() {
|
|||||||
return {
|
return {
|
||||||
postMessage: bridge.postMessage.bind(bridge),
|
postMessage: bridge.postMessage.bind(bridge),
|
||||||
addCommandListener: bridge.addCommandListener.bind(bridge),
|
addCommandListener: bridge.addCommandListener.bind(bridge),
|
||||||
|
commandRequest: bridge.commandRequest.bind(bridge),
|
||||||
isConnected: bridge.isConnected
|
isConnected: bridge.isConnected
|
||||||
};
|
};
|
||||||
}
|
}
|
@ -82,7 +82,7 @@ import { TaskLoop } from './task-loop';
|
|||||||
import { llmManager, llms } from '@/views/setting/llm';
|
import { llmManager, llms } from '@/views/setting/llm';
|
||||||
|
|
||||||
import * as Message from './message';
|
import * as Message from './message';
|
||||||
import Setting from './setting.vue';
|
import Setting from './options/setting.vue';
|
||||||
import KCuteTextarea from '@/components/k-cute-textarea/index.vue';
|
import KCuteTextarea from '@/components/k-cute-textarea/index.vue';
|
||||||
|
|
||||||
import { provide } from 'vue';
|
import { provide } from 'vue';
|
||||||
|
@ -8,7 +8,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
import { defineProps } from 'vue';
|
||||||
import { markdownToHtml } from '../markdown';
|
import { markdownToHtml } from '../markdown/markdown';
|
||||||
|
|
||||||
import MessageMeta from './message-meta.vue';
|
import MessageMeta from './message-meta.vue';
|
||||||
|
|
||||||
|
@ -19,7 +19,7 @@
|
|||||||
|
|
||||||
<script setup lang="ts">
|
<script setup lang="ts">
|
||||||
import { defineProps } from 'vue';
|
import { defineProps } from 'vue';
|
||||||
import { markdownToHtml } from '../markdown';
|
import { markdownToHtml } from '../markdown/markdown';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
streamingContent: {
|
streamingContent: {
|
||||||
|
@ -96,7 +96,7 @@
|
|||||||
import { defineProps, ref, watch, PropType, computed, defineEmits } from 'vue';
|
import { defineProps, ref, watch, PropType, computed, defineEmits } from 'vue';
|
||||||
|
|
||||||
import MessageMeta from './message-meta.vue';
|
import MessageMeta from './message-meta.vue';
|
||||||
import { markdownToHtml } from '../markdown';
|
import { markdownToHtml } from '../markdown/markdown';
|
||||||
import { createTest } from '@/views/setting/llm';
|
import { createTest } from '@/views/setting/llm';
|
||||||
import { IRenderMessage, MessageState } from '../chat';
|
import { IRenderMessage, MessageState } from '../chat';
|
||||||
import { ToolCallContent } from '@/hook/type';
|
import { ToolCallContent } from '@/hook/type';
|
||||||
|
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip :content="t('context-length')" placement="top">
|
||||||
|
<div class="setting-button" @click="showContextLengthDialog = true">
|
||||||
|
<span class="iconfont icon-length"></span>
|
||||||
|
<span class="value-badge">{{ tabStorage.settings.contextLength }}</span>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<!-- 上下文长度设置 - 改为滑块形式 -->
|
||||||
|
<el-dialog v-model="showContextLengthDialog" :title="t('context-length') + ' ' + tabStorage.settings.contextLength"
|
||||||
|
width="400px">
|
||||||
|
<div class="slider-container">
|
||||||
|
<el-slider v-model="tabStorage.settings.contextLength" :min="1" :max="99" :step="1" />
|
||||||
|
<div class="slider-tips">
|
||||||
|
<span> 1: {{ t('single-dialog') }}</span>
|
||||||
|
<span> >1: {{ t('multi-dialog') }}</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="showContextLengthDialog = false">{{ t("cancel") }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineComponent, inject, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ChatStorage } from '../chat';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||||
|
|
||||||
|
const showContextLengthDialog = ref(false);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon-length {
|
||||||
|
font-size: 16px;
|
||||||
|
}
|
||||||
|
</style>
|
67
renderer/src/components/main-panel/chat/options/model.vue
Normal file
67
renderer/src/components/main-panel/chat/options/model.vue
Normal file
@ -0,0 +1,67 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip :content="t('choose-model')" placement="top">
|
||||||
|
<div class="setting-button" @click="showModelDialog = true">
|
||||||
|
<span class="iconfont icon-model">
|
||||||
|
{{ currentServerName }}/{{ currentModelName }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<!-- 模型选择对话框 -->
|
||||||
|
<el-dialog v-model="showModelDialog" :title="t('choose-model')" width="400px">
|
||||||
|
<el-radio-group v-model="selectedModelIndex" @change="onRadioGroupChange">
|
||||||
|
<el-radio v-for="(model, index) in availableModels" :key="index" :value="index">
|
||||||
|
{{ model }}
|
||||||
|
</el-radio>
|
||||||
|
</el-radio-group>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="showModelDialog = false">{{ t("cancel") }}</el-button>
|
||||||
|
<el-button type="primary" @click="confirmModelChange">{{ t("confirm") }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { saveSetting } from '@/hook/setting';
|
||||||
|
import { llmManager, llms } from '@/views/setting/llm';
|
||||||
|
import { computed, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const showModelDialog = ref(false);
|
||||||
|
const currentModel = llms[llmManager.currentModelIndex].userModel;
|
||||||
|
const selectedModelIndex = ref(llms[llmManager.currentModelIndex].models.indexOf(currentModel));
|
||||||
|
|
||||||
|
const currentServerName = computed(() => {
|
||||||
|
const currentLlm = llms[llmManager.currentModelIndex];
|
||||||
|
if (currentLlm) {
|
||||||
|
return currentLlm.name;
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const currentModelName = computed(() => {
|
||||||
|
const currentLlm = llms[llmManager.currentModelIndex];
|
||||||
|
if (currentLlm) {
|
||||||
|
return currentLlm.models[selectedModelIndex.value];
|
||||||
|
}
|
||||||
|
return '';
|
||||||
|
});
|
||||||
|
|
||||||
|
const availableModels = computed(() => {
|
||||||
|
return llms[llmManager.currentModelIndex].models;
|
||||||
|
});
|
||||||
|
|
||||||
|
const confirmModelChange = () => {
|
||||||
|
showModelDialog.value = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
const onRadioGroupChange = () => {
|
||||||
|
const currentModel = llms[llmManager.currentModelIndex].models[selectedModelIndex.value];
|
||||||
|
llms[llmManager.currentModelIndex].userModel = currentModel;
|
||||||
|
saveSetting();
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
201
renderer/src/components/main-panel/chat/options/setting.vue
Normal file
201
renderer/src/components/main-panel/chat/options/setting.vue
Normal file
@ -0,0 +1,201 @@
|
|||||||
|
<template>
|
||||||
|
<div class="chat-settings">
|
||||||
|
<Model />
|
||||||
|
<SystemPrompt />
|
||||||
|
<ToolUse />
|
||||||
|
<Websearch />
|
||||||
|
<Temperature />
|
||||||
|
<ContextLength />
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { defineProps, provide } from 'vue';
|
||||||
|
import { llmManager } from '@/views/setting/llm';
|
||||||
|
import { tabs } from '../../panel';
|
||||||
|
import type { ChatSetting, ChatStorage } from '../chat';
|
||||||
|
|
||||||
|
import Model from './model.vue';
|
||||||
|
import SystemPrompt from './system-prompt.vue';
|
||||||
|
import ToolUse from './tool-use.vue';
|
||||||
|
import Websearch from './websearch.vue';
|
||||||
|
import Temperature from './temperature.vue';
|
||||||
|
import ContextLength from './context-length.vue';
|
||||||
|
|
||||||
|
const props = defineProps({
|
||||||
|
tabId: {
|
||||||
|
type: Number,
|
||||||
|
required: true
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
const tab = tabs.content[props.tabId];
|
||||||
|
const tabStorage = tab.storage as ChatStorage & { settings: ChatSetting };
|
||||||
|
|
||||||
|
if (!tabStorage.settings) {
|
||||||
|
tabStorage.settings = {
|
||||||
|
modelIndex: llmManager.currentModelIndex,
|
||||||
|
enableTools: [],
|
||||||
|
enableWebSearch: false,
|
||||||
|
temperature: 0.7,
|
||||||
|
contextLength: 10,
|
||||||
|
systemPrompt: ''
|
||||||
|
} as ChatSetting;
|
||||||
|
}
|
||||||
|
|
||||||
|
provide('tabStorage', tabStorage);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.chat-settings {
|
||||||
|
display: flex;
|
||||||
|
gap: 2px;
|
||||||
|
padding: 8px 0;
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
width: fit-content;
|
||||||
|
border-radius: 99%;
|
||||||
|
bottom: 0px;
|
||||||
|
z-index: 10;
|
||||||
|
position: absolute;
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-button {
|
||||||
|
padding: 5px 8px;
|
||||||
|
margin-right: 3px;
|
||||||
|
border-radius: .5em;
|
||||||
|
font-size: 12px;
|
||||||
|
position: relative;
|
||||||
|
user-select: none;
|
||||||
|
-webkit-user-drag: none;
|
||||||
|
display: flex;
|
||||||
|
align-items: center;
|
||||||
|
cursor: pointer;
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-button.active {
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-button.active:hover {
|
||||||
|
background-color: var(--el-color-primary);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.setting-button:hover {
|
||||||
|
background-color: var(--background);
|
||||||
|
transition: var(--animation-3s);
|
||||||
|
}
|
||||||
|
|
||||||
|
.value-badge {
|
||||||
|
font-size: 10px;
|
||||||
|
padding: 1px 4px;
|
||||||
|
border-radius: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-container {
|
||||||
|
padding: 0 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.slider-tips {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
margin-top: 10px;
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 新增工具相关样式 */
|
||||||
|
.tools-container {
|
||||||
|
padding: 10px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-item {
|
||||||
|
display: flex;
|
||||||
|
justify-content: space-between;
|
||||||
|
align-items: center;
|
||||||
|
padding: 12px 0;
|
||||||
|
border-bottom: 1px solid var(--el-border-color-light);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-info {
|
||||||
|
flex: 1;
|
||||||
|
margin-right: 20px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-name {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-bottom: 4px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tool-description {
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-secondary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-dialog-container .el-switch__core {
|
||||||
|
border: 1px solid var(--main-color) !important;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
.tools-dialog-container .el-switch .el-switch__action {
|
||||||
|
background-color: var(--main-color);
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-dialog-container .el-switch.is-checked .el-switch__action {
|
||||||
|
background-color: var(--sidebar);
|
||||||
|
}
|
||||||
|
|
||||||
|
/* 新增工具对话框样式 */
|
||||||
|
.tools-dialog-container {
|
||||||
|
display: flex;
|
||||||
|
gap: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.tools-list {
|
||||||
|
flex: 1;
|
||||||
|
border-right: 1px solid var(--el-border-color);
|
||||||
|
padding-right: 16px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema-viewer {
|
||||||
|
flex: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema-viewer pre {
|
||||||
|
margin: 0;
|
||||||
|
border-radius: 4px;
|
||||||
|
white-space: pre-wrap;
|
||||||
|
word-wrap: break-word;
|
||||||
|
background-color: var(--el-bg-color-overlay);
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema-viewer .openmcp-code-block {
|
||||||
|
border: none;
|
||||||
|
}
|
||||||
|
|
||||||
|
.schema-viewer code {
|
||||||
|
font-family: var(--code-font-family);
|
||||||
|
font-size: 12px;
|
||||||
|
color: var(--el-text-color-primary);
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-outer {
|
||||||
|
position: relative;
|
||||||
|
}
|
||||||
|
|
||||||
|
.badge-inner {
|
||||||
|
position: absolute;
|
||||||
|
color: var(--foreground);
|
||||||
|
background-color: var(--main-color);
|
||||||
|
border-radius: 50%;
|
||||||
|
padding: 2px 6px;
|
||||||
|
font-size: 10px;
|
||||||
|
top: -16px;
|
||||||
|
right: -18px;
|
||||||
|
box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
|
||||||
|
}
|
||||||
|
</style>
|
@ -0,0 +1,19 @@
|
|||||||
|
import { useMessageBridge } from "@/api/message-bridge";
|
||||||
|
import { ref } from "vue";
|
||||||
|
|
||||||
|
interface SystemPrompt {
|
||||||
|
name: string;
|
||||||
|
content: string;
|
||||||
|
}
|
||||||
|
|
||||||
|
export const systemPrompt = ref<SystemPrompt>({
|
||||||
|
name: '默认',
|
||||||
|
content: '你是一个AI助手, 你可以回答任何问题。'
|
||||||
|
});
|
||||||
|
|
||||||
|
export function saveSystemPrompts() {
|
||||||
|
const bridge = useMessageBridge();
|
||||||
|
return new Promise(resolve => {
|
||||||
|
|
||||||
|
})
|
||||||
|
}
|
@ -0,0 +1,38 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip :content="t('system-prompt')" placement="top">
|
||||||
|
<div class="setting-button" :class="{ 'active': hasSystemPrompt }" size="small"
|
||||||
|
@click="showSystemPromptDialog = true">
|
||||||
|
<span class="iconfont icon-prompt"></span>
|
||||||
|
</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>
|
||||||
|
<el-button @click="showSystemPromptDialog = false">{{ t("cancel") }}</el-button>
|
||||||
|
<el-button type="primary" @click="showSystemPromptDialog = false">{{ t("save") }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, inject } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ChatStorage } from '../chat';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||||
|
|
||||||
|
const showSystemPromptDialog = ref(false);
|
||||||
|
|
||||||
|
const hasSystemPrompt = computed(() => {
|
||||||
|
return !!tabStorage.settings.systemPrompt?.trim();
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
@ -0,0 +1,42 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip :content="t('temperature-parameter')" placement="top">
|
||||||
|
<div class="setting-button" @click="showTemperatureSlider = true">
|
||||||
|
<span class="iconfont icon-temperature"></span>
|
||||||
|
<span class="value-badge">{{ tabStorage.settings.temperature.toFixed(1) }}</span>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<!-- 温度参数滑块 -->
|
||||||
|
<el-dialog v-model="showTemperatureSlider" :title="t('temperature-parameter')" width="400px">
|
||||||
|
<div class="slider-container">
|
||||||
|
<el-slider v-model="tabStorage.settings.temperature" :min="0" :max="2" :step="0.1" />
|
||||||
|
<div class="slider-tips">
|
||||||
|
<span> {{ t('precise') }}(0)</span>
|
||||||
|
<span>{{ t('moderate') }}(1)</span>
|
||||||
|
<span>{{ t('creative') }}(2)</span>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button @click="showTemperatureSlider = false">{{ t("cancel") }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { inject, ref } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ChatStorage } from '../chat';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||||
|
|
||||||
|
const showTemperatureSlider = ref(false);
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
.icon-temperature {
|
||||||
|
font-size: 18px;
|
||||||
|
}
|
||||||
|
|
||||||
|
</style>
|
85
renderer/src/components/main-panel/chat/options/tool-use.vue
Normal file
85
renderer/src/components/main-panel/chat/options/tool-use.vue
Normal file
@ -0,0 +1,85 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip :content="t('tool-use')" placement="top">
|
||||||
|
<div class="setting-button" :class="{ 'active': availableToolsNum > 0 }" size="small" @click="toggleTools">
|
||||||
|
<span class="iconfont icon-tool badge-outer">
|
||||||
|
<span class="badge-inner">
|
||||||
|
{{ availableToolsNum }}
|
||||||
|
</span>
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
</el-tooltip>
|
||||||
|
|
||||||
|
<el-dialog v-model="showToolsDialog" title="工具管理" width="800px">
|
||||||
|
<div class="tools-dialog-container">
|
||||||
|
<el-scrollbar height="400px" class="tools-list">
|
||||||
|
<div v-for="(tool, index) in tabStorage.settings.enableTools" :key="index" class="tool-item">
|
||||||
|
<div class="tool-info">
|
||||||
|
<div class="tool-name">{{ tool.name }}</div>
|
||||||
|
<div v-if="tool.description" class="tool-description">{{ tool.description }}</div>
|
||||||
|
</div>
|
||||||
|
<el-switch v-model="tool.enabled" />
|
||||||
|
</div>
|
||||||
|
</el-scrollbar>
|
||||||
|
|
||||||
|
<el-scrollbar height="400px" class="schema-viewer">
|
||||||
|
<div v-html="activeToolsSchemaHTML"></div>
|
||||||
|
</el-scrollbar>
|
||||||
|
</div>
|
||||||
|
<template #footer>
|
||||||
|
<el-button type="primary" @click="enableAllTools">激活所有工具</el-button>
|
||||||
|
<el-button type="danger" @click="disableAllTools">禁用所有工具</el-button>
|
||||||
|
<el-button type="primary" @click="showToolsDialog = false">{{ t("cancel") }}</el-button>
|
||||||
|
</template>
|
||||||
|
</el-dialog>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { ref, computed, inject } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ChatStorage, getToolSchema } from '../chat';
|
||||||
|
import { markdownToHtml } from '../markdown/markdown';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||||
|
|
||||||
|
const showToolsDialog = ref(false);
|
||||||
|
|
||||||
|
const availableToolsNum = computed(() => {
|
||||||
|
return tabStorage.settings.enableTools.filter(tool => tool.enabled).length;
|
||||||
|
});
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
|
// 修改 toggleTools 方法
|
||||||
|
const toggleTools = () => {
|
||||||
|
showToolsDialog.value = true;
|
||||||
|
};
|
||||||
|
|
||||||
|
const activeToolsSchemaHTML = computed(() => {
|
||||||
|
const toolsSchema = getToolSchema(tabStorage.settings.enableTools);
|
||||||
|
const jsonString = JSON.stringify(toolsSchema, null, 2);
|
||||||
|
|
||||||
|
return markdownToHtml(
|
||||||
|
"```json\n" + jsonString + "\n```"
|
||||||
|
);
|
||||||
|
});
|
||||||
|
|
||||||
|
// 新增方法 - 激活所有工具
|
||||||
|
const enableAllTools = () => {
|
||||||
|
tabStorage.settings.enableTools.forEach(tool => {
|
||||||
|
tool.enabled = true;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
// 新增方法 - 禁用所有工具
|
||||||
|
const disableAllTools = () => {
|
||||||
|
tabStorage.settings.enableTools.forEach(tool => {
|
||||||
|
tool.enabled = false;
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style></style>
|
@ -0,0 +1,26 @@
|
|||||||
|
<template>
|
||||||
|
<el-tooltip :content="t('websearch')" placement="top">
|
||||||
|
<div class="setting-button" :class="{ 'active': tabStorage.settings.enableWebSearch }" size="small"
|
||||||
|
@click="toggleWebSearch">
|
||||||
|
<span class="iconfont icon-web"></span>
|
||||||
|
</div>
|
||||||
|
</el-tooltip>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { inject } from 'vue';
|
||||||
|
import { useI18n } from 'vue-i18n';
|
||||||
|
import { ChatStorage } from '../chat';
|
||||||
|
|
||||||
|
const { t } = useI18n();
|
||||||
|
|
||||||
|
const tabStorage = inject('tabStorage') as ChatStorage;
|
||||||
|
|
||||||
|
const toggleWebSearch = () => {
|
||||||
|
tabStorage.settings.enableWebSearch = !tabStorage.settings.enableWebSearch;
|
||||||
|
};
|
||||||
|
|
||||||
|
</script>
|
||||||
|
|
||||||
|
<style>
|
||||||
|
</style>
|
@ -1,438 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div class="chat-settings">
|
|
||||||
<el-tooltip :content="t('choose-model')" placement="top">
|
|
||||||
<div class="setting-button" @click="showModelDialog = true">
|
|
||||||
<span class="iconfont icon-model">
|
|
||||||
{{ currentServerName }}/{{ currentModelName }}
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<el-tooltip :content="t('system-prompt')" placement="top">
|
|
||||||
<div class="setting-button" :class="{ 'active': hasSystemPrompt }" size="small"
|
|
||||||
@click="showSystemPromptDialog = true">
|
|
||||||
<span class="iconfont icon-prompt"></span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<el-tooltip :content="t('tool-use')" placement="top">
|
|
||||||
<div class="setting-button" :class="{ 'active': availableToolsNum > 0 }" size="small"
|
|
||||||
@click="toggleTools">
|
|
||||||
<span class="iconfont icon-tool badge-outer">
|
|
||||||
<span class="badge-inner">
|
|
||||||
{{ availableToolsNum }}
|
|
||||||
</span>
|
|
||||||
</span>
|
|
||||||
</div>
|
|
||||||
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<el-tooltip :content="t('websearch')" placement="top">
|
|
||||||
<div class="setting-button" :class="{ 'active': tabStorage.settings.enableWebSearch }" size="small"
|
|
||||||
@click="toggleWebSearch">
|
|
||||||
<span class="iconfont icon-web"></span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<el-tooltip :content="t('temperature-parameter')" placement="top">
|
|
||||||
<div class="setting-button" @click="showTemperatureSlider = true">
|
|
||||||
<span class="iconfont icon-temperature"></span>
|
|
||||||
<span class="value-badge">{{ tabStorage.settings.temperature.toFixed(1) }}</span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<el-tooltip :content="t('context-length')" placement="top">
|
|
||||||
<div class="setting-button" @click="showContextLengthDialog = true">
|
|
||||||
<span class="iconfont icon-length"></span>
|
|
||||||
<span class="value-badge">{{ tabStorage.settings.contextLength }}</span>
|
|
||||||
</div>
|
|
||||||
</el-tooltip>
|
|
||||||
|
|
||||||
<!-- 模型选择对话框 -->
|
|
||||||
<el-dialog v-model="showModelDialog" :title="t('choose-model')" width="400px">
|
|
||||||
<el-radio-group v-model="selectedModelIndex" @change="onRadioGroupChange">
|
|
||||||
<el-radio v-for="(model, index) in availableModels" :key="index" :label="index">
|
|
||||||
{{ model }}
|
|
||||||
</el-radio>
|
|
||||||
</el-radio-group>
|
|
||||||
<template #footer>
|
|
||||||
<el-button @click="showModelDialog = false">{{ t("cancel") }}</el-button>
|
|
||||||
<el-button type="primary" @click="confirmModelChange">{{ t("confirm") }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 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>
|
|
||||||
<el-button @click="showSystemPromptDialog = false">{{ t("cancel") }}</el-button>
|
|
||||||
<el-button type="primary" @click="showSystemPromptDialog = false">{{ t("save") }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 温度参数滑块 -->
|
|
||||||
<el-dialog v-model="showTemperatureSlider" :title="t('temperature-parameter')" width="400px">
|
|
||||||
<div class="slider-container">
|
|
||||||
<el-slider v-model="tabStorage.settings.temperature" :min="0" :max="2" :step="0.1" />
|
|
||||||
<div class="slider-tips">
|
|
||||||
<span> {{ t('precise') }}(0)</span>
|
|
||||||
<span>{{ t('moderate') }}(1)</span>
|
|
||||||
<span>{{ t('creative') }}(2)</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<el-button @click="showTemperatureSlider = false">{{ t("cancel") }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 上下文长度设置 - 改为滑块形式 -->
|
|
||||||
<el-dialog v-model="showContextLengthDialog" :title="t('context-length') + ' ' + tabStorage.settings.contextLength" width="400px">
|
|
||||||
<div class="slider-container">
|
|
||||||
<el-slider v-model="tabStorage.settings.contextLength" :min="1" :max="99" :step="1" />
|
|
||||||
<div class="slider-tips">
|
|
||||||
<span> 1: {{ t('single-dialog') }}</span>
|
|
||||||
<span> >1: {{ t('multi-dialog') }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<el-button @click="showContextLengthDialog = false">{{ t("cancel") }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
|
|
||||||
<!-- 修改后的工具使用对话框 -->
|
|
||||||
<el-dialog v-model="showToolsDialog" title="工具管理" width="800px">
|
|
||||||
<div class="tools-dialog-container">
|
|
||||||
<el-scrollbar height="400px" class="tools-list">
|
|
||||||
<div v-for="(tool, index) in tabStorage.settings.enableTools" :key="index" class="tool-item">
|
|
||||||
<div class="tool-info">
|
|
||||||
<div class="tool-name">{{ tool.name }}</div>
|
|
||||||
<div v-if="tool.description" class="tool-description">{{ tool.description }}</div>
|
|
||||||
</div>
|
|
||||||
<el-switch v-model="tool.enabled"/>
|
|
||||||
</div>
|
|
||||||
</el-scrollbar>
|
|
||||||
|
|
||||||
<el-scrollbar height="400px" class="schema-viewer">
|
|
||||||
<div v-html="activeToolsSchemaHTML"></div>
|
|
||||||
</el-scrollbar>
|
|
||||||
</div>
|
|
||||||
<template #footer>
|
|
||||||
<el-button type="primary" @click="enableAllTools">激活所有工具</el-button>
|
|
||||||
<el-button type="danger" @click="disableAllTools">禁用所有工具</el-button>
|
|
||||||
<el-button type="primary" @click="showToolsDialog = false">{{ t("cancel") }}</el-button>
|
|
||||||
</template>
|
|
||||||
</el-dialog>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup lang="ts">
|
|
||||||
import { ref, computed, defineProps, onMounted, onUnmounted } from 'vue';
|
|
||||||
import { llmManager, llms } from '@/views/setting/llm';
|
|
||||||
import { tabs } from '../panel';
|
|
||||||
import { allTools, ChatSetting, ChatStorage, getToolSchema } from './chat';
|
|
||||||
import { useMessageBridge } from '@/api/message-bridge';
|
|
||||||
import { CasualRestAPI, ToolItem, ToolsListResponse } from '@/hook/type';
|
|
||||||
import { markdownToHtml } from './markdown';
|
|
||||||
import { saveSetting } from '@/hook/setting';
|
|
||||||
|
|
||||||
import { useI18n } from 'vue-i18n';
|
|
||||||
|
|
||||||
const { t } = useI18n();
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
tabId: {
|
|
||||||
type: Number,
|
|
||||||
required: true
|
|
||||||
}
|
|
||||||
});
|
|
||||||
|
|
||||||
const showModelDialog = ref(false);
|
|
||||||
const showTemperatureSlider = ref(false);
|
|
||||||
const showContextLengthDialog = ref(false);
|
|
||||||
const showSystemPromptDialog = ref(false);
|
|
||||||
|
|
||||||
const currentServerName = computed(() => {
|
|
||||||
const currentLlm = llms[llmManager.currentModelIndex];
|
|
||||||
if (currentLlm) {
|
|
||||||
return currentLlm.name;
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
const currentModelName = computed(() => {
|
|
||||||
const currentLlm = llms[llmManager.currentModelIndex];
|
|
||||||
if (currentLlm) {
|
|
||||||
return currentLlm.models[selectedModelIndex.value];
|
|
||||||
}
|
|
||||||
return '';
|
|
||||||
});
|
|
||||||
|
|
||||||
const tab = tabs.content[props.tabId];
|
|
||||||
const tabStorage = tab.storage as ChatStorage & { settings: ChatSetting };
|
|
||||||
|
|
||||||
|
|
||||||
if (!tabStorage.settings) {
|
|
||||||
tabStorage.settings = {
|
|
||||||
modelIndex: llmManager.currentModelIndex,
|
|
||||||
enableTools: [],
|
|
||||||
enableWebSearch: false,
|
|
||||||
temperature: 0.7,
|
|
||||||
contextLength: 10,
|
|
||||||
systemPrompt: ''
|
|
||||||
} as ChatSetting;
|
|
||||||
}
|
|
||||||
|
|
||||||
// 代表当前使用的服务商的当前模型的索引
|
|
||||||
const currentModel = llms[llmManager.currentModelIndex].userModel;
|
|
||||||
const selectedModelIndex = ref(llms[llmManager.currentModelIndex].models.indexOf(currentModel));
|
|
||||||
|
|
||||||
const availableModels = computed(() => {
|
|
||||||
return llms[llmManager.currentModelIndex].models;
|
|
||||||
});
|
|
||||||
|
|
||||||
const hasSystemPrompt = computed(() => {
|
|
||||||
return !!tabStorage.settings.systemPrompt?.trim();
|
|
||||||
});
|
|
||||||
|
|
||||||
|
|
||||||
const showToolsDialog = ref(false);
|
|
||||||
|
|
||||||
const availableToolsNum = computed(() => {
|
|
||||||
return tabStorage.settings.enableTools.filter(tool => tool.enabled).length;
|
|
||||||
});
|
|
||||||
|
|
||||||
// 修改 toggleTools 方法
|
|
||||||
const toggleTools = () => {
|
|
||||||
showToolsDialog.value = true;
|
|
||||||
};
|
|
||||||
|
|
||||||
|
|
||||||
const toggleWebSearch = () => {
|
|
||||||
tabStorage.settings.enableWebSearch = !tabStorage.settings.enableWebSearch;
|
|
||||||
};
|
|
||||||
|
|
||||||
const confirmModelChange = () => {
|
|
||||||
showModelDialog.value = false;
|
|
||||||
};
|
|
||||||
|
|
||||||
const onRadioGroupChange = () => {
|
|
||||||
const currentModel = llms[llmManager.currentModelIndex].models[selectedModelIndex.value];
|
|
||||||
llms[llmManager.currentModelIndex].userModel = currentModel;
|
|
||||||
saveSetting();
|
|
||||||
};
|
|
||||||
|
|
||||||
const bridge = useMessageBridge();
|
|
||||||
let commandCancel: (() => void);
|
|
||||||
|
|
||||||
onMounted(() => {
|
|
||||||
commandCancel = bridge.addCommandListener('tools/list', (data: CasualRestAPI<ToolsListResponse>) => {
|
|
||||||
allTools.value = data.msg.tools || [];
|
|
||||||
tabStorage.settings.enableTools = [];
|
|
||||||
for (const tool of allTools.value) {
|
|
||||||
tabStorage.settings.enableTools.push({
|
|
||||||
name: tool.name,
|
|
||||||
description: tool.description,
|
|
||||||
enabled: true
|
|
||||||
});
|
|
||||||
}
|
|
||||||
}, { once: false });
|
|
||||||
|
|
||||||
bridge.postMessage({
|
|
||||||
command: 'tools/list'
|
|
||||||
});
|
|
||||||
});
|
|
||||||
|
|
||||||
onUnmounted(() => {
|
|
||||||
if (commandCancel) {
|
|
||||||
commandCancel();
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
// 新增计算属性获取激活工具的JSON Schema
|
|
||||||
const activeToolsSchemaHTML = computed(() => {
|
|
||||||
const toolsSchema = getToolSchema(tabStorage.settings.enableTools);
|
|
||||||
const jsonString = JSON.stringify(toolsSchema, null, 2);
|
|
||||||
|
|
||||||
return markdownToHtml(
|
|
||||||
"```json\n" + jsonString + "\n```"
|
|
||||||
);
|
|
||||||
});
|
|
||||||
|
|
||||||
// 新增方法 - 激活所有工具
|
|
||||||
const enableAllTools = () => {
|
|
||||||
tabStorage.settings.enableTools.forEach(tool => {
|
|
||||||
tool.enabled = true;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
|
|
||||||
// 新增方法 - 禁用所有工具
|
|
||||||
const disableAllTools = () => {
|
|
||||||
tabStorage.settings.enableTools.forEach(tool => {
|
|
||||||
tool.enabled = false;
|
|
||||||
});
|
|
||||||
};
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style>
|
|
||||||
.chat-settings {
|
|
||||||
display: flex;
|
|
||||||
gap: 2px;
|
|
||||||
padding: 8px 0;
|
|
||||||
background-color: var(--sidebar);
|
|
||||||
width: fit-content;
|
|
||||||
border-radius: 99%;
|
|
||||||
bottom: 0px;
|
|
||||||
z-index: 10;
|
|
||||||
position: absolute;
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-button {
|
|
||||||
padding: 5px 8px;
|
|
||||||
margin-right: 3px;
|
|
||||||
border-radius: .5em;
|
|
||||||
font-size: 12px;
|
|
||||||
position: relative;
|
|
||||||
user-select: none;
|
|
||||||
-webkit-user-drag: none;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
cursor: pointer;
|
|
||||||
transition: var(--animation-3s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-button.active {
|
|
||||||
background-color: var(--el-color-primary);
|
|
||||||
color: var(--el-text-color-primary);
|
|
||||||
transition: var(--animation-3s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-button.active:hover {
|
|
||||||
background-color: var(--el-color-primary);
|
|
||||||
transition: var(--animation-3s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.setting-button:hover {
|
|
||||||
background-color: var(--background);
|
|
||||||
transition: var(--animation-3s);
|
|
||||||
}
|
|
||||||
|
|
||||||
.value-badge {
|
|
||||||
font-size: 10px;
|
|
||||||
padding: 1px 4px;
|
|
||||||
border-radius: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-container {
|
|
||||||
padding: 0 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-temperature {
|
|
||||||
font-size: 18px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.icon-length {
|
|
||||||
font-size: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.slider-tips {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
margin-top: 10px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 新增工具相关样式 */
|
|
||||||
.tools-container {
|
|
||||||
padding: 10px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-item {
|
|
||||||
display: flex;
|
|
||||||
justify-content: space-between;
|
|
||||||
align-items: center;
|
|
||||||
padding: 12px 0;
|
|
||||||
border-bottom: 1px solid var(--el-border-color-light);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-info {
|
|
||||||
flex: 1;
|
|
||||||
margin-right: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-name {
|
|
||||||
font-weight: 500;
|
|
||||||
margin-bottom: 4px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tool-description {
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--el-text-color-secondary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tools-dialog-container .el-switch__core {
|
|
||||||
border: 1px solid var(--main-color) !important;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
.tools-dialog-container .el-switch .el-switch__action {
|
|
||||||
background-color: var(--main-color);
|
|
||||||
}
|
|
||||||
|
|
||||||
.tools-dialog-container .el-switch.is-checked .el-switch__action {
|
|
||||||
background-color: var(--sidebar);
|
|
||||||
}
|
|
||||||
|
|
||||||
/* 新增工具对话框样式 */
|
|
||||||
.tools-dialog-container {
|
|
||||||
display: flex;
|
|
||||||
gap: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.tools-list {
|
|
||||||
flex: 1;
|
|
||||||
border-right: 1px solid var(--el-border-color);
|
|
||||||
padding-right: 16px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.schema-viewer {
|
|
||||||
flex: 1;
|
|
||||||
}
|
|
||||||
|
|
||||||
.schema-viewer pre {
|
|
||||||
margin: 0;
|
|
||||||
border-radius: 4px;
|
|
||||||
white-space: pre-wrap;
|
|
||||||
word-wrap: break-word;
|
|
||||||
background-color: var(--el-bg-color-overlay);
|
|
||||||
}
|
|
||||||
|
|
||||||
.schema-viewer .openmcp-code-block {
|
|
||||||
border: none;
|
|
||||||
}
|
|
||||||
|
|
||||||
.schema-viewer code {
|
|
||||||
font-family: var(--code-font-family);
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--el-text-color-primary);
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-outer {
|
|
||||||
position: relative;
|
|
||||||
}
|
|
||||||
|
|
||||||
.badge-inner {
|
|
||||||
position: absolute;
|
|
||||||
color: var(--foreground);
|
|
||||||
background-color: var(--main-color);
|
|
||||||
border-radius: 50%;
|
|
||||||
padding: 2px 6px;
|
|
||||||
font-size: 10px;
|
|
||||||
top: -16px;
|
|
||||||
right: -18px;
|
|
||||||
box-shadow: 0 0 6px rgba(0, 0, 0, 0.2);
|
|
||||||
}
|
|
||||||
</style>
|
|
@ -75,7 +75,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
const targetResource = resourcesManager.templates.find(template => template.name === tabStorage.currentResourceName);
|
const targetResource = resourcesManager.templates.find(template => template.name === tabStorage.currentResourceName);
|
||||||
if (targetResource === undefined) {
|
if (targetResource === undefined) {
|
||||||
tabStorage.currentResourceName = resourcesManager.templates[0].name;
|
tabStorage.currentResourceName = resourcesManager.templates[0]?.name;
|
||||||
tabStorage.lastResourceReadResponse = undefined;
|
tabStorage.lastResourceReadResponse = undefined;
|
||||||
}
|
}
|
||||||
}, { once: false });
|
}, { once: false });
|
||||||
|
@ -40,7 +40,7 @@ import { defineComponent, defineProps, computed, ref } from 'vue';
|
|||||||
import { useI18n } from 'vue-i18n';
|
import { useI18n } from 'vue-i18n';
|
||||||
import { tabs } from '../panel';
|
import { tabs } from '../panel';
|
||||||
import { ToolStorage } from './tools';
|
import { ToolStorage } from './tools';
|
||||||
import { markdownToHtml } from '../chat/markdown';
|
import { markdownToHtml } from '../chat/markdown/markdown';
|
||||||
|
|
||||||
defineComponent({ name: 'tool-logger' });
|
defineComponent({ name: 'tool-logger' });
|
||||||
const { t } = useI18n();
|
const { t } = useI18n();
|
||||||
|
@ -23,25 +23,6 @@ export function getCurrentTime() {
|
|||||||
return timeStr;
|
return timeStr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
export function getBase64StringByFilename(filename: string) {
|
|
||||||
const bridge = useMessageBridge();
|
|
||||||
|
|
||||||
return new Promise<string>(resolve => {
|
|
||||||
bridge.addCommandListener('ocr/get-ocr-image', data => {
|
|
||||||
const { code, msg = {} } = data;
|
|
||||||
resolve(msg.base64String);
|
|
||||||
}, { once: true});
|
|
||||||
|
|
||||||
bridge.postMessage({
|
|
||||||
command: 'ocr/get-ocr-image',
|
|
||||||
data: {
|
|
||||||
filename
|
|
||||||
}
|
|
||||||
});
|
|
||||||
});
|
|
||||||
}
|
|
||||||
|
|
||||||
const blobUrlCache = new Map<string, string>();
|
const blobUrlCache = new Map<string, string>();
|
||||||
|
|
||||||
export async function getBlobUrlByFilename(filename: string) {
|
export async function getBlobUrlByFilename(filename: string) {
|
||||||
@ -50,7 +31,13 @@ export async function getBlobUrlByFilename(filename: string) {
|
|||||||
return blobUrlCache.get(filename);
|
return blobUrlCache.get(filename);
|
||||||
}
|
}
|
||||||
|
|
||||||
const base64String = await getBase64StringByFilename(filename);
|
const bridge = useMessageBridge();
|
||||||
|
const res = await bridge.commandRequest('ocr/get-ocr-image', { filename });
|
||||||
|
if (res?.code !== 200) {
|
||||||
|
return '';
|
||||||
|
}
|
||||||
|
|
||||||
|
const base64String = res?.msg?.base64String;
|
||||||
|
|
||||||
if (!base64String) {
|
if (!base64String) {
|
||||||
return '';
|
return '';
|
||||||
|
@ -4,7 +4,7 @@ import router from "./router";
|
|||||||
import i18n from './i18n';
|
import i18n from './i18n';
|
||||||
import ElementPlus from 'element-plus';
|
import ElementPlus from 'element-plus';
|
||||||
import 'element-plus/dist/index.css';
|
import 'element-plus/dist/index.css';
|
||||||
import "@/components/main-panel/chat/prism/prism.css";
|
import "@/components/main-panel/chat/markdown/prism/prism.css";
|
||||||
import 'katex/dist/katex.min.css';
|
import 'katex/dist/katex.min.css';
|
||||||
|
|
||||||
createApp(App)
|
createApp(App)
|
||||||
|
@ -72,6 +72,7 @@ export class ClientController {
|
|||||||
|
|
||||||
@Controller('resources/templates/list')
|
@Controller('resources/templates/list')
|
||||||
async listResourceTemplates(client: RequestClientType, data: any, webview: PostMessageble) {
|
async listResourceTemplates(client: RequestClientType, data: any, webview: PostMessageble) {
|
||||||
|
|
||||||
if (!client) {
|
if (!client) {
|
||||||
return {
|
return {
|
||||||
code: 501,
|
code: 501,
|
||||||
|
Loading…
x
Reference in New Issue
Block a user