refactor: 增强消息来源处理,合并相关组件并优化状态管理
This commit is contained in:
parent
9cbed254bb
commit
3ac6fee7b8
@ -84,11 +84,8 @@
|
|||||||
<RefsComponent
|
<RefsComponent
|
||||||
v-if="shouldShowRefs(conv)"
|
v-if="shouldShowRefs(conv)"
|
||||||
:message="getLastMessage(conv)"
|
:message="getLastMessage(conv)"
|
||||||
:show-refs="['model', 'copy']"
|
:show-refs="['model', 'copy', 'sources']"
|
||||||
:is-latest-message="false"
|
:is-latest-message="false"
|
||||||
/>
|
|
||||||
<ConversationSourcesPanel
|
|
||||||
v-if="shouldShowRefs(conv)"
|
|
||||||
:sources="getConversationSources(conv)"
|
:sources="getConversationSources(conv)"
|
||||||
/>
|
/>
|
||||||
</div>
|
</div>
|
||||||
@ -219,7 +216,6 @@ import HumanApprovalModal from '@/components/HumanApprovalModal.vue'
|
|||||||
import { useApproval } from '@/composables/useApproval'
|
import { useApproval } from '@/composables/useApproval'
|
||||||
import { useAgentStreamHandler } from '@/composables/useAgentStreamHandler'
|
import { useAgentStreamHandler } from '@/composables/useAgentStreamHandler'
|
||||||
import AgentPanel from '@/components/AgentPanel.vue'
|
import AgentPanel from '@/components/AgentPanel.vue'
|
||||||
import ConversationSourcesPanel from '@/components/ConversationSourcesPanel.vue'
|
|
||||||
|
|
||||||
// ==================== PROPS & EMITS ====================
|
// ==================== PROPS & EMITS ====================
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -237,7 +233,9 @@ const {
|
|||||||
defaultAgentId,
|
defaultAgentId,
|
||||||
selectedAgentConfigId,
|
selectedAgentConfigId,
|
||||||
agentConfig,
|
agentConfig,
|
||||||
configurableItems
|
configurableItems,
|
||||||
|
availableKnowledgeBases,
|
||||||
|
availableMcps
|
||||||
} = storeToRefs(agentStore)
|
} = storeToRefs(agentStore)
|
||||||
|
|
||||||
// ==================== LOCAL CHAT & UI STATE ====================
|
// ==================== LOCAL CHAT & UI STATE ====================
|
||||||
@ -294,10 +292,6 @@ const localUIState = reactive({
|
|||||||
isInitialRender: true
|
isInitialRender: true
|
||||||
})
|
})
|
||||||
|
|
||||||
// Mention resources
|
|
||||||
const availableKnowledgeBases = ref([])
|
|
||||||
const availableMcps = ref([])
|
|
||||||
|
|
||||||
// Agent Panel State
|
// Agent Panel State
|
||||||
const isAgentPanelOpen = ref(false)
|
const isAgentPanelOpen = ref(false)
|
||||||
const isResizing = ref(false)
|
const isResizing = ref(false)
|
||||||
@ -1135,19 +1129,6 @@ const resumeActiveRunForThread = async (threadId) => {
|
|||||||
clearActiveRunSnapshot(threadId)
|
clearActiveRunSnapshot(threadId)
|
||||||
}
|
}
|
||||||
|
|
||||||
const fetchMentionResources = async () => {
|
|
||||||
try {
|
|
||||||
const [dbsRes, mcpsRes] = await Promise.all([
|
|
||||||
databaseApi.getAccessibleDatabases().catch(() => ({ databases: [] })),
|
|
||||||
mcpApi.getMcpServers().catch(() => ({ data: [] }))
|
|
||||||
])
|
|
||||||
availableKnowledgeBases.value = dbsRes.databases || []
|
|
||||||
availableMcps.value = mcpsRes.data || []
|
|
||||||
} catch (e) {
|
|
||||||
console.warn('Failed to fetch mention resources', e)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const ensureActiveThread = async (title = '新的对话') => {
|
const ensureActiveThread = async (title = '新的对话') => {
|
||||||
if (currentChatId.value) return currentChatId.value
|
if (currentChatId.value) return currentChatId.value
|
||||||
try {
|
try {
|
||||||
@ -1648,7 +1629,7 @@ const showMsgRefs = (msg) => {
|
|||||||
|
|
||||||
// 只有真正完成的消息才显示 refs
|
// 只有真正完成的消息才显示 refs
|
||||||
if (msg.isLast && msg.status === 'finished') {
|
if (msg.isLast && msg.status === 'finished') {
|
||||||
return ['copy']
|
return ['copy', 'sources']
|
||||||
}
|
}
|
||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
@ -1693,7 +1674,6 @@ const initAll = async () => {
|
|||||||
if (!agentStore.isInitialized) {
|
if (!agentStore.isInitialized) {
|
||||||
await agentStore.initialize()
|
await agentStore.initialize()
|
||||||
}
|
}
|
||||||
await fetchMentionResources()
|
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
handleChatError(error, 'load')
|
handleChatError(error, 'load')
|
||||||
}
|
}
|
||||||
|
|||||||
@ -91,6 +91,7 @@
|
|||||||
:message="message"
|
:message="message"
|
||||||
:show-refs="showRefs"
|
:show-refs="showRefs"
|
||||||
:is-latest-message="isLatestMessage"
|
:is-latest-message="isLatestMessage"
|
||||||
|
:sources="messageSources"
|
||||||
@retry="emit('retry')"
|
@retry="emit('retry')"
|
||||||
@openRefs="emit('openRefs', $event)"
|
@openRefs="emit('openRefs', $event)"
|
||||||
/>
|
/>
|
||||||
@ -115,6 +116,7 @@ import { useAgentStore } from '@/stores/agent'
|
|||||||
import { useInfoStore } from '@/stores/info'
|
import { useInfoStore } from '@/stores/info'
|
||||||
import { useThemeStore } from '@/stores/theme'
|
import { useThemeStore } from '@/stores/theme'
|
||||||
import { storeToRefs } from 'pinia'
|
import { storeToRefs } from 'pinia'
|
||||||
|
import { MessageProcessor } from '@/utils/messageProcessor'
|
||||||
|
|
||||||
import { MdPreview } from 'md-editor-v3'
|
import { MdPreview } from 'md-editor-v3'
|
||||||
import 'md-editor-v3/lib/preview.css'
|
import 'md-editor-v3/lib/preview.css'
|
||||||
@ -223,9 +225,18 @@ const getErrorMessage = computed(() => {
|
|||||||
|
|
||||||
// 引入智能体 store
|
// 引入智能体 store
|
||||||
const agentStore = useAgentStore()
|
const agentStore = useAgentStore()
|
||||||
|
const { availableKnowledgeBases } = storeToRefs(agentStore)
|
||||||
const infoStore = useInfoStore()
|
const infoStore = useInfoStore()
|
||||||
const themeStore = useThemeStore()
|
const themeStore = useThemeStore()
|
||||||
|
|
||||||
|
// 提取消息来源
|
||||||
|
const messageSources = computed(() => {
|
||||||
|
if (props.message.type === 'ai') {
|
||||||
|
return MessageProcessor.extractSourcesFromMessage(props.message, availableKnowledgeBases.value)
|
||||||
|
}
|
||||||
|
return { knowledgeChunks: [], webSources: [] }
|
||||||
|
})
|
||||||
|
|
||||||
// 主题设置 - 根据系统主题动态切换
|
// 主题设置 - 根据系统主题动态切换
|
||||||
const theme = computed(() => (themeStore.isDark ? 'dark' : 'light'))
|
const theme = computed(() => (themeStore.isDark ? 'dark' : 'light'))
|
||||||
|
|
||||||
|
|||||||
@ -1,120 +0,0 @@
|
|||||||
<template>
|
|
||||||
<div v-if="hasAny" class="conversation-sources-panel">
|
|
||||||
<button class="panel-header" type="button" @click="toggleExpanded">
|
|
||||||
<div class="header-left">
|
|
||||||
<div class="title">来源</div>
|
|
||||||
<div class="summary">
|
|
||||||
<span v-if="knowledgeChunks.length > 0">知识库 {{ knowledgeChunks.length }}</span>
|
|
||||||
<span v-if="webSources.length > 0">网络 {{ webSources.length }}</span>
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
<ChevronDown :size="14" class="expand-icon" :class="{ rotated: !expanded }" />
|
|
||||||
</button>
|
|
||||||
|
|
||||||
<div v-if="expanded" class="panel-body">
|
|
||||||
<KnowledgeSourceSection v-if="knowledgeChunks.length > 0" :chunks="knowledgeChunks" />
|
|
||||||
<WebSearchSourceSection v-if="webSources.length > 0" :sources="webSources" />
|
|
||||||
</div>
|
|
||||||
</div>
|
|
||||||
</template>
|
|
||||||
|
|
||||||
<script setup>
|
|
||||||
import { computed, ref } from 'vue'
|
|
||||||
import { ChevronDown } from 'lucide-vue-next'
|
|
||||||
import KnowledgeSourceSection from '@/components/KnowledgeSourceSection.vue'
|
|
||||||
import WebSearchSourceSection from '@/components/WebSearchSourceSection.vue'
|
|
||||||
|
|
||||||
const props = defineProps({
|
|
||||||
sources: {
|
|
||||||
type: Object,
|
|
||||||
default: () => ({})
|
|
||||||
},
|
|
||||||
knowledgeChunks: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
},
|
|
||||||
webSources: {
|
|
||||||
type: Array,
|
|
||||||
default: () => []
|
|
||||||
}
|
|
||||||
})
|
|
||||||
|
|
||||||
const expanded = ref(false)
|
|
||||||
const knowledgeChunks = computed(() =>
|
|
||||||
Array.isArray(props.sources?.knowledgeChunks) ? props.sources.knowledgeChunks : props.knowledgeChunks
|
|
||||||
)
|
|
||||||
const webSources = computed(() =>
|
|
||||||
Array.isArray(props.sources?.webSources) ? props.sources.webSources : props.webSources
|
|
||||||
)
|
|
||||||
|
|
||||||
const hasAny = computed(
|
|
||||||
() =>
|
|
||||||
(Array.isArray(knowledgeChunks.value) && knowledgeChunks.value.length > 0) ||
|
|
||||||
(Array.isArray(webSources.value) && webSources.value.length > 0)
|
|
||||||
)
|
|
||||||
|
|
||||||
const toggleExpanded = () => {
|
|
||||||
expanded.value = !expanded.value
|
|
||||||
}
|
|
||||||
</script>
|
|
||||||
|
|
||||||
<style scoped lang="less">
|
|
||||||
.conversation-sources-panel {
|
|
||||||
margin: 8px 0 14px 0;
|
|
||||||
border: 1px solid var(--gray-200);
|
|
||||||
border-radius: 8px;
|
|
||||||
background: var(--gray-25);
|
|
||||||
overflow: hidden;
|
|
||||||
|
|
||||||
.panel-header {
|
|
||||||
width: 100%;
|
|
||||||
border: none;
|
|
||||||
background: transparent;
|
|
||||||
display: flex;
|
|
||||||
align-items: center;
|
|
||||||
justify-content: space-between;
|
|
||||||
padding: 10px 12px;
|
|
||||||
cursor: pointer;
|
|
||||||
|
|
||||||
.header-left {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 10px;
|
|
||||||
min-width: 0;
|
|
||||||
|
|
||||||
.title {
|
|
||||||
font-size: 13px;
|
|
||||||
font-weight: 600;
|
|
||||||
color: var(--gray-700);
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
|
|
||||||
.summary {
|
|
||||||
display: flex;
|
|
||||||
align-items: baseline;
|
|
||||||
gap: 8px;
|
|
||||||
font-size: 12px;
|
|
||||||
color: var(--gray-600);
|
|
||||||
line-height: 20px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.expand-icon {
|
|
||||||
color: var(--gray-600);
|
|
||||||
transition: transform 0.2s ease;
|
|
||||||
flex-shrink: 0;
|
|
||||||
|
|
||||||
&.rotated {
|
|
||||||
transform: rotate(-90deg);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
.panel-body {
|
|
||||||
display: flex;
|
|
||||||
flex-direction: column;
|
|
||||||
gap: 10px;
|
|
||||||
padding: 0 10px 10px;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
</style>
|
|
||||||
@ -41,6 +41,31 @@
|
|||||||
title="重新生成"
|
title="重新生成"
|
||||||
><RotateCcw size="12" />
|
><RotateCcw size="12" />
|
||||||
</span>
|
</span>
|
||||||
|
|
||||||
|
<!-- 来源按钮 - 使用 flex-grow 占据剩余空间并右对齐 -->
|
||||||
|
<div v-if="hasSources && showKey('sources')" class="sources-spacer"></div>
|
||||||
|
<span
|
||||||
|
v-if="hasSources && showKey('sources')"
|
||||||
|
class="item btn sources-btn"
|
||||||
|
:class="{ expanded: isSourcesExpanded }"
|
||||||
|
@click="toggleSources"
|
||||||
|
:title="isSourcesExpanded ? '收起详情' : '查看来源详情'"
|
||||||
|
>
|
||||||
|
<BookOpen size="12" />
|
||||||
|
<span class="sources-label">
|
||||||
|
来源
|
||||||
|
<template v-if="sourceCount > 0">
|
||||||
|
{{ sourceCount }}
|
||||||
|
</template>
|
||||||
|
</span>
|
||||||
|
<ChevronDown :size="12" class="expand-icon" :class="{ rotated: isSourcesExpanded }" />
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
|
||||||
|
<!-- 来源详情面板 -->
|
||||||
|
<div v-if="isSourcesExpanded" class="sources-panel-body">
|
||||||
|
<KnowledgeSourceSection v-if="knowledgeChunks.length > 0" :chunks="knowledgeChunks" />
|
||||||
|
<WebSearchSourceSection v-if="webSources.length > 0" :sources="webSources" />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
@ -68,8 +93,10 @@
|
|||||||
import { ref, computed, reactive, watch } from 'vue'
|
import { ref, computed, reactive, watch } from 'vue'
|
||||||
import { useClipboard } from '@vueuse/core'
|
import { useClipboard } from '@vueuse/core'
|
||||||
import { message } from 'ant-design-vue'
|
import { message } from 'ant-design-vue'
|
||||||
import { ThumbsUp, ThumbsDown, Bot, Copy, Check, RotateCcw } from 'lucide-vue-next'
|
import { ThumbsUp, ThumbsDown, Bot, Copy, Check, RotateCcw, BookOpen, ChevronDown } from 'lucide-vue-next'
|
||||||
import { agentApi } from '@/apis'
|
import { agentApi } from '@/apis'
|
||||||
|
import KnowledgeSourceSection from '@/components/KnowledgeSourceSection.vue'
|
||||||
|
import WebSearchSourceSection from '@/components/WebSearchSourceSection.vue'
|
||||||
|
|
||||||
const emit = defineEmits(['retry', 'openRefs'])
|
const emit = defineEmits(['retry', 'openRefs'])
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
@ -81,11 +108,39 @@ const props = defineProps({
|
|||||||
isLatestMessage: {
|
isLatestMessage: {
|
||||||
type: Boolean,
|
type: Boolean,
|
||||||
default: false
|
default: false
|
||||||
|
},
|
||||||
|
sources: {
|
||||||
|
type: Object,
|
||||||
|
default: () => ({})
|
||||||
}
|
}
|
||||||
})
|
})
|
||||||
|
|
||||||
const msg = ref(props.message)
|
const msg = ref(props.message)
|
||||||
|
|
||||||
|
// Sources state
|
||||||
|
const isSourcesExpanded = ref(false)
|
||||||
|
|
||||||
|
const knowledgeChunks = computed(() =>
|
||||||
|
Array.isArray(props.sources?.knowledgeChunks) ? props.sources.knowledgeChunks : []
|
||||||
|
)
|
||||||
|
const webSources = computed(() =>
|
||||||
|
Array.isArray(props.sources?.webSources) ? props.sources.webSources : []
|
||||||
|
)
|
||||||
|
|
||||||
|
const hasSources = computed(
|
||||||
|
() =>
|
||||||
|
knowledgeChunks.value.length > 0 ||
|
||||||
|
webSources.value.length > 0
|
||||||
|
)
|
||||||
|
|
||||||
|
const sourceCount = computed(
|
||||||
|
() => knowledgeChunks.value.length + webSources.value.length
|
||||||
|
)
|
||||||
|
|
||||||
|
const toggleSources = () => {
|
||||||
|
isSourcesExpanded.value = !isSourcesExpanded.value
|
||||||
|
}
|
||||||
|
|
||||||
// Feedback state
|
// Feedback state
|
||||||
const feedbackState = reactive({
|
const feedbackState = reactive({
|
||||||
hasSubmitted: false,
|
hasSubmitted: false,
|
||||||
@ -272,11 +327,12 @@ const cancelDislike = () => {
|
|||||||
<style lang="less" scoped>
|
<style lang="less" scoped>
|
||||||
.refs {
|
.refs {
|
||||||
display: flex;
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
margin-bottom: 20px;
|
margin-bottom: 20px;
|
||||||
margin-top: 10px;
|
margin-top: 10px;
|
||||||
color: var(--gray-500);
|
color: var(--gray-500);
|
||||||
font-size: 13px;
|
font-size: 13px;
|
||||||
gap: 10px;
|
gap: 12px;
|
||||||
|
|
||||||
.item {
|
.item {
|
||||||
background: var(--gray-50);
|
background: var(--gray-50);
|
||||||
@ -313,7 +369,66 @@ const cancelDislike = () => {
|
|||||||
.tags {
|
.tags {
|
||||||
display: flex;
|
display: flex;
|
||||||
flex-wrap: wrap;
|
flex-wrap: wrap;
|
||||||
|
align-items: center;
|
||||||
gap: 10px;
|
gap: 10px;
|
||||||
|
width: 100%;
|
||||||
|
|
||||||
|
.sources-spacer {
|
||||||
|
flex-grow: 1;
|
||||||
|
}
|
||||||
|
|
||||||
|
.sources-btn {
|
||||||
|
margin-left: auto;
|
||||||
|
background: var(--gray-50);
|
||||||
|
border: 1px solid transparent;
|
||||||
|
padding: 6px 10px;
|
||||||
|
|
||||||
|
&:hover {
|
||||||
|
background: var(--gray-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
&.expanded {
|
||||||
|
background: var(--main-50);
|
||||||
|
color: var(--main-700);
|
||||||
|
border-color: var(--main-100);
|
||||||
|
}
|
||||||
|
|
||||||
|
.sources-label {
|
||||||
|
font-weight: 500;
|
||||||
|
margin-left: 2px;
|
||||||
|
}
|
||||||
|
|
||||||
|
.expand-icon {
|
||||||
|
margin-left: 4px;
|
||||||
|
transition: transform 0.2s ease;
|
||||||
|
|
||||||
|
&.rotated {
|
||||||
|
transform: rotate(180deg);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
.sources-panel-body {
|
||||||
|
background: var(--gray-25);
|
||||||
|
border: 1px solid var(--gray-150);
|
||||||
|
border-radius: 8px;
|
||||||
|
padding: 12px;
|
||||||
|
display: flex;
|
||||||
|
flex-direction: column;
|
||||||
|
gap: 12px;
|
||||||
|
animation: slideDown 0.2s ease-out;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@keyframes slideDown {
|
||||||
|
from {
|
||||||
|
opacity: 0;
|
||||||
|
transform: translateY(-8px);
|
||||||
|
}
|
||||||
|
to {
|
||||||
|
opacity: 1;
|
||||||
|
transform: translateY(0);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
</style>
|
</style>
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
import { defineStore } from 'pinia'
|
import { defineStore } from 'pinia'
|
||||||
import { ref, computed } from 'vue'
|
import { ref, computed } from 'vue'
|
||||||
import { agentApi } from '@/apis/agent_api'
|
import { agentApi, databaseApi, mcpApi } from '@/apis'
|
||||||
import { handleChatError } from '@/utils/errorHandler'
|
import { handleChatError } from '@/utils/errorHandler'
|
||||||
import { useUserStore } from '@/stores/user'
|
import { useUserStore } from '@/stores/user'
|
||||||
|
|
||||||
@ -14,6 +14,10 @@ export const useAgentStore = defineStore(
|
|||||||
const selectedAgentId = ref(null)
|
const selectedAgentId = ref(null)
|
||||||
const defaultAgentId = ref(null)
|
const defaultAgentId = ref(null)
|
||||||
|
|
||||||
|
// 资源相关状态
|
||||||
|
const availableKnowledgeBases = ref([])
|
||||||
|
const availableMcps = ref([])
|
||||||
|
|
||||||
// 智能体配置相关状态
|
// 智能体配置相关状态
|
||||||
const agentConfig = ref({})
|
const agentConfig = ref({})
|
||||||
const originalAgentConfig = ref({})
|
const originalAgentConfig = ref({})
|
||||||
@ -91,6 +95,22 @@ export const useAgentStore = defineStore(
|
|||||||
})
|
})
|
||||||
|
|
||||||
// ==================== 方法 ====================
|
// ==================== 方法 ====================
|
||||||
|
/**
|
||||||
|
* 获取可提及的资源(知识库、MCP等)
|
||||||
|
*/
|
||||||
|
async function fetchMentionResources() {
|
||||||
|
try {
|
||||||
|
const [dbsRes, mcpsRes] = await Promise.all([
|
||||||
|
databaseApi.getAccessibleDatabases().catch(() => ({ databases: [] })),
|
||||||
|
mcpApi.getMcpServers().catch(() => ({ data: [] }))
|
||||||
|
])
|
||||||
|
availableKnowledgeBases.value = dbsRes.databases || []
|
||||||
|
availableMcps.value = mcpsRes.data || []
|
||||||
|
} catch (e) {
|
||||||
|
console.warn('Failed to fetch mention resources:', e)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 初始化 store
|
* 初始化 store
|
||||||
*/
|
*/
|
||||||
@ -104,6 +124,7 @@ export const useAgentStore = defineStore(
|
|||||||
try {
|
try {
|
||||||
await fetchAgents()
|
await fetchAgents()
|
||||||
await fetchDefaultAgent()
|
await fetchDefaultAgent()
|
||||||
|
await fetchMentionResources()
|
||||||
|
|
||||||
if (!selectedAgent.value) {
|
if (!selectedAgent.value) {
|
||||||
if (defaultAgent.value) {
|
if (defaultAgent.value) {
|
||||||
@ -449,6 +470,8 @@ export const useAgentStore = defineStore(
|
|||||||
agents.value = []
|
agents.value = []
|
||||||
selectedAgentId.value = null
|
selectedAgentId.value = null
|
||||||
defaultAgentId.value = null
|
defaultAgentId.value = null
|
||||||
|
availableKnowledgeBases.value = []
|
||||||
|
availableMcps.value = []
|
||||||
agentConfig.value = {}
|
agentConfig.value = {}
|
||||||
originalAgentConfig.value = {}
|
originalAgentConfig.value = {}
|
||||||
agentConfigs.value = {}
|
agentConfigs.value = {}
|
||||||
@ -468,6 +491,8 @@ export const useAgentStore = defineStore(
|
|||||||
agents,
|
agents,
|
||||||
selectedAgentId,
|
selectedAgentId,
|
||||||
defaultAgentId,
|
defaultAgentId,
|
||||||
|
availableKnowledgeBases,
|
||||||
|
availableMcps,
|
||||||
agentConfig,
|
agentConfig,
|
||||||
originalAgentConfig,
|
originalAgentConfig,
|
||||||
agentDetails,
|
agentDetails,
|
||||||
@ -495,6 +520,7 @@ export const useAgentStore = defineStore(
|
|||||||
fetchAgents,
|
fetchAgents,
|
||||||
fetchAgentDetail,
|
fetchAgentDetail,
|
||||||
fetchDefaultAgent,
|
fetchDefaultAgent,
|
||||||
|
fetchMentionResources,
|
||||||
setDefaultAgent,
|
setDefaultAgent,
|
||||||
selectAgent,
|
selectAgent,
|
||||||
selectAgentConfig,
|
selectAgentConfig,
|
||||||
|
|||||||
@ -249,6 +249,23 @@ export class MessageProcessor {
|
|||||||
return webSources
|
return webSources
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 提取单个消息中的来源
|
||||||
|
* @param {Object} message - 消息对象
|
||||||
|
* @param {Array} databases - 知识库列表
|
||||||
|
* @returns {{knowledgeChunks: Array, webSources: Array}}
|
||||||
|
*/
|
||||||
|
static extractSourcesFromMessage(message, databases = []) {
|
||||||
|
if (!message || message.type !== 'ai') return { knowledgeChunks: [], webSources: [] }
|
||||||
|
|
||||||
|
// 复用提取逻辑,通过构建临时对话对象
|
||||||
|
const mockConv = { messages: [message] }
|
||||||
|
return {
|
||||||
|
knowledgeChunks: MessageProcessor.extractKnowledgeChunksFromConversation(mockConv, databases),
|
||||||
|
webSources: MessageProcessor.extractWebSourcesFromConversation(mockConv),
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* 提取一轮对话中的全部来源(知识库+网络搜索)
|
* 提取一轮对话中的全部来源(知识库+网络搜索)
|
||||||
* @param {Object} conv - 单轮对话
|
* @param {Object} conv - 单轮对话
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user