Compare commits

...

3 Commits
main ... main

Author SHA1 Message Date
e94697e245 更新 src/components/chat/MessageList.vue 2024-10-15 11:48:36 +00:00
75b4d63b62 更新 src/pages/Guest.vue 2024-10-15 09:55:36 +00:00
255dbe2abf 添加ai search block
Signed-off-by: jwai <jwai4789@gmail.com>
2024-10-15 07:56:40 +00:00
2 changed files with 129 additions and 171 deletions

View File

@ -1,188 +1,138 @@
<template>
<div class="text-base">
<n-divider> <small class="select-none">这是聊天的开头</small> </n-divider>
<div class="chat-container">
<div class="chat-messages">
</div>
<div v-for="(message, index) in chat_messages" :key="index">
<div class="mt-10 mb-10"></div>
<div v-if="message.role === 'file'">
<!-- 文件类型 -->
<n-flex justify="end">
<div class="flex items-center flex-nowrap">
<div class="flex items-end flex-col">
<div>
<n-divider class="!p-0 !m-0" title-placement="right">
{{ userStore.user.name }}
</n-divider>
</div>
<n-image
v-if="
message.file && message.file.mime_type?.startsWith('image/')
"
width="100"
:src="fileBaseUrl + '/download/' + message.file.file_hash"
/>
<n-text italic depth="3" v-else> 你上传了一个文件 </n-text>
</div>
<div class="relative h-full">
<n-avatar
round
size="large"
:src="userStore.user.avatar"
class="ml-3 min-w-10 absolute top-0"
/>
</div>
</div>
</n-flex>
</div>
<div v-else-if="(message.role === 'user' || message.role === 'user_hide' || message.role === 'user_later') && message.content">
<!-- 用户消息 -->
<n-flex justify="end">
<div class="flex items-center flex-nowrap">
<!-- <vue-markdown-it
:source="message.content"
:options="markdownOptions"
:plugins="markdownPlugins"
/> -->
<!-- <v-md-preview :text="message.content" height="500px"></v-md-preview> -->
<div class="flex items-end flex-col">
<div>
<n-divider class="!p-0 !m-0" title-placement="right">
{{ userStore.user.name }}
</n-divider>
</div>
<div
v-if="mdInited"
class="break-all break-words markdown-body"
v-html="mdIt.render(message.content)"
></div>
</div>
<div class="relative h-full">
<n-avatar
round
size="large"
:src="userStore.user.avatar"
class="ml-3 min-w-10 absolute top-0"
/>
</div>
</div>
</n-flex>
</div>
<div v-else-if="message.role === 'assistant' && message.content">
<!-- 助理消息 -->
<n-flex justify="start" class="!flex-nowrap">
<div class="relative h-full">
<n-avatar
round
size="large"
:src="leaflowPng"
class="min-w-10 min-h-10 p-1.5 absolute top-0 !bg-transparent"
/>
</div>
<div class="flex items-center flex-nowrap">
<!-- <vue-markdown-it
:source="message.content"
:options="markdownOptions"
:plugins="[markdownPlugins]"
/> -->
<!-- <div v-html="mdIt.render('# Math Rulez! \n $\\sqrt{3x-1}+(1+x)^2$')"></div> -->
<!-- message.content 变化时重新渲染 -->
<div>
<div
v-if="
message.assistant_id &&
message.assistant !== null &&
message.assistant?.name !== ''
"
>
<n-divider class="!p-0 !m-0" title-placement="left">
{{ message.assistant?.name }}
</n-divider>
</div>
<div
v-if="mdInited"
class="break-all break-words markdown-body"
v-html="mdIt.render(message.content)"
></div>
</div>
<!-- <div v-html="mdIt.render(message.content)"></div> -->
<!-- <v-md-preview :text="message.content" height="500px"></v-md-preview> -->
</div>
</n-flex>
//advice and textarea
<div class="chat-input-container">
<textarea
v-model="message"
@input="onInput"
ref="inputBox"
rows="1"
class="chat-input"
placeholder="输入你的消息..."
></textarea>
<button @click="sendMessage" :disabled="sending">发送</button>
<div v-if="suggestions.length" class="suggestions">
<ul>
<li
v-for="(suggestion, index) in suggestions"
:key="index"
@click="selectSuggestion(suggestion)"
>
{{ suggestion }}
</li>
</ul>
</div>
</div>
</div>
<!-- 这里要加上输入框占的部分但是那个占了很多所以设置大一点 -->
<div style="margin-bottom: 10rem"></div>
</template>
<script lang="ts" setup>
import { Ref } from "vue";
import { EntityChatMessage } from "@/api";
import { useUserStore } from "@/stores/user";
import leaflowPng from "@/assets/images/leaflow.png";
import markdownKatex from "@traptitech/markdown-it-katex";
import markdownIt from "markdown-it";
// highlightjs
import hljs from "highlight.js";
import config from "@/config/config";
import { ref } from "vue";
const mdIt = markdownIt();
const mdInited = ref(true);
const message = ref("");
const suggestions = ref<string[]>([]);
const sending = ref(false);
const unsupportedLanguages = ["assembly", "blade", "vue"];
mdIt.options.highlight = function (str: string, lang: string) {
// TODO:
lang = "text"
if (!lang || unsupportedLanguages.includes(lang)) {
// return str;
lang = "text"
}
return hljs.highlight(str, { language: lang }).value;
//
const fetchSuggestions = (query: string) => {
//
return new Promise<string[]>((resolve) => {
setTimeout(() => {
resolve(["建议1", "建议2", "建议3"].filter((suggestion) => suggestion.includes(query)));
}, 300);
});
};
mdIt.use(markdownKatex, {
throwOnError: false,
errorColor: "#cc0000",
output: "html",
});
// async function initMD() {
// mdIt.use(
// await Shiki({
// themes: {
// light: "vitesse-light",
// dark: "vitesse-dark",
// },
// })
// );
const onInput = async () => {
adjustHeight();
if (message.value) {
suggestions.value = await fetchSuggestions(message.value);
} else {
suggestions.value = [];
}
};
// mdIt.use(markdownKatex, {
// throwOnError: false,
// errorColor: "#cc0000",
// output: "html",
// });
const adjustHeight = () => {
const inputBox = refs.inputBox as HTMLTextAreaElement;
inputBox.style.height = 'auto';
inputBox.style.height = inputBox.scrollHeight + 'px';
};
// mdInited.value = true;
// }
const userStore = useUserStore();
const selectSuggestion = (suggestion: string) => {
message.value = suggestion;
suggestions.value = [];
};
const props = defineProps({
chat_messages: {
required: true,
type: Array as () => EntityChatMessage[],
},
});
const sendMessage = async () => {
if (!message.value.trim()) return;
const chat_messages = toRef(props, "chat_messages") as Ref<EntityChatMessage[]>;
const fileBaseUrl = config.backend + "/api/v1/files";
onMounted(() => {
// initMD();
});
sending.value = true;
try {
//
console.log("发送消息: ", message.value);
message.value = "";
suggestions.value = [];
adjustHeight();
} catch (error) {
console.error("发送消息失败: ", error);
} finally {
sending.value = false;
}
};
</script>
<style scoped>
.chat-container {
display: flex;
flex-direction: column;
height: 100%;
}
.chat-messages {
flex: 1;
overflow-y: auto;
padding: 1rem;
}
.chat-input-container {
display: flex;
flex-direction: column;
border-top: 1px solid #ccc;
padding: 0.5rem;
}
.chat-input {
width: 100%;
resize: none;
padding: 0.5rem;
margin-bottom: 0.5rem;
border: 1px solid #ccc;
border-radius: 0.5rem;
}
.suggestions {
border: 1px solid #ccc;
border-radius: 0.5rem;
background-color: #fff;
max-height: 200px;
overflow-y: auto;
}
.suggestions ul {
list-style: none;
padding: 0;
margin: 0;
}
.suggestions li {
padding: 0.5rem;
cursor: pointer;
}
.suggestions li:hover {
background-color: #f0f0f0;
}
</style>

View File

@ -147,6 +147,14 @@ const features = ref([
longDescription: '文档管理是 Amber 知识库的核心功能之一。对话中上传的文件将自动保存至知识库您只需设置助理关联知识库即可实现快速检索和使用。假设您是一位项目经理您可以在对话中上传项目计划书并在需要时快速检索相关内容。Amber 在您发送消息时,将会自动搜索资料库。此外,我们正在开发桌面端软件,该软件可以根据您的需要同步文档至 Amber以便随时调用🗂。',
showLong: false
},
{
icon: "🔍",
title: "AI 搜索功能",
shortDescription: "利用 AI 技术进行快速准确的信息搜索。",
longDescription:
"Amber 的 AI 搜索功能让信息检索变得更加高效和智能。通过自然语言处理技术您可以用简单的对话方式从海量数据中快速找到所需信息。无论是搜索文档、网页内容还是数据库记录Amber 都能提供准确且相关的搜索结果,节省您的时间和精力。",
showLong: false,
},
]);
const partners = ref([