chat接口使用sse

This commit is contained in:
lijun 2025-09-23 20:35:33 +08:00
parent 3c0721de45
commit 66c345e788
6 changed files with 1586 additions and 269 deletions

6
package-lock.json generated
View File

@ -15,6 +15,7 @@
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.38.1",
"@element-plus/icons-vue": "*",
"@microsoft/fetch-event-source": "^2.0.1",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.3",
@ -802,6 +803,11 @@
"langium": "3.3.1"
}
},
"node_modules/@microsoft/fetch-event-source": {
"version": "2.0.1",
"resolved": "https://registry.npmjs.org/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz",
"integrity": "sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA=="
},
"node_modules/@nodelib/fs.scandir": {
"version": "2.1.5",
"resolved": "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz",

View File

@ -15,6 +15,7 @@
"@codemirror/theme-one-dark": "^6.1.3",
"@codemirror/view": "^6.38.1",
"@element-plus/icons-vue": "*",
"@microsoft/fetch-event-source": "^2.0.1",
"@wangeditor/editor": "^5.1.23",
"@wangeditor/editor-for-vue": "^5.1.12",
"axios": "^1.6.3",

View File

@ -53,6 +53,7 @@ request.interceptors.response.use(
requests.forEach(cb => cb(token));
requests = [];
isRefreshing = false;
localStorage.setItem("refresh_time", Date.now().toString());
// 重试当前请求
const config = result.config;

View File

@ -21,11 +21,7 @@
</template>
<el-scrollbar style="height: 400px; scrollbar-width: none">
<ul>
<li
v-for="(session, index) in historySessions"
:key="index"
@click="loadSession(session.ID)"
>
<li v-for="(session, index) in historySessions" :key="index" @click="loadSession(session.ID)">
<el-tooltip :content="session.Name" placement="top">
<span>{{ getShortenedName(session.Name) }}</span>
</el-tooltip>
@ -48,31 +44,18 @@
<!-- 原有的聊天区域 -->
<div class="chat-container">
<!-- 消息列表 -->
<el-card
class="chat-messages"
shadow="never"
ref="messagesContainer"
v-if="messages.length > 0"
>
<div
v-for="(message, index) in messages"
:key="index"
:class="['message', message.role]"
>
<el-card class="chat-messages" shadow="never" ref="messagesContainer" v-if="messages.length > 0">
<div v-for="(message, index) in messages" :key="index" :class="['message', message.role]">
<div class="message-avatar">
<span v-if="message.role === 'assistant'">💬</span>
<span v-else>🧑🎓</span>
</div>
<div class="message-content">
<div v-html="renderMarkdown(message,index)"></div>
<div v-html="renderMarkdown(message, index)"></div>
<!-- 添加复制 -->
<div>
<el-tooltip content="复制" placement="top">
<el-button
type="text"
:icon="DocumentCopy"
@click="copyMessage(message.content)"
></el-button>
<el-button type="text" :icon="DocumentCopy" @click="copyMessage(message.content)"></el-button>
</el-tooltip>
<!-- <el-button
type="text"
@ -80,60 +63,43 @@
@click="MessageTextToDoc(message.content)"
></el-button> -->
<el-tooltip content="预览、文本创建文件" placement="top">
<el-button
type="text"
:icon="Document"
@click="MessageTextToDoc(message.content)"
>
<el-button type="text" :icon="Document" @click="MessageTextToDoc(message.content)">
</el-button>
</el-tooltip>
</div>
</div>
</div>
<div v-if="loading" class="loading-indicator">Loading...,已回答字符:{{currentAIMessage.length}}</div>
<div v-if="loading" class="loading-indicator">Loading...,已回答字符:{{ currentAIMessage.length }}</div>
</el-card>
<!-- 输入区域 -->
<el-card class="chat-input" shadow="never">
<el-row :gutter="10">
<el-col :span="20">
<el-input
v-model="inputMessage"
type="textarea"
style="border: 0"
:rows="5"
placeholder="输入消息..."
@keyup.enter="sendMessage"
/>
<el-input v-model="inputMessage" type="textarea" style="border: 0" :rows="5" placeholder="输入消息..."
@keyup.enter="sendMessage" />
<!-- <el-text
v-model="inputMessage"
aria-placeholder="输入信息...."
></el-text> -->
</el-col>
<el-col :span="4" style="text-align: center">
<el-button
@click="sendMessage"
type="success"
:icon="Check"
round
:disabled="loading"
>发送</el-button
>
<el-button @click="sendMessage" type="success" :icon="Check" round :disabled="loading">发送</el-button>
<el-dropdown trigger="click" class="model-dropdown">
<span class="el-dropdown-link">
<span>模型参数</span>
<el-icon><ArrowDown /></el-icon>
<el-icon>
<ArrowDown />
</el-icon>
</span>
<template #dropdown>
<div class="dropdown-content">
<div class="model-params">
<h4>模型参数
<el-tooltip
effect="dark"
placement="right"
content="建议仅调整 temperature 或 top_p 其中之一,不建议两者都修改"
>
<el-icon class="tip-icon"><QuestionFilled /></el-icon>
<el-tooltip effect="dark" placement="right" content="建议仅调整 temperature 或 top_p 其中之一,不建议两者都修改">
<el-icon class="tip-icon">
<QuestionFilled />
</el-icon>
</el-tooltip>
</h4>
@ -141,21 +107,13 @@
<div class="param-item">
<div class="param-label">
<span>温度 (Temperature)</span>
<el-tooltip
effect="dark"
placement="right"
content="采样温度控制生成随机性0: 保守2: 随机)"
>
<el-icon class="tip-icon"><QuestionFilled /></el-icon>
<el-tooltip effect="dark" placement="right" content="采样温度控制生成随机性0: 保守2: 随机)">
<el-icon class="tip-icon">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
<el-slider
v-model="temperature"
:min="0"
:max="2"
:step="0.1"
:show-tooltip="false"
/>
<el-slider v-model="temperature" :min="0" :max="2" :step="0.1" :show-tooltip="false" />
<div class="param-value">{{ temperature }}</div>
</div>
@ -163,21 +121,13 @@
<div class="param-item">
<div class="param-label">
<span>Top P</span>
<el-tooltip
effect="dark"
placement="right"
content="限制候选词范围0: 严格1: 宽松)"
>
<el-icon class="tip-icon"><QuestionFilled /></el-icon>
<el-tooltip effect="dark" placement="right" content="限制候选词范围0: 严格1: 宽松)">
<el-icon class="tip-icon">
<QuestionFilled />
</el-icon>
</el-tooltip>
</div>
<el-slider
v-model="topP"
:min="0"
:max="1"
:step="0.1"
:show-tooltip="false"
/>
<el-slider v-model="topP" :min="0" :max="1" :step="0.1" :show-tooltip="false" />
<div class="param-value">{{ topP }}</div>
</div>
</div>
@ -191,19 +141,15 @@
</el-col>
<el-col :span="3" style="text-align: center">
<el-select v-model="selectModel" placeholder="选择模型" style="width: 320px;">
<el-option
v-for="item in ModelList"
:key="item.ID"
:label="item.Type + ':' + item.Description"
:value="item.ID"
></el-option>
<el-option v-for="item in ModelList" :key="item.ID" :label="item.Type + ':' + item.Description"
:value="item.ID"></el-option>
</el-select>
</el-col>
<el-col :span="10" style="text-align: center">
<el-tooltip content="文件选择" placement="top">
<el-button @click="handleSelectFileVisible"
><el-icon><Files /></el-icon
></el-button>
<el-button @click="handleSelectFileVisible"><el-icon>
<Files />
</el-icon></el-button>
</el-tooltip>
</el-col>
<!-- <el-col :span="1" style="text-align: center">
@ -214,13 +160,8 @@
</el-col> -->
<!-- 已选文件一行显示 -->
<el-col :span="12" style="text-align: center">
<el-tag
v-for="(file, index) in selectedFiles"
:key="index"
closable
@close="removeFile(index)"
>{{ file.UserFileName }}</el-tag
>
<el-tag v-for="(file, index) in selectedFiles" :key="index" closable @close="removeFile(index)">{{
file.UserFileName }}</el-tag>
</el-col>
</el-row>
</el-card>
@ -228,35 +169,17 @@
<div>
<!-- 文件对话框 -->
<el-dialog
v-model="selectFileVisible"
title="从上传文件中选择"
width="50%"
>
<el-input
placeholder="搜索文件"
v-model="searchFileQuery"
prefix-icon="el-icon-search"
/>
<el-dialog v-model="selectFileVisible" title="从上传文件中选择" width="50%">
<el-input placeholder="搜索文件" v-model="searchFileQuery" prefix-icon="el-icon-search" />
<el-button @click="uploadMessageFile">上传文件</el-button>
<!-- 文件列表 -->
<div class="file-list">
<el-checkbox-group v-model="selectedFiles">
<el-checkbox
v-for="(item, index) in filteredFiles"
:key="index"
:label="item"
>
<el-checkbox v-for="(item, index) in filteredFiles" :key="index" :label="item">
<span class="file-icon">
<!-- 根据文件类型展示不同图标 -->
<i
v-if="item.UploadType === 'image'"
class="el-icon-picture"
></i>
<i
v-else-if="item.UploadType === 'file'"
class="el-icon-document"
></i>
<i v-if="item.UploadType === 'image'" class="el-icon-picture"></i>
<i v-else-if="item.UploadType === 'file'" class="el-icon-document"></i>
<!-- 可继续补充其他文件类型图标 -->
</span>
{{ item.UserFileName }}
@ -266,34 +189,21 @@
</div>
<!-- 底部状态栏和按钮 -->
<div class="footer-bar">
<span class="selected-count"
>已选 {{ selectedFiles.length }} 个文件</span
>
<span class="selected-count">已选 {{ selectedFiles.length }} 个文件</span>
<el-button @click="selectFileVisible = false">取消</el-button>
<el-button type="primary" @click="handleSelectFileConfirm"
>确认添加({{ selectedFiles.length }})</el-button
>
<el-button type="primary" @click="handleSelectFileConfirm">确认添加({{ selectedFiles.length }})</el-button>
</div>
</el-dialog>
</div>
<!-- 上传文件对话框 -->
<div>
<el-dialog
title="上传文件"
v-model="uploadFileVisible"
width="50%"
:before-close="handleUploadFileClose"
>
<el-dialog title="上传文件" v-model="uploadFileVisible" width="50%" :before-close="handleUploadFileClose">
<UploadFile></UploadFile>
</el-dialog>
</div>
<div>
<!-- 文本创建文件对话 -->
<el-dialog
title="文本创建文件"
v-model="textToDocFileVisible"
width="70%"
heigth="80vh"
<el-dialog title="文本创建文件" v-model="textToDocFileVisible" width="70%" heigth="80vh"
:before-close="handleMessageTextToDOCClose">
<!-- <textarea v-model="textToDocFileContent"></textarea> -->
<div ref="vditorRef"></div>
@ -318,19 +228,19 @@
</template>
<script setup lang="ts">
import { ref, onMounted, onUnmounted, reactive, nextTick,watch } from "vue";
import { ref, onMounted, onUnmounted, reactive, nextTick, watch } from "vue";
import { ElCard, ElInput, ElButton, ElDialog } from "element-plus";
import { WSMessage, GenMessage } from "@/types/im";
import { GetMessageService } from "@/api/im";
import { FindUserFileService } from "@/api/file";
import { Model } from "@/types/model";
import {UserUISettings} from '@/types/user';
import { UserUISettings } from '@/types/user';
import { File, fileUrl } from "@/types/file";
import { Session } from "@/types/session";
import { FindSessionService } from "@/api/session";
import { SetMessageTextToDocService } from "@/api/tool";
import { ElMessage } from "element-plus";
import { Check, DocumentCopy,Document, PriceTag } from "@element-plus/icons-vue";
import { Check, DocumentCopy, Document, PriceTag } from "@element-plus/icons-vue";
import MarkdownIt from "markdown-it";
import hljs from "highlight.js";
import UploadFile from "@/components/upload-file.vue";
@ -339,6 +249,7 @@ import { FindModelListByFunctionName } from "@/api/function";
import markdownItHighlightjs from "markdown-it-highlightjs";
import markdownItKatex from "markdown-it-katex";
import mermaidPlugin from "@agoose77/markdown-it-mermaid";
import { fetchEventSource } from '@microsoft/fetch-event-source';
import "katex/dist/katex.min.css";
import Vditor from 'vditor';
import 'vditor/dist/index.css';
@ -350,8 +261,8 @@ interface Message {
finished?: boolean;
}
interface SendImageMessage {
img_name : string;
img_url : string;
img_name: string;
img_url: string;
}
interface ImageMessage {
image_content: SendImageMessage[];
@ -417,25 +328,25 @@ const userUIconfigInfo = ref<UserUISettings>({} as UserUISettings); // 用户UI
//
watch(
[selectModel, temperature, topP,sessionID],
[selectModel, temperature, topP, sessionID],
() => { //
updateUserUIconfigInfo();
}
)
const historyMsgHtml= ref([]); // HTML
const historyMsgHtml = ref([]); // HTML
const wssUrl =
"wss://pm.ljsea.top/im/ai_chat_ws?" +
"token=" +
localStorage.getItem("token");
const renderMarkdown = (message: Message, index:number) => {
if(message.finished == false){
const renderMarkdown = (message: Message, index: number) => {
if (message.finished == false) {
//console.log("not finished");
return message.content;
}
if(historyMsgHtml.value[index]){
if (historyMsgHtml.value[index]) {
//console.log("historyMsgHtml:", historyMsgHtml.value[index]);
//console.log("historyMsgHtml:", index);
//
@ -553,7 +464,7 @@ onMounted(() => {
// //
// md.use(markdownItMermaid);
// }
IMWSConnect();
//IMWSConnect();
@ -603,12 +514,43 @@ const doReceiveMessage = (event) => {
});
}
const doReceiveMessageSSE = (event) => {
//
let msg: WSMessage = JSON.parse(event.data.replace("data: ", ""));
const existingMessage = messages.find(
(msg) => msg.role === "assistant" && !msg.finished
);
if (existingMessage) {
//
existingMessage.content += msg.msg.msg.response;
} else {
//
messages.push({
role: "assistant",
content: msg.msg.msg.response,
finished: false,
});
}
sessionID.value = msg.session_id;
currentAIMessage.value += msg.msg.msg.response;
if (msg.msg.msg.done) {
const assistantMessage = messages[messages.length - 1];
assistantMessage.finished = true;
loading.value = false;
currentAIMessage.value = "";
doButtonD();
}
nextTick(() => {
scrollToBottom(); //
});
}
const updateUserUIconfigInfo = () => {
//
let req:UserUISettings = JSON.parse(localStorage.getItem("userUIconfigInfo") || "{}");
if(req.user_id == 0){
let req: UserUISettings = JSON.parse(localStorage.getItem("userUIconfigInfo") || "{}");
if (req.user_id == 0) {
req.user_id = parseInt(localStorage.getItem("user_id") || "0");
}else{
} else {
req.gen_ai_function.model_id = selectModel.value;
req.gen_ai_function.temperature = temperature.value;
req.gen_ai_function.top_p = topP.value;
@ -623,23 +565,28 @@ const updateUserUIconfigInfo = () => {
});
}
const sendMessage = async () => {
//ws
if (socket.value == null) {
await IMWSConnect();
// if (socket.value == null) {
// await IMWSConnect();
}
if(loading.value === true){
// }
if (loading.value === true) {
ElMessage.warning("正在等待AI回复请稍后再试");
return;
}
await nextTick();
sendMessageWithFile();
sendMessageWithFileUseSSE();
//sendMessageWithFile();
return;
};
const sendMessageWithFile =async () => {
const sendMessageWithFileUseSSE = async () => {
checkTokenIsValidAndRefresh();
let url = "https://pm.ljsea.top/im/chat_completion";
let headers = { "token": localStorage.getItem("token") };
if (inputMessage.value.trim() === "") {
ElMessage.warning("消息不能为空");
return;
@ -657,13 +604,13 @@ const sendMessageWithFile =async () => {
//
console.log("选中的文件:", selectedFiles.value);
let file_contents = []
let file_type= ""
let file_type = ""
for (let i = 0; i < selectedFiles.value.length; i++) {
let file: File = selectedFiles.value[i];
//jpgpnggifbmp
if (file.UserFileName.endsWith(".jpg") || file.UserFileName.endsWith(".png") || file.UserFileName.endsWith(".gif") || file.UserFileName.endsWith(".bmp")) {
file_type = "image_file"
}else{
} else {
file_type = "text_file"
}
let file_msg: FileMessage = {
@ -672,7 +619,102 @@ const sendMessageWithFile =async () => {
};
file_contents.push(file_msg);
}
let msg : GenerationMessage = {
let msg: GenerationMessage = {
text: inputMessage.value,
file_content: file_contents,
};
let msg_str = JSON.stringify(msg);
end_msg["msg"] = msg_str;
end_msg["is_file"] = true;
}
fetchEventSource(url, {
method: 'POST',
headers: {
'Content-Type': 'application/json', //
...headers, // Authorization
},
body: JSON.stringify(end_msg), //
openWhenHidden: true, //
onopen: () => {
console.log('SSE connection established');
},
onmessage: (event) => {
try {
doReceiveMessageSSE(event);
} catch (e) {
console.error('Failed to parse SSE data:', e);
}
},
onerror: (error) => {
ElMessage.error("发送失败!请刷新页面!");
},
onclose: () => {
console.log('SSE connection closed');
},
});
let pMsgContent = "";
if (end_msg["is_file"]) {
let file_msg: GenerationMessage = JSON.parse(end_msg["msg"]);
//md
let file_content = file_msg.file_content
for (let i = 0; i < file_content.length; i++) {
if (file_content[i].file_type == "image_file") {
pMsgContent += `![${file_content[i].file_content.UserFileName}](${fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
} else {
pMsgContent += `文件:[${file_content[i].file_content.UserFileName}](${fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
}
}
pMsgContent = pMsgContent + "输入:" + file_msg.text;
} else {
pMsgContent = end_msg.msg;
}
messages.push({ role: "user", content: pMsgContent, finished: true });
inputMessage.value = "";
nextTick(() => {
scrollToBottom(); //
});
loading.value = true;
if (sessionID.value == 0) {
sessionName.value = end_msg.msg;
}
};
const sendMessageWithFile = async () => {
if (inputMessage.value.trim() === "") {
ElMessage.warning("消息不能为空");
return;
}
let end_msg = {
msg: inputMessage.value,
type: "null",
function: "gen-ai-chat",
session_id: sessionID.value,
model_id: selectModel.value,
temperature: temperature.value,
top_p: topP.value,
};
if (selectedFiles.value.length > 0) {
//
console.log("选中的文件:", selectedFiles.value);
let file_contents = []
let file_type = ""
for (let i = 0; i < selectedFiles.value.length; i++) {
let file: File = selectedFiles.value[i];
//jpgpnggifbmp
if (file.UserFileName.endsWith(".jpg") || file.UserFileName.endsWith(".png") || file.UserFileName.endsWith(".gif") || file.UserFileName.endsWith(".bmp")) {
file_type = "image_file"
} else {
file_type = "text_file"
}
let file_msg: FileMessage = {
file_content: file,
file_type: file_type,
};
file_contents.push(file_msg);
}
let msg: GenerationMessage = {
text: inputMessage.value,
file_content: file_contents,
};
@ -693,16 +735,16 @@ const sendMessageWithFile =async () => {
if (sessionID.value == 0) {
sessionName.value = inputMessage.value;
}
let pMsgContent ="";
let pMsgContent = "";
if (end_msg["is_file"]) {
let file_msg: GenerationMessage = JSON.parse(end_msg["msg"]);
//md
let file_content= file_msg.file_content
let file_content = file_msg.file_content
for (let i = 0; i < file_content.length; i++) {
if(file_content[i].file_type == "image_file"){
pMsgContent += `![${file_content[i].file_content.UserFileName}](${ fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
}else{
pMsgContent += `文件:[${file_content[i].file_content.UserFileName}](${ fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
if (file_content[i].file_type == "image_file") {
pMsgContent += `![${file_content[i].file_content.UserFileName}](${fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
} else {
pMsgContent += `文件:[${file_content[i].file_content.UserFileName}](${fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
}
}
@ -721,7 +763,7 @@ const sendMessageWithFile =async () => {
}
};
const IMWSConnect = async () =>{
const IMWSConnect = async () => {
let url =
"wss://pm.ljsea.top/im/ai_chat_ws?" +
"token=" +
@ -729,7 +771,7 @@ const IMWSConnect = async () =>{
//
let test_url = "ws://127.0.0.1:8084/im/ai_chat_ws?" + "token=" + localStorage.getItem("token");
//url =test_url;
socket.value =new WebSocket(url);
socket.value = new WebSocket(url);
socket.value.onopen = () => {
console.log("WebSocket 连接已建立");
ElMessage.success("连接成功");
@ -805,15 +847,15 @@ const getMessage = async (session_id: number) => {
for (let i = 0; i < data.length; i++) {
if (data[i]["Type"] === 3) {
let msg: GenMessage = data[i];
let pMsgContent="";
let pMsgContent = "";
if (msg.Status == 3) {
let file_msg: GenerationMessage = JSON.parse(msg.Msg);
let file_content= file_msg.file_content
let file_content = file_msg.file_content
for (let i = 0; i < file_content.length; i++) {
if(file_content[i].file_type == "image_file"){
pMsgContent += `![${file_content[i].file_content.UserFileName}](${ fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
}else{
pMsgContent += `文件:[${file_content[i].file_content.UserFileName}](${ fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
if (file_content[i].file_type == "image_file") {
pMsgContent += `![${file_content[i].file_content.UserFileName}](${fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
} else {
pMsgContent += `文件:[${file_content[i].file_content.UserFileName}](${fileUrl + file_content[i].file_content.file_store_name})` + "\n\n";
}
}
pMsgContent = pMsgContent + file_msg.text;
@ -857,11 +899,11 @@ const getMessageWithFile = async (session_id: number) => {
for (let i = 0; i < data.length; i++) {
if (data[i]["Type"] === 3) {
let msg: GenMessage = data[i];
let pMsgContent="";
let pMsgContent = "";
if (msg.Status == 3) {
let img_msg: ImageMessage = JSON.parse(msg.Msg);
//md
let img_content= img_msg.image_content
let img_content = img_msg.image_content
for (let i = 0; i < img_content.length; i++) {
pMsgContent += `![${img_content[i].img_name}](${img_content[i].img_url})` + "\n";
}
@ -919,6 +961,25 @@ const copyMessage = (content: string) => {
});
};
const checkTokenIsValidAndRefresh = () => {
let last_refresh = localStorage.getItem("refresh_time"); //localStorage.setItem("refresh_time", Date.now().toString());
let now = Date.now();
let isValid = false;
if (last_refresh) {
let diff = now - parseInt(last_refresh);
//1token
if (diff < 60 * 60 * 1000) {
isValid = true;
}
} else {
isValid = false;
}
if (isValid == false) {
GetModelListByFunctionName();
}
};
const GetModelListByFunctionName = async () => {
let req = {
function: "gen-ai-chat",
@ -928,14 +989,14 @@ const GetModelListByFunctionName = async () => {
let result = await FindModelListByFunctionName(req);
if (result["code"] === 0) {
ModelList.value = result["data"];
if(userUIconfigInfo.value.gen_ai_function.model_id ==0 ){
if (userUIconfigInfo.value.gen_ai_function.model_id == 0) {
selectModel.value = ModelList.value[0].ID;
}else{
} else {
selectModel.value = userUIconfigInfo.value.gen_ai_function.model_id;
temperature.value = userUIconfigInfo.value.gen_ai_function.temperature;
topP.value = userUIconfigInfo.value.gen_ai_function.top_p;
sessionID.value = userUIconfigInfo.value.gen_ai_function.session_id;
if (sessionID.value != 0){
if (sessionID.value != 0) {
getMessage(sessionID.value);
}
}
@ -1031,7 +1092,8 @@ const HandleTextToDocFile = async () => {
.chat-messages {
flex: 1;
overflow-y: auto; /* 允许垂直滚动 */
overflow-y: auto;
/* 允许垂直滚动 */
padding: 10px;
margin-bottom: 20px;
scrollbar-width: 10px;
@ -1045,7 +1107,8 @@ const HandleTextToDocFile = async () => {
flex: 1;
display: flex;
flex-direction: column;
overflow-y: auto; /* 确保内部可以滚动 */
overflow-y: auto;
/* 确保内部可以滚动 */
height: 100%;
}
@ -1104,6 +1167,7 @@ const HandleTextToDocFile = async () => {
margin-bottom: 20px;
height: 20%;
}
.session-card2 {
margin-bottom: 20px;
height: 70%;
@ -1160,23 +1224,28 @@ const HandleTextToDocFile = async () => {
border-radius: 4px;
margin-bottom: 10px;
}
.file-icon {
margin-right: 5px;
color: #606266;
}
.file-time {
float: right;
color: #909399;
font-size: 12px;
}
.footer-bar {
display: flex;
justify-content: space-between;
align-items: center;
}
.selected-count {
margin-right: 10px;
}
.el-icon-document {
color: #409eff;
}
@ -1193,6 +1262,7 @@ const HandleTextToDocFile = async () => {
.model-params {
margin-top: 10px;
}
.param-value {
margin-top: 5px;
text-align: right;

File diff suppressed because it is too large Load Diff

View File

@ -503,6 +503,11 @@
dependencies:
langium "3.3.1"
"@microsoft/fetch-event-source@^2.0.1":
version "2.0.1"
resolved "https://registry.npmjs.org/@microsoft/fetch-event-source/-/fetch-event-source-2.0.1.tgz"
integrity sha512-W6CLUJ2eBMw3Rec70qrsEW0jOm/3twwJv21mrmj2yORiaVmVYGS4sSS5yUwvQc1ZlDLYGPnClVWmUUMagKNsfA==
"@nodelib/fs.scandir@2.1.5":
version "2.1.5"
resolved "https://registry.npmjs.org/@nodelib/fs.scandir/-/fs.scandir-2.1.5.tgz"