完成大模型的输入传输

This commit is contained in:
锦恢 2025-04-07 01:48:54 +08:00
parent 3ac2af597c
commit 7330889290
8 changed files with 81 additions and 69 deletions

View File

@ -1,11 +1,11 @@
{ {
"name": "app", "name": "renderer",
"version": "0.1.0", "version": "0.1.0",
"lockfileVersion": 3, "lockfileVersion": 3,
"requires": true, "requires": true,
"packages": { "packages": {
"": { "": {
"name": "app", "name": "renderer",
"version": "0.1.0", "version": "0.1.0",
"dependencies": { "dependencies": {
"core-js": "^3.8.3", "core-js": "^3.8.3",

View File

@ -39,7 +39,7 @@ onMounted(() => {
// debug // debug
if (acquireVsCodeApi === undefined) { if (acquireVsCodeApi === undefined) {
connectionArgs.commandString = 'uv run mcp run ../servers/main.py'; connectionArgs.commandString = 'mcp run ../servers/main.py';
connectionMethods.current = 'STDIO'; connectionMethods.current = 'STDIO';
bridge.addCommandListener('connect', data => { bridge.addCommandListener('connect', data => {

View File

@ -28,13 +28,13 @@
<el-footer class="chat-footer" ref="footerRef"> <el-footer class="chat-footer" ref="footerRef">
<div class="input-area"> <div class="input-area">
<Setting :tabId="tabId" />
<div class="input-wrapper"> <div class="input-wrapper">
<Setting :tabId="tabId" />
<el-input v-model="userInput" type="textarea" :rows="inputHeightLines" :maxlength="2000" <el-input v-model="userInput" type="textarea" :rows="inputHeightLines" :maxlength="2000"
placeholder="输入消息..." :disabled="isLoading" @keydown.enter="handleKeydown" resize="none" placeholder="输入消息..." @keydown.enter="handleKeydown" resize="none"
class="chat-input" /> class="chat-input" />
<el-button type="primary" :loading="isLoading" @click="handleSend" class="send-button" <el-button type="primary" :loading="isLoading" @click="handleSend" class="send-button"
:disabled="!userInput.trim()"> :disabled="!userInput.trim()">
<span class="iconfont icon-send"></span> <span class="iconfont icon-send"></span>
@ -48,14 +48,14 @@
<script setup lang="ts"> <script setup lang="ts">
import { ref, onMounted, defineComponent, defineProps, onUnmounted, computed } from 'vue'; import { ref, onMounted, defineComponent, defineProps, onUnmounted, computed } from 'vue';
import { useI18n } from 'vue-i18n'; import { useI18n } from 'vue-i18n';
import { User, Comment } from '@element-plus/icons-vue'; import { Comment } from '@element-plus/icons-vue';
import { useMessageBridge } from "@/api/message-bridge"; import { useMessageBridge } from "@/api/message-bridge";
import { ElMessage } from 'element-plus'; import { ElMessage } from 'element-plus';
import { llmManager, llms } from '@/views/setting/llm';
import { tabs } from '../panel'; import { tabs } from '../panel';
import { ChatMessage, ChatStorage } from './chat'; import { ChatMessage, ChatStorage } from './chat';
import Setting from './setting.vue'; import Setting from './setting.vue';
import { llmManager, llms } from '@/views/setting/llm';
defineComponent({ name: 'chat' }); defineComponent({ name: 'chat' });
@ -71,55 +71,19 @@ const props = defineProps({
const tab = tabs.content[props.tabId]; const tab = tabs.content[props.tabId];
const tabStorage = tab.storage as ChatStorage; const tabStorage = tab.storage as ChatStorage;
const bridge = useMessageBridge(); const bridge = useMessageBridge();
const userInput = ref(''); const userInput = ref('');
const inputHeightLines = computed(() => { const inputHeightLines = computed(() => {
const currentLines = userInput.value.split('\n').length; const currentLines = userInput.value.split('\n').length;
return Math.min(12, Math.max(5, currentLines)); return Math.min(12, Math.max(5, currentLines));
}); });
const messages = ref<ChatMessage[]>([
{ // messages
role: 'assistant', if (!tabStorage.messages) {
content: '你好我是AI助手有什么可以帮您的吗' tabStorage.messages = [] as ChatMessage[];
}, }
{ const messages = tabStorage.messages;
role: 'user',
content: '你好,能帮我写一封求职信吗?'
},
{
role: 'assistant',
content: '当然可以。请问您应聘的是什么职位?需要包含哪些特别的信息吗?'
},
{
role: 'user',
content: '我想应聘前端开发工程师有3年Vue和React经验'
},
{
role: 'assistant',
content: '好的,我已根据您的要求写了一封求职信模板:\n\n尊敬的招聘经理\n\n您好我在贵公司官网上看到前端开发工程师的招聘信息...'
},
{
role: 'user',
content: '谢谢!能再帮我优化一下简历中的项目描述部分吗?'
},
{
role: 'assistant',
content: '当然可以。建议采用STAR法则(Situation-Task-Action-Result)来描述项目经验,这样更能突出您的贡献和价值。'
},
{
role: 'user',
content: '什么是STAR法则能举个例子吗'
},
{
role: 'assistant',
content: 'STAR法则是一种结构化表达方法\n\n情境(Situation):项目背景\n任务(Task):你的职责\n行动(Action):采取的措施\n结果(Result):取得的成果\n\n例如开发了基于Vue3的管理系统优化了页面加载速度30%...'
},
{
role: 'user',
content: '明白了,这样写确实更专业!'
}
]);
const isLoading = ref(false); const isLoading = ref(false);
const streamingContent = ref(''); const streamingContent = ref('');
const chatContainerRef = ref<HTMLElement>(); const chatContainerRef = ref<HTMLElement>();
@ -146,16 +110,34 @@ const handleSend = () => {
if (!userInput.value.trim() || isLoading.value) return; if (!userInput.value.trim() || isLoading.value) return;
const userMessage = userInput.value.trim(); const userMessage = userInput.value.trim();
messages.value.push({ role: 'user', content: userMessage }); messages.push({ role: 'user', content: userMessage });
// baseURL, apiKey, model, messages, temperature
const baseURL = llms[llmManager.currentModelIndex].baseUrl;
const apiKey = llms[llmManager.currentModelIndex].userToken;
const model = llms[llmManager.currentModelIndex].userModel;
const temperature = tabStorage.settings.temperature;
const userMessages = [];
if (tabStorage.settings.systemPrompt) {
userMessages.push({
role: 'system',
content: tabStorage.settings.systemPrompt
});
}
userMessages.concat(messages);
userMessages.push({
role: 'assistant',
content: streamingContent.value
});
const chatData = { const chatData = {
messages: [ baseURL,
...messages.value.filter(msg => msg.role === 'user').map(msg => ({ apiKey,
role: msg.role, model,
content: msg.content temperature,
})), messages: userMessages,
{ role: 'assistant', content: streamingContent.value }
]
}; };
isLoading.value = true; isLoading.value = true;
@ -171,7 +153,7 @@ const handleSend = () => {
const handleError = (errorMsg: string) => { const handleError = (errorMsg: string) => {
ElMessage.error(errorMsg); ElMessage.error(errorMsg);
messages.value.push({ messages.push({
role: 'assistant', role: 'assistant',
content: `错误: ${errorMsg}` content: `错误: ${errorMsg}`
}); });
@ -200,7 +182,7 @@ onMounted(() => {
return; return;
} }
if (streamingContent.value) { if (streamingContent.value) {
messages.value.push({ messages.push({
role: 'assistant', role: 'assistant',
content: streamingContent.value content: streamingContent.value
}); });
@ -323,6 +305,12 @@ onUnmounted(() => {
border-radius: .5em; border-radius: .5em;
} }
:deep(.chat-settings) {
position: absolute;
left: 0;
bottom: 8px;
z-index: 1;
}
.typing-cursor { .typing-cursor {
animation: blink 1s infinite; animation: blink 1s infinite;
} }

View File

@ -46,7 +46,9 @@
<!-- 模型选择对话框 --> <!-- 模型选择对话框 -->
<el-dialog v-model="showModelDialog" title="选择模型" width="400px"> <el-dialog v-model="showModelDialog" title="选择模型" width="400px">
<el-radio-group v-model="selectedModelIndex"> <el-radio-group v-model="selectedModelIndex"
@change="onRadioGroupChange"
>
<el-radio v-for="(model, index) in availableModels" :key="index" :label="index"> <el-radio v-for="(model, index) in availableModels" :key="index" :label="index">
{{ model }} {{ model }}
</el-radio> </el-radio>
@ -157,6 +159,11 @@ const confirmModelChange = () => {
llmManager.currentModelIndex = selectedModelIndex.value; llmManager.currentModelIndex = selectedModelIndex.value;
showModelDialog.value = false; showModelDialog.value = false;
}; };
const onRadioGroupChange = () => {
const currentModel = llms[llmManager.currentModelIndex].models[selectedModelIndex.value];
llms[llmManager.currentModelIndex].userModel = currentModel;
};
</script> </script>
<style scoped> <style scoped>
@ -164,6 +171,12 @@ const confirmModelChange = () => {
display: flex; display: flex;
gap: 2px; gap: 2px;
padding: 8px 0; padding: 8px 0;
background-color: var(--sidebar);
width: fit-content;
border-radius: 99%;
bottom: 0px;
z-index: 10;
position: absolute;
} }
.setting-button { .setting-button {

View File

@ -45,7 +45,6 @@ let tabCounter = 1;
watch( watch(
() => tabs, () => tabs,
(newValue, oldValue) => { (newValue, oldValue) => {
console.log('state change');
safeSavePanels(); safeSavePanels();
}, },
{ deep: true } { deep: true }

View File

@ -58,7 +58,7 @@ export function safeSavePanels() {
clearTimeout(debounceHandler); clearTimeout(debounceHandler);
debounceHandler = setTimeout(() => { debounceHandler = setTimeout(() => {
savePanels(); savePanels();
}, 200); }, 1000);
} }
export function savePanels(saveHandler?: () => void) { export function savePanels(saveHandler?: () => void) {

View File

@ -12,7 +12,7 @@ export async function chatCompletionHandler(client: MCPClient | undefined, data:
} }
const { baseURL, apiKey, model, messages } = data; const { baseURL, apiKey, model, messages, temperature } = data;
try { try {
const client = new OpenAI({ const client = new OpenAI({
@ -23,6 +23,8 @@ export async function chatCompletionHandler(client: MCPClient | undefined, data:
const stream = await client.chat.completions.create({ const stream = await client.chat.completions.create({
model, model,
messages, messages,
temperature,
web_search_options: {},
stream: true stream: true
}); });

View File

@ -12,9 +12,19 @@
"enableTools": true, "enableTools": true,
"enableWebSearch": false, "enableWebSearch": false,
"temperature": 0.7, "temperature": 0.7,
"contextLength": 41, "contextLength": 10,
"systemPrompt": "ad" "systemPrompt": "你的名字是 openmcp"
} },
"messages": [
{
"role": "user",
"content": "请问你的名字是什么"
},
{
"role": "assistant",
"content": "我的名字是 **OpenMCP**Open Multi-modal Cognitive Platform。 \n\n我是由 **深度求索DeepSeek** 开发的一款 **多模态认知大模型**,能够处理文本、图像、文档等多种信息,并具备强大的理解和推理能力。 \n\n你可以叫我 **OpenMCP**,或者简称 **MCP**!😊 有什么我可以帮你的吗?"
}
]
} }
}, },
{ {