数通互联化工云平台
You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

178 lines
4.3 KiB

<!--
@ 作者: han2015
@ 时间: 2025-05-12 15:39:13
@ 备注: aibot组件
-->
<script lang="ts" setup>
import { doAiChat,aiChatData} from "@/api/doc/space"
import {ElText,ElInput, ButtonInstance} from "element-plus";
import { VueMarkdown } from '@crazydos/vue-markdown'
import rehypeRaw from 'rehype-raw'
import remarkGfm from 'remark-gfm'
//选中的咨询模式
const checkedModel = ref([])
const baseURL=import.meta.env.VITE_APP_BASE_API
const conversation=ref("") //当前会话的uuid
const myquestion=ref('')
const controller = ref<AbortController | null>(null)
const interact_msg=ref<{ask:boolean,think:string,content:string}[]>([])
const props = withDefaults(defineProps<{
//closefunc:()=>void,
//agent:{model:boolean,name:string,uuid:string[]}
}>(),{})
//消息体
interface message{
ask:boolean,
think:string,
content:string
}
//会话记录
interface chatRecord{
uuid:string,
agentuuid:string,
brief:string,
messages:message[]
}
/* 中断函数,供按钮调用 */
function abortFetch() {
if (controller.value) {
controller.value.abort()
controller.value = null
}
}
defineExpose({onSendParamToAI})
async function onSendParamToAI(user:string,arr:Array<{ uuids: string[]; params: { [key: string]: any } }>){
interact_msg.value=[]
for (let ele of arr){
interact_msg.value.unshift({ask:true,think:"", content:"AI正在分析。。。"})
for (let uid of ele.uuids){
await doRequest(user,uid,ele.params)
}
}
}
async function doRequest(_user:string,uuid:string,param:any){
let mRespMsg=""
const params={
"onlineSearch":"否",
"useDataset":"否"
}
for (let item of checkedModel.value){
if(item==="onlineSearch") params.onlineSearch="是"
if(item==="useDataset") params.useDataset="是"
}
controller.value = new AbortController();
try{
const res= await doAiChat(`${baseURL}/aibot/assisted/${uuid}/chat`,{
inputs: params,
query:JSON.stringify(param),
response_mode:"streaming",
conversation_id:"",//conversation.value,
user:_user,//这里已经base64解析了
},controller.value.signal
)
if (!res.ok) {
throw new Error(`HTTP ${res.status} ${res.statusText}`);
}
const reader = res.body!.getReader();
const decoder = new TextDecoder('utf-8');
let chunk = ''; // 行缓存
while (true) {
const {done, value} = await reader.read()
if (done) break;
// 服务器可能一次返回多行,需要手动按行拆分
chunk += decoder.decode(value, {stream: true});
const lines = chunk.split(/\n/);
chunk = lines.pop() || ''
for (const line of lines) {
if (!line.trim()) continue; // 跳过空行
if (line.startsWith('data: ')) {
const data = line.slice(6);
const json = JSON.parse(data);
if(json.event==="message"){
//conversation.value=json.conversation_id
mRespMsg+=json.answer
}
}
}
}
}catch (e: any) {
if (e.name === 'AbortError') {
console.log('用户手动中断')
}
}
interact_msg.value.unshift({ask:true,think:"", content:JSON.stringify(param)})
const arr=mRespMsg.split("</think>")
if(arr.length===1){
interact_msg.value.unshift({ask:false,think:"",content:arr[0]})
}else{
//思考模式
interact_msg.value.unshift({ask:false,think:arr[0],content:arr[1]})
}
}
function resetContext(){
interact_msg.value=[]
conversation.value=""
//props.closefunc()
}
//渲染完页面再执行
// onMounted(() => {
// });
</script>
<template>
<div :style="{backgroundColor:'#f3f3f3'}">
<div class="reply_area" >
<template v-for="msg of interact_msg">
<div v-if="msg.ask" class="t_ask" >{{ msg.content }}</div>
<div v-else class="t_resp">
{{ msg.content }}
<!-- <VueMarkdown :markdown="msg.content"></VueMarkdown> -->
</div>
</template>
</div>
</div>
</template>
<style lang="scss" scoped>
.reply_area{
width: 260px;
background: white;
overflow-y: auto;
display: flex;
flex-direction: column;
overflow-wrap:anywhere;
}
.t_ask{
margin: 5px;
align-self: end;
background-color: rgb(188 211 241);
border-radius:10px;
}
.t_resp{
margin: 5px;
align-self: start;
color: black;
background-color: rgb(222, 243, 222);
}
</style>