feat: 新增停止大模型回答功能(关联 Issue #104)
This commit is contained in:
commit
964e5f9079
@ -1,7 +1,8 @@
|
|||||||
source "https://rubygems.org"
|
source "https://rubygems.org"
|
||||||
|
|
||||||
gem "jekyll", "~> 4.3.2"
|
gem "jekyll", "~> 4.3.2"
|
||||||
gem "just-the-docs", "~> 0.5.0"
|
# gem "just-the-docs", "~> 0.5.0"
|
||||||
|
gem "minima", "~> 2.5"
|
||||||
|
|
||||||
group :jekyll_plugins do
|
group :jekyll_plugins do
|
||||||
gem "jekyll-include-cache"
|
gem "jekyll-include-cache"
|
||||||
@ -22,4 +23,4 @@ gem "wdm", "~> 0.1.1", :platforms => [:mingw, :x64_mingw, :mswin]
|
|||||||
|
|
||||||
# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
|
# Lock `http_parser.rb` gem to `v0.6.x` on JRuby builds since newer versions of the gem
|
||||||
# do not have a Java counterpart.
|
# do not have a Java counterpart.
|
||||||
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
|
gem "http_parser.rb", "~> 0.6.0", :platforms => [:jruby]
|
||||||
@ -1,4 +1,4 @@
|
|||||||
remote_theme: just-the-docs/just-the-docs
|
# remote_theme: just-the-docs/just-the-docs
|
||||||
url: https://xerrors.github.io/Yuxi-Know
|
url: https://xerrors.github.io/Yuxi-Know
|
||||||
baseurl: /Yuxi-Know
|
baseurl: /Yuxi-Know
|
||||||
title: 语析 - 基于大模型的知识库与知识图谱问答系统
|
title: 语析 - 基于大模型的知识库与知识图谱问答系统
|
||||||
@ -23,4 +23,4 @@ defaults:
|
|||||||
type: pages
|
type: pages
|
||||||
values:
|
values:
|
||||||
layout: home
|
layout: home
|
||||||
search_exclude: true
|
search_exclude: true
|
||||||
@ -1,5 +1,5 @@
|
|||||||
---
|
---
|
||||||
layout: home
|
layout: default
|
||||||
title: 语析 - 基于大模型的知识库与知识图谱问答系统
|
title: 语析 - 基于大模型的知识库与知识图谱问答系统
|
||||||
nav_order: 1
|
nav_order: 1
|
||||||
description: "语析是一个强大的问答平台,结合了大模型 RAG 知识库与知识图谱技术,基于 Llamaindex + VueJS + FastAPI + Neo4j 构建。"
|
description: "语析是一个强大的问答平台,结合了大模型 RAG 知识库与知识图谱技术,基于 Llamaindex + VueJS + FastAPI + Neo4j 构建。"
|
||||||
@ -133,4 +133,4 @@ docker tag m.daocloud.io/docker.io/library/neo4j:latest neo4j:latest
|
|||||||
|
|
||||||
## 许可证
|
## 许可证
|
||||||
|
|
||||||
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件
|
本项目采用 MIT 许可证 - 详见 [LICENSE](LICENSE) 文件
|
||||||
@ -9,11 +9,29 @@
|
|||||||
>
|
>
|
||||||
<MenuOutlined />
|
<MenuOutlined />
|
||||||
</div>
|
</div>
|
||||||
<a-tooltip :title="configStore.config?.model_name" placement="rightTop">
|
|
||||||
<div class="newchat nav-btn" @click="$emit('newconv')">
|
<div class="newchat nav-btn" @click="$emit('newconv')">
|
||||||
<PlusCircleOutlined /> <span class="text">新对话</span>
|
<PlusCircleOutlined /> <span class="text">新对话</span>
|
||||||
</div>
|
</div>
|
||||||
</a-tooltip>
|
<a-dropdown>
|
||||||
|
<a class="model-select nav-btn" @click.prevent>
|
||||||
|
<BulbOutlined /> <span class="text">{{ configStore.config?.model_provider }}/{{ configStore.config?.model_name }}</span>
|
||||||
|
</a>
|
||||||
|
<template #overlay>
|
||||||
|
<a-menu class="scrollable-menu">
|
||||||
|
<a-menu-item-group v-for="(item, key) in modelKeys" :key="key" :title="modelNames[item]?.name">
|
||||||
|
<a-menu-item v-for="(model, idx) in modelNames[item]?.models" :key="`${item}-${idx}`" @click="selectModel(item, model)">
|
||||||
|
{{ item }}/{{ model }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu-item-group>
|
||||||
|
<a-menu-item-group v-if="customModels.length > 0" title="自定义模型">
|
||||||
|
<a-menu-item v-for="(model, idx) in customModels" :key="`custom-${idx}`" @click="selectModel('custom', model.custom_id)">
|
||||||
|
custom/{{ model.custom_id }}
|
||||||
|
</a-menu-item>
|
||||||
|
</a-menu-item-group>
|
||||||
|
</a-menu>
|
||||||
|
</template>
|
||||||
|
</a-dropdown>
|
||||||
</div>
|
</div>
|
||||||
<div class="header__right">
|
<div class="header__right">
|
||||||
<div class="nav-btn text" @click="opts.showPanel = !opts.showPanel">
|
<div class="nav-btn text" @click="opts.showPanel = !opts.showPanel">
|
||||||
@ -179,7 +197,7 @@
|
|||||||
</template>
|
</template>
|
||||||
|
|
||||||
<script setup>
|
<script setup>
|
||||||
import { reactive, ref, onMounted, toRefs, nextTick, onUnmounted, watch } from 'vue'
|
import { reactive, ref, onMounted, toRefs, nextTick, onUnmounted, watch, computed } from 'vue'
|
||||||
import {
|
import {
|
||||||
SendOutlined,
|
SendOutlined,
|
||||||
MenuOutlined,
|
MenuOutlined,
|
||||||
@ -199,7 +217,7 @@ import {
|
|||||||
FolderOpenOutlined,
|
FolderOpenOutlined,
|
||||||
GlobalOutlined,
|
GlobalOutlined,
|
||||||
FileTextOutlined,
|
FileTextOutlined,
|
||||||
RobotOutlined,
|
BulbOutlined,
|
||||||
CaretRightOutlined,
|
CaretRightOutlined,
|
||||||
DeploymentUnitOutlined,
|
DeploymentUnitOutlined,
|
||||||
StopOutlined
|
StopOutlined
|
||||||
@ -638,6 +656,7 @@ watch(
|
|||||||
{ deep: true }
|
{ deep: true }
|
||||||
);
|
);
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
// 处理发送或停止
|
// 处理发送或停止
|
||||||
const handleSendOrStop = () => {
|
const handleSendOrStop = () => {
|
||||||
if (isStreaming.value) {
|
if (isStreaming.value) {
|
||||||
@ -666,6 +685,22 @@ const retryStoppedMessage = (message) => {
|
|||||||
conv.value.messages = conv.value.messages.slice(0, messageIndex);
|
conv.value.messages = conv.value.messages.slice(0, messageIndex);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
const modelNames = computed(() => configStore.config?.model_names)
|
||||||
|
const modelStatus = computed(() => configStore.config?.model_provider_status)
|
||||||
|
const customModels = computed(() => configStore.config?.custom_models || [])
|
||||||
|
|
||||||
|
// 筛选 modelStatus 中为真的key
|
||||||
|
const modelKeys = computed(() => {
|
||||||
|
return Object.keys(modelStatus.value || {}).filter(key => modelStatus.value?.[key])
|
||||||
|
})
|
||||||
|
|
||||||
|
// 选择模型的方法
|
||||||
|
const selectModel = (provider, name) => {
|
||||||
|
configStore.setConfigValue('model_provider', provider)
|
||||||
|
configStore.setConfigValue('model_name', name)
|
||||||
|
message.success(`已切换到模型: ${provider}/${name}`)
|
||||||
|
>>>>>>> 2cec299306b323c10368dd3af82c110753ae963b
|
||||||
}
|
}
|
||||||
</script>
|
</script>
|
||||||
|
|
||||||
@ -709,9 +744,9 @@ const retryStoppedMessage = (message) => {
|
|||||||
border-radius: 8px;
|
border-radius: 8px;
|
||||||
color: var(--gray-900);
|
color: var(--gray-900);
|
||||||
cursor: pointer;
|
cursor: pointer;
|
||||||
font-size: 1rem;
|
// font-size: 1rem;
|
||||||
width: auto;
|
width: auto;
|
||||||
padding: 0.5rem 1rem;
|
padding: 0.5rem 0.75rem;
|
||||||
|
|
||||||
.text {
|
.text {
|
||||||
margin-left: 10px;
|
margin-left: 10px;
|
||||||
@ -722,6 +757,18 @@ const retryStoppedMessage = (message) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
.model-select {
|
||||||
|
// color: var(--gray-900);
|
||||||
|
max-width: 300px;
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
white-space: nowrap;
|
||||||
|
|
||||||
|
.text {
|
||||||
|
overflow: hidden;
|
||||||
|
text-overflow: ellipsis;
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
.metas {
|
.metas {
|
||||||
display: flex;
|
display: flex;
|
||||||
@ -1170,6 +1217,7 @@ const retryStoppedMessage = (message) => {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
<<<<<<< HEAD
|
||||||
.retry-hint {
|
.retry-hint {
|
||||||
margin-top: 8px;
|
margin-top: 8px;
|
||||||
padding: 8px 16px;
|
padding: 8px 16px;
|
||||||
@ -1195,6 +1243,28 @@ const retryStoppedMessage = (message) => {
|
|||||||
&:hover {
|
&:hover {
|
||||||
background-color: #ff7875 !important;
|
background-color: #ff7875 !important;
|
||||||
}
|
}
|
||||||
|
=======
|
||||||
|
.scrollable-menu {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
|
||||||
|
&::-webkit-scrollbar {
|
||||||
|
width: 6px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-track {
|
||||||
|
background: transparent;
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb {
|
||||||
|
background: var(--gray-400);
|
||||||
|
border-radius: 3px;
|
||||||
|
}
|
||||||
|
|
||||||
|
&::-webkit-scrollbar-thumb:hover {
|
||||||
|
background: var(--gray-500);
|
||||||
|
>>>>>>> 2cec299306b323c10368dd3af82c110753ae963b
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
@ -1249,4 +1319,14 @@ const retryStoppedMessage = (message) => {
|
|||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|
||||||
|
<style lang="less">
|
||||||
|
// 添加全局样式以确保滚动功能在dropdown内正常工作
|
||||||
|
.ant-dropdown-menu {
|
||||||
|
&.scrollable-menu {
|
||||||
|
max-height: 300px;
|
||||||
|
overflow-y: auto;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
</style>
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
@ -94,7 +94,7 @@
|
|||||||
</div>
|
</div>
|
||||||
<div class="setting" v-if="state.windowWidth <= 520 || state.section === 'model'">
|
<div class="setting" v-if="state.windowWidth <= 520 || state.section === 'model'">
|
||||||
<h3>模型配置</h3>
|
<h3>模型配置</h3>
|
||||||
<p>请在 <code>src/.env</code> 文件中配置对应的 APIKEY</p>
|
<p>请在 <code>src/.env</code> 文件中配置对应的 APIKEY,并重新启动服务</p>
|
||||||
<div class="model-provider-card">
|
<div class="model-provider-card">
|
||||||
<div class="card-header">
|
<div class="card-header">
|
||||||
<h3>自定义模型</h3>
|
<h3>自定义模型</h3>
|
||||||
@ -167,9 +167,9 @@
|
|||||||
<InfoCircleOutlined />
|
<InfoCircleOutlined />
|
||||||
</a>
|
</a>
|
||||||
</div>
|
</div>
|
||||||
<a-button
|
<a-button
|
||||||
type="text"
|
type="text"
|
||||||
class="expand-button"
|
class="expand-button"
|
||||||
@click.stop="toggleExpand(item)"
|
@click.stop="toggleExpand(item)"
|
||||||
>
|
>
|
||||||
<span class="icon-wrapper" :class="{'rotated': expandedModels[item]}">
|
<span class="icon-wrapper" :class="{'rotated': expandedModels[item]}">
|
||||||
@ -550,7 +550,7 @@ const sendRestart = () => {
|
|||||||
.model-icon {
|
.model-icon {
|
||||||
width: 28px;
|
width: 28px;
|
||||||
height: 28px;
|
height: 28px;
|
||||||
|
|
||||||
// 灰度
|
// 灰度
|
||||||
filter: grayscale(100%);
|
filter: grayscale(100%);
|
||||||
img {
|
img {
|
||||||
@ -627,7 +627,7 @@ const sendRestart = () => {
|
|||||||
.icon-wrapper {
|
.icon-wrapper {
|
||||||
display: inline-flex;
|
display: inline-flex;
|
||||||
transition: transform 0.2s ease;
|
transition: transform 0.2s ease;
|
||||||
|
|
||||||
&.rotated {
|
&.rotated {
|
||||||
transform: rotate(180deg);
|
transform: rotate(180deg);
|
||||||
}
|
}
|
||||||
@ -639,7 +639,7 @@ const sendRestart = () => {
|
|||||||
max-height: 0;
|
max-height: 0;
|
||||||
overflow: hidden;
|
overflow: hidden;
|
||||||
transition: max-height 0.2s ease-out; // 先快后慢
|
transition: max-height 0.2s ease-out; // 先快后慢
|
||||||
|
|
||||||
&.expanded {
|
&.expanded {
|
||||||
max-height: 700px; /* 设置一个足够大的值 */
|
max-height: 700px; /* 设置一个足够大的值 */
|
||||||
}
|
}
|
||||||
@ -710,7 +710,7 @@ const sendRestart = () => {
|
|||||||
flex: 1;
|
flex: 1;
|
||||||
margin-right: 8px;
|
margin-right: 8px;
|
||||||
position: relative;
|
position: relative;
|
||||||
|
|
||||||
&:hover::after {
|
&:hover::after {
|
||||||
content: attr(title);
|
content: attr(title);
|
||||||
position: absolute;
|
position: absolute;
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user