Add File
This commit is contained in:
290
main/ui/llm_chat.aps
Normal file
290
main/ui/llm_chat.aps
Normal file
@@ -0,0 +1,290 @@
|
||||
<div x-data="chat" class="mx-auto flex w-full">
|
||||
<div class="mx-4 flex w-full flex-col h-[90vh]">
|
||||
<!-- 对话的展示-->
|
||||
<div id="chat_display" class="flex-grow overflow-hidden overflow-y-auto">
|
||||
|
||||
<!-- 对话列表-->
|
||||
<div class="">
|
||||
<div class="flex flex-col gap-2 mx-4">
|
||||
<!-- AI's Response -->
|
||||
<div class="w-full mt-2 border-zinc-300 bg-zinc-100 p-2 text-left dark:border-zinc-700 dark:bg-zinc-800 rounded border" >
|
||||
|
||||
<div class="flex items-center gap-2 text-neutral-900 dark:text-zinc-50">
|
||||
<span class="flex size-8 items-center justify-center rounded-full bg-sky-700 text-white dark:bg-sky-600 dark:text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-5">
|
||||
<path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135" />
|
||||
<path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5" />
|
||||
</svg>
|
||||
</span>
|
||||
<span class="text-sm font-bold">Yi.AI</span>
|
||||
</div>
|
||||
<p class="text-pretty sm:pl-10 mt-4 sm:mt-0 text-sm text-neutral-600 dark:text-zinc-200">
|
||||
厂家:<span x-text="llm.title"></span><br>
|
||||
地址:<span x-text="llm.url"></span><br>
|
||||
模型:<span x-text="llm.name"></span><br>
|
||||
现在可以对话测试了
|
||||
</p>
|
||||
</div>
|
||||
|
||||
|
||||
<template x-for="(item, index) in Items" :key="index">
|
||||
<div>
|
||||
<!-- User's Chat -->
|
||||
<div :id="'QA'+index" class="w-full ml-auto max-w-xs border-zinc-300 bg-zinc-100 p-2 text-left dark:border-zinc-700 dark:bg-zinc-800 rounded border" >
|
||||
<div class="flex items-center gap-2 text-neutral-900 dark:text-zinc-50">
|
||||
<span class="flex size-8 items-center justify-center rounded-full bg-blue-700 text-white dark:bg-sky-600 dark:text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-5">
|
||||
<path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135" />
|
||||
<path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5" />
|
||||
</svg>
|
||||
</span>
|
||||
<span x-text="item.req" class="text-sm">Alice Brown</span>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- AI's Response -->
|
||||
<div x-show="item.rsp_show" class="mt-2 w-full border-zinc-300 bg-zinc-100 p-2 text-left dark:border-zinc-700 dark:bg-zinc-800 rounded border" >
|
||||
|
||||
<div class="flex items-center gap-2 text-neutral-900 dark:text-zinc-50">
|
||||
<span class="flex size-8 items-center justify-center rounded-full bg-sky-700 text-white dark:bg-sky-600 dark:text-white">
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" aria-hidden="true" class="size-5">
|
||||
<path d="M6 12.5a.5.5 0 0 1 .5-.5h3a.5.5 0 0 1 0 1h-3a.5.5 0 0 1-.5-.5M3 8.062C3 6.76 4.235 5.765 5.53 5.886a26.6 26.6 0 0 0 4.94 0C11.765 5.765 13 6.76 13 8.062v1.157a.93.93 0 0 1-.765.935c-.845.147-2.34.346-4.235.346s-3.39-.2-4.235-.346A.93.93 0 0 1 3 9.219zm4.542-.827a.25.25 0 0 0-.217.068l-.92.9a25 25 0 0 1-1.871-.183.25.25 0 0 0-.068.495c.55.076 1.232.149 2.02.193a.25.25 0 0 0 .189-.071l.754-.736.847 1.71a.25.25 0 0 0 .404.062l.932-.97a25 25 0 0 0 1.922-.188.25.25 0 0 0-.068-.495c-.538.074-1.207.145-1.98.189a.25.25 0 0 0-.166.076l-.754.785-.842-1.7a.25.25 0 0 0-.182-.135" />
|
||||
<path d="M8.5 1.866a1 1 0 1 0-1 0V3h-2A4.5 4.5 0 0 0 1 7.5V8a1 1 0 0 0-1 1v2a1 1 0 0 0 1 1v1a2 2 0 0 0 2 2h10a2 2 0 0 0 2-2v-1a1 1 0 0 0 1-1V9a1 1 0 0 0-1-1v-.5A4.5 4.5 0 0 0 10.5 3h-2zM14 7.5V13a1 1 0 0 1-1 1H3a1 1 0 0 1-1-1V7.5A3.5 3.5 0 0 1 5.5 4h5A3.5 3.5 0 0 1 14 7.5" />
|
||||
</svg>
|
||||
</span>
|
||||
<span x-text="item.rsp" style="overflow-wrap: break-word;" class="text-sm font-bold">Pengu AI</span>
|
||||
<button @click="show_info(index)" class="rounded-full p-1 text-neutral-600/75 hover:bg-zinc-900/10 hover:text-neutral-600 focus:outline-none focus-visible:text-neutral-600 focus-visible:zinc-300 focus-visible:outline-offset-0 focus-visible:outline-sky-700 active:bg-zinc-900/5 active:-outline-offset-2 dark:text-zinc-200/75 dark:hover:bg-zinc-50/10 dark:hover:text-zinc-200 dark:focus-visible:text-zinc-200 dark:focus-visible:outline-sky-600 dark:active:bg-zinc-50/5" title="上下文信息" aria-label="上下文信息" >
|
||||
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16" fill="currentColor" class="size-4" aria-hidden="true">
|
||||
<path d="M9.5 13a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0m0-5a1.5 1.5 0 1 1-3 0 1.5 1.5 0 0 1 3 0"/>
|
||||
</svg>
|
||||
</button>
|
||||
</div>
|
||||
<p x-bind:id="item.chat_id" :x-ref="item.chat_id" class="text-pretty sm:pl-10 mt-4 sm:mt-0 text-sm text-neutral-600 dark:text-zinc-200"></p>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</template>
|
||||
</ul>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<!-- 输入框 -->
|
||||
<div class="flex w-full flex-col gap-2 mb-4 mt-2">
|
||||
<div class="relative w-full">
|
||||
<label class="sr-only">ai prompt</label>
|
||||
<!-- 文件上传-->
|
||||
<input x-ref="fileInput" @input="upload_file" type="file" multiple hidden class="w-full overflow-clip rounded border border-red-700 bg-zinc-100/50 text-sm text-red-700 file:mr-4 file:cursor-pointer file:border-none file:bg-zinc-100 file:px-4 file:py-2 file:font-medium file:text-neutral-900 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:bg-zinc-800/50 dark:file:bg-zinc-800 dark:file:text-zinc-50 dark:focus-visible:outline-sky-600" />
|
||||
|
||||
<svg @click="$refs.fileInput.click()" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 24 24" aria-hidden="true" class="absolute left-3 top-1/2 size-6 cursor-pointer -translate-y-1/2 fill-blue-700 dark:fill-sky-600">
|
||||
<path fill-rule="evenodd" d="M10.5 3.75a6 6 0 0 0-5.98 6.496A5.25 5.25 0 0 0 6.75 20.25H18a4.5 4.5 0 0 0 2.206-8.423 3.75 3.75 0 0 0-4.133-4.303A6.001 6.001 0 0 0 10.5 3.75Zm2.03 5.47a.75.75 0 0 0-1.06 0l-3 3a.75.75 0 1 0 1.06 1.06l1.72-1.72v4.94a.75.75 0 0 0 1.5 0v-4.94l1.72 1.72a.75.75 0 1 0 1.06-1.06l-3-3Z" clip-rule="evenodd"/>
|
||||
</svg>
|
||||
|
||||
<input x-show="one" x-model="query" x-on:input.debounce.100ms="query_len" x-on:keydown.enter="gen" type="text" class="w-full border-outline bg-zinc-100 border border-zinc-300 rounded px-2 py-2 pl-10 pr-24 text-sm text-neutral-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-200 dark:focus-visible:outline-sky-600" value="" name="prompt" placeholder="Ask AI ..." />
|
||||
|
||||
<textarea id="aiPromt" x-show="!one" rows="5" x-model="query" x-on:input.debounce.100ms="query_len" type="text" class="w-full border-outline bg-zinc-100 border border-zinc-300 rounded px-2 py-2 pl-10 pr-24 text-sm text-neutral-600 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 disabled:cursor-not-allowed disabled:opacity-75 dark:border-zinc-700 dark:bg-zinc-800/50 dark:text-zinc-200 dark:focus-visible:outline-sky-600" value="" name="prompt" placeholder="Ask AI ..." ></textarea>
|
||||
|
||||
<button type="button" x-show="is_gen" x-on:click="gen" class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer bg-sky-700 rounded px-2 py-1 text-xs tracking-wide text-white transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">Generate</button>
|
||||
<button type="button" x-show="!is_gen" x-on:click="stop" class="absolute right-3 top-1/2 -translate-y-1/2 cursor-pointer bg-blue-700 rounded px-2 py-1 text-xs tracking-wide text-white transition hover:opacity-75 focus-visible:outline focus-visible:outline-2 focus-visible:outline-offset-2 focus-visible:outline-sky-700 active:opacity-100 active:outline-offset-0 dark:bg-sky-600 dark:text-white dark:focus-visible:outline-sky-600">Stop</button>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
|
||||
<script>
|
||||
function chat(){
|
||||
return {
|
||||
query:"",
|
||||
mode:"FA", //KG-知识库,NS-全网搜索,FA-自由回答,DS-深度思考
|
||||
eventSource: "", //对话流
|
||||
is_gen: true, //生成模式
|
||||
Info: false, // 显示上下文
|
||||
fItems:[], //上下文信息
|
||||
one:true, //一行文本输入
|
||||
style: "",
|
||||
do_chat_files:{}, //对话过程中产生的文件,主要是数据分析
|
||||
chat_files:[], //保留对话的文件的记录
|
||||
Items:[], //对话过程所有信息
|
||||
qItems:[], //对话问题的列表,用于多轮对话
|
||||
ai_file: {},
|
||||
llm:{},
|
||||
init(){
|
||||
this.llm = this.llm_cfg.find(item => item.id === this.tabSelected);
|
||||
},
|
||||
push_item(item){
|
||||
this.Items.push(item);
|
||||
},
|
||||
push_qitem(item){
|
||||
this.qItems.push(item);
|
||||
},
|
||||
update_last_item(chat_id,title,ctx){
|
||||
var last = this.Items[this.Items.length-1];
|
||||
last.chat_id=chat_id;
|
||||
last.rsp=title;
|
||||
last.rsp_show=true;
|
||||
last.ctx=ctx;
|
||||
},
|
||||
update_last_qitem(answer){
|
||||
//更新最后一个问题的答案
|
||||
var last = this.qItems[this.qItems.length-1];
|
||||
last.rsp=answer;
|
||||
},
|
||||
|
||||
query_len(){
|
||||
if (this.query.length >45){
|
||||
this.one=false;
|
||||
if (this.query.length >300){
|
||||
const aiPromt = document.getElementById("aiPromt");
|
||||
aiPromt.rows="10";
|
||||
}
|
||||
if (this.query.length >1000){
|
||||
const aiPromt = document.getElementById("aiPromt");
|
||||
aiPromt.rows="15";
|
||||
}
|
||||
}else{
|
||||
this.one=true;
|
||||
}
|
||||
},
|
||||
copy_query(query){ //复制查询
|
||||
this.query = query;
|
||||
this.query_len()
|
||||
|
||||
},
|
||||
|
||||
copy_that(chat_id){
|
||||
const messagesDiv = document.getElementById(chat_id);
|
||||
let rsp = messagesDiv.innerHTML;
|
||||
rsp = rsp.replace("<think>","<p>").replace("</think>","</p>");
|
||||
copyToClipboard(rsp);
|
||||
},
|
||||
|
||||
stop(){
|
||||
this.eventSource.close();
|
||||
this.is_gen = true;
|
||||
this.one=true;
|
||||
},
|
||||
gen(){
|
||||
this.is_gen = false;
|
||||
this.push_item({chat_id:"",req:this.query,rsp:"",rsp_show:false});
|
||||
|
||||
var query=this.query;
|
||||
if (this.query.length >100){
|
||||
query = this.query.slice(0,100)+"...";
|
||||
}
|
||||
// 数据对象,代表要发送的数据
|
||||
const newItem = {
|
||||
question: this.query,
|
||||
ai_mode: JSON.stringify(this.llm)
|
||||
};
|
||||
|
||||
// 使用 axios 发送 POST 请求
|
||||
axios.post('/api/chat_llm_test', newItem)
|
||||
.then(response =>{
|
||||
|
||||
rsp = response.data;
|
||||
|
||||
this.update_last_item(rsp.chat_id,`正在请求中...`,"");
|
||||
|
||||
|
||||
this.push_qitem({chat_id:rsp.chat_id,req:query});
|
||||
|
||||
// 创建一个EventSource实例连接到服务器发送的事件流
|
||||
this.eventSource = new EventSource('/api/chat_llm/'+rsp.chat_id);
|
||||
|
||||
update_chat(this.eventSource,rsp.chat_id,this);
|
||||
|
||||
const div = document.getElementById("chat_display");
|
||||
div.scrollTop = div.scrollHeight;
|
||||
|
||||
})
|
||||
.catch(error => {
|
||||
console.error('Error post data:', error);
|
||||
throw error; // 重新抛出错误以便调用者可以处理
|
||||
});
|
||||
|
||||
this.query="";
|
||||
this.one=true;
|
||||
const div = document.getElementById("chat_display");
|
||||
div.scrollTop = div.scrollHeight+20;
|
||||
},
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
//流式更新对话
|
||||
function update_chat(eventSource,chat_id,fun){
|
||||
fun.is_gen = false;
|
||||
var old_think="";
|
||||
//分块更新
|
||||
var ResponseDiv = null;
|
||||
var messagesDiv = document.createElement('div');
|
||||
|
||||
var do_files=[];
|
||||
// 监听消息事件,将接收到的数据显示在网页上
|
||||
eventSource.onmessage = function(event) {
|
||||
|
||||
//首次创建
|
||||
if (ResponseDiv==null){
|
||||
ResponseDiv = document.getElementById(chat_id);
|
||||
ResponseDiv.appendChild(messagesDiv);
|
||||
}
|
||||
|
||||
const message = JSON.parse(event.data);
|
||||
|
||||
if (message.rsp.indexOf("<think>")==0){
|
||||
var end = message.rsp.indexOf("</think>");
|
||||
if (end==-1){ //思考还未结束
|
||||
|
||||
var think_mk = marked.parse(message.rsp.slice(7));
|
||||
messagesDiv.innerHTML = old_think + think_mk;
|
||||
//messagesDiv.insertAdjacentHTML("beforeend",think_mk);
|
||||
}else{
|
||||
var think_mk = marked.parse(message.rsp.slice(7,end));
|
||||
var result= marked.parse(message.rsp.slice(end+8));
|
||||
if (result!=""){
|
||||
var think_all=`${think_mk}<div style="color: white; background-color: rgb(3 105 161 / 0.7); border-radius: 5px; margin-bottom: 5px; padding:5px;">${result}</div>`;
|
||||
messagesDiv.innerHTML = old_think+think_all;
|
||||
fun.update_last_qitem(result);
|
||||
}
|
||||
}
|
||||
|
||||
}else if(message.rsp.indexOf("<new>")==0){
|
||||
//新的一个消息,新的一个层
|
||||
messagesDiv = document.createElement('div');
|
||||
ResponseDiv.appendChild(messagesDiv);
|
||||
}else{
|
||||
var think_all=`<div style="color: white; background-color: rgb(3 105 161 / 0.7); border-radius: 5px; margin-bottom: 5px; padding:5px;">${marked.parse(message.rsp)}</div>`;
|
||||
messagesDiv.innerHTML = old_think+ think_all;
|
||||
fun.update_last_qitem(think_all);
|
||||
}
|
||||
|
||||
|
||||
|
||||
//自动滚动
|
||||
const div = document.getElementById("chat_display");
|
||||
div.scrollTop = div.scrollHeight;
|
||||
|
||||
};
|
||||
// 监听结束
|
||||
eventSource.onerror = function(error) {
|
||||
if (eventSource.readyState != EventSource.CLOSED){
|
||||
eventSource.close(); // 关闭连接
|
||||
}
|
||||
|
||||
fun.is_gen = true;
|
||||
fun.gen_ppt_ai(chat_id);
|
||||
console.info(chat_id+" 回答结束");
|
||||
if (do_files.length >0){
|
||||
fun.do_chat_files[chat_id]=do_files;
|
||||
console.info(do_files);
|
||||
}
|
||||
//结束后滚动
|
||||
const div = document.getElementById("chat_display");
|
||||
div.scrollTop = div.scrollHeight;
|
||||
};
|
||||
}
|
||||
|
||||
</script>
|
||||
Reference in New Issue
Block a user