Commit 59e9ce00 authored by nick zheng's avatar nick zheng

feat: 数据库配置&&应用

parent 7643dadc
...@@ -23,6 +23,7 @@ export default [ ...@@ -23,6 +23,7 @@ export default [
ViteEnv: 'readonly', ViteEnv: 'readonly',
AnyObject: 'readonly', AnyObject: 'readonly',
KnowledgeContentResultItem: 'readonly', KnowledgeContentResultItem: 'readonly',
DBChainResultItem: 'readonly',
ConversationMessageItem: 'readonly', ConversationMessageItem: 'readonly',
ConversationMessageItemInfo: 'readonly', ConversationMessageItemInfo: 'readonly',
MittEvents: 'readonly', MittEvents: 'readonly',
......
...@@ -58,3 +58,11 @@ export function fetchGetDataBaseDetail<T>(id: string) { ...@@ -58,3 +58,11 @@ export function fetchGetDataBaseDetail<T>(id: string) {
export function fetchGetDBTableList<T>(id: string) { export function fetchGetDBTableList<T>(id: string) {
return request.post<T>(`/databaseRest/getTableInfo.json?id=${id}`) return request.post<T>(`/databaseRest/getTableInfo.json?id=${id}`)
} }
/**
* @query ids 数据库Id列表
* @returns 通过Id列表获取数据库详情
*/
export function fetchGetDataListByIds<T>(ids: number[]) {
return request.post<T>(`/databaseRest/getByIds.json?ids=${ids}`)
}
...@@ -10,6 +10,7 @@ interface ResponseData { ...@@ -10,6 +10,7 @@ interface ResponseData {
reasoningContent: string reasoningContent: string
function: { name: string } function: { name: string }
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainResult: DBChainResultItem[]
} }
const { t } = i18n.global const { t } = i18n.global
......
...@@ -155,6 +155,12 @@ common_module: ...@@ -155,6 +155,12 @@ common_module:
cancel_authorization: 'Cancel authorization' cancel_authorization: 'Cancel authorization'
get_code: 'Get Code' get_code: 'Get Code'
database: 'Database' database: 'Database'
no_database_table: 'No database table'
view_database_table: 'View database table'
total_database_table: '1 database table in total | {count} database tables in total'
add_database_successfully: 'Database set {0} was added successfully'
remove_database_successfully: 'Database set {0} was removed successfully'
database_QA_executed_successfully: 'Database Q&A executed successfully'
dialogue_module: dialogue_module:
continue_question_message: 'You can keep asking questions' continue_question_message: 'You can keep asking questions'
...@@ -377,6 +383,8 @@ personal_space_module: ...@@ -377,6 +383,8 @@ personal_space_module:
back_chunk_content: 'Back chunk content' back_chunk_content: 'Back chunk content'
from_knowledge_base: 'From knowledge base' from_knowledge_base: 'From knowledge base'
score: 'Score' score: 'Score'
only_one_database_can_be_added: 'Only one database can be added'
associated_database_desc: 'You can upload local spreadsheet data or connect to a business database to build your dataset. When users ask numerical questions, the application can query, compute, and analyze the data to provide answers. Note: The app supports association with only 1 database at most'
upload_file: 'Upload file' upload_file: 'Upload file'
upload_file_desc: 'Enable the user to upload files for chat, support TXT, MD, PDF, DOC, DOCX format files' upload_file_desc: 'Enable the user to upload files for chat, support TXT, MD, PDF, DOC, DOCX format files'
dialogue: 'Dialogue' dialogue: 'Dialogue'
...@@ -646,6 +654,7 @@ personal_space_module: ...@@ -646,6 +654,7 @@ personal_space_module:
connect_test_fail: 'Database connection failed. Please verify your input' connect_test_fail: 'Database connection failed. Please verify your input'
connect_test_must_pass_to_create: 'Connect test must pass to create' connect_test_must_pass_to_create: 'Connect test must pass to create'
connect_test_must_pass_to_update: 'Connect test must pass to update' connect_test_must_pass_to_update: 'Connect test must pass to update'
add_database: 'Add database'
share_agent_module: share_agent_module:
please: 'Please first' please: 'Please first'
......
...@@ -154,6 +154,12 @@ common_module: ...@@ -154,6 +154,12 @@ common_module:
cancel_authorization: '取消授权' cancel_authorization: '取消授权'
get_code: '获取验证码' get_code: '获取验证码'
database: '数据库' database: '数据库'
no_database_table: '暂无数据库表'
view_database_table: '查看数据库表'
total_database_table: '共{count}个数据库表'
add_database_successfully: '数据库 {0} 添加成功'
remove_database_successfully: '数据库 {0} 移除成功'
database_QA_executed_successfully: '数据库问答执行成功'
dialogue_module: dialogue_module:
continue_question_message: '你可以继续提问' continue_question_message: '你可以继续提问'
...@@ -375,6 +381,8 @@ personal_space_module: ...@@ -375,6 +381,8 @@ personal_space_module:
back_chunk_content: '返回切片内容' back_chunk_content: '返回切片内容'
from_knowledge_base: '所属知识库' from_knowledge_base: '所属知识库'
score: '匹配分' score: '匹配分'
only_one_database_can_be_added: '仅支持添加一个数据库'
associated_database_desc: '可上传本地表格数据或连接业务数据库构建数据库。用户询问数值类问题时,应用能够查询、计算和分析数据并答复。应用最多可关联1个数据库'
upload_file: '上传文件' upload_file: '上传文件'
upload_file_desc: '开启后支持用户上传文件进行对话聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件' upload_file_desc: '开启后支持用户上传文件进行对话聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件'
dialogue: '对话' dialogue: '对话'
...@@ -644,6 +652,7 @@ personal_space_module: ...@@ -644,6 +652,7 @@ personal_space_module:
connect_test_fail: '无法联通数据库,请检查填写内容' connect_test_fail: '无法联通数据库,请检查填写内容'
connect_test_must_pass_to_create: '测试通过才能创建' connect_test_must_pass_to_create: '测试通过才能创建'
connect_test_must_pass_to_update: '测试通过才能更新' connect_test_must_pass_to_update: '测试通过才能更新'
add_database: '添加数据库'
share_agent_module: share_agent_module:
please: '请先' please: '请先'
......
...@@ -154,6 +154,12 @@ common_module: ...@@ -154,6 +154,12 @@ common_module:
cancel_authorization: '取消授權' cancel_authorization: '取消授權'
get_code: '獲取驗証碼' get_code: '獲取驗証碼'
database: '數據庫' database: '數據庫'
no_database_table: '暫無數據庫表'
view_database_table: '查看數據庫表'
total_database_table: '共{count}個數據庫表'
add_database_successfully: '數據庫 {0} 添加成功'
remove_database_successfully: '數據庫 {0} 移除成功'
database_QA_executed_successfully: '數據庫問答執行成功'
dialogue_module: dialogue_module:
continue_question_message: '你可以繼續提問' continue_question_message: '你可以繼續提問'
...@@ -375,6 +381,8 @@ personal_space_module: ...@@ -375,6 +381,8 @@ personal_space_module:
back_chunk_content: '返回切片內容' back_chunk_content: '返回切片內容'
from_knowledge_base: '所屬知識庫' from_knowledge_base: '所屬知識庫'
score: '匹配分' score: '匹配分'
only_one_database_can_be_added: '僅支持添加一個數據庫'
associated_database_desc: '可上傳本地表格數據或連接業務數據庫構建數據庫。用户詢問數值類問題時,應用能夠查詢、計算和分析數據並答覆。應用最多可關聯1個數據庫'
upload_file: '上傳文件' upload_file: '上傳文件'
upload_file_desc: '開啓後支持用户上傳文件進行對話聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件' upload_file_desc: '開啓後支持用户上傳文件進行對話聊天, 支持TXT、MD、PDF、DOC、DOCX格式的文件'
dialogue: '對話' dialogue: '對話'
...@@ -644,6 +652,7 @@ personal_space_module: ...@@ -644,6 +652,7 @@ personal_space_module:
connect_test_fail: '無法聯通數據庫,請檢查填寫內容' connect_test_fail: '無法聯通數據庫,請檢查填寫內容'
connect_test_must_pass_to_create: '測試通過才能創建' connect_test_must_pass_to_create: '測試通過才能創建'
connect_test_must_pass_to_update: '測試通過才能更新' connect_test_must_pass_to_update: '測試通過才能更新'
add_database: '添加數據庫'
share_agent_module: share_agent_module:
please: '請先' please: '請先'
......
...@@ -34,6 +34,9 @@ export function defaultPersonalAppConfigState(): PersonalAppConfigState { ...@@ -34,6 +34,9 @@ export function defaultPersonalAppConfigState(): PersonalAppConfigState {
knowledgeSimilarity: 0.4, knowledgeSimilarity: 0.4,
knowledgeNResult: 3, knowledgeNResult: 3,
}, },
databaseConfig: {
ids: [],
},
commModelConfig: { commModelConfig: {
largeModel: '文心4.0 (8K)', largeModel: '文心4.0 (8K)',
topP: 0.7, topP: 0.7,
......
...@@ -35,6 +35,9 @@ export interface PersonalAppConfigState { ...@@ -35,6 +35,9 @@ export interface PersonalAppConfigState {
knowledgeSimilarity: number //最低相似度 0.01-0.99 knowledgeSimilarity: number //最低相似度 0.01-0.99
knowledgeNResult: number //知识库返回结果数 1-10 knowledgeNResult: number //知识库返回结果数 1-10
} }
databaseConfig: {
ids: number[]
}
commModelConfig: { commModelConfig: {
largeModel: string //大模型 largeModel: string //大模型
topP: number //topP 0-1.00 topP: number //topP 0-1.00
......
...@@ -101,6 +101,7 @@ function messageItemFactory() { ...@@ -101,6 +101,7 @@ function messageItemFactory() {
imageUrl: '', imageUrl: '',
reasoningContent: '', reasoningContent: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} as MessageItemInterface } as MessageItemInterface
} }
...@@ -196,7 +197,19 @@ function handleQuestionSubmit() { ...@@ -196,7 +197,19 @@ function handleQuestionSubmit() {
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateMessageItem', answerMessageId, { pluginName: data.function.name }, modelIndex) emit('updateMessageItem', answerMessageId, { pluginName: data.function.name }, modelIndex)
emit('messageListScrollToBottom') emit('messageListScrollToBottom')
return }
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit(
'updateMessageItem',
answerMessageId,
{
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
},
modelIndex,
)
emit('messageListScrollToBottom')
} }
// 知识库 // 知识库
......
...@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n' ...@@ -4,6 +4,7 @@ import { useI18n } from 'vue-i18n'
import { CheckOne, Down } from '@icon-park/vue-next' import { CheckOne, Down } from '@icon-park/vue-next'
import type { MessageItemInterface } from '../types' import type { MessageItemInterface } from '../types'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue' import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import ExecuteCodeRender from '@/components/execute-code-render/execute-code-render.vue'
import MessageBubbleLoading from './message-bubble-loading.vue' import MessageBubbleLoading from './message-bubble-loading.vue'
interface Props { interface Props {
...@@ -122,6 +123,14 @@ function handleShowReasoningContentSwitch() { ...@@ -122,6 +123,14 @@ function handleShowReasoningContentSwitch() {
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="mb-[10px] w-full">
<ExecuteCodeRender
:title="t('common_module.database_QA_executed_successfully')"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<MarkdownRender <MarkdownRender
:raw-text-content=" :raw-text-content="
messageItem.content ? messageItem.content : t('common_module.dialogue_module.empty_message_content') messageItem.content ? messageItem.content : t('common_module.dialogue_module.empty_message_content')
......
...@@ -37,6 +37,7 @@ export interface MessageItemInterface { ...@@ -37,6 +37,7 @@ export interface MessageItemInterface {
imageUrl?: string imageUrl?: string
reasoningContent: string reasoningContent: string
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainSQLContent: string
} }
export interface LargeModelItem { export interface LargeModelItem {
......
...@@ -10,6 +10,7 @@ interface ResponseData { ...@@ -10,6 +10,7 @@ interface ResponseData {
reasoningContent: string reasoningContent: string
function: { name: string } function: { name: string }
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainResult: DBChainResultItem[]
} }
const { t } = i18n.global const { t } = i18n.global
......
...@@ -151,6 +151,7 @@ function messageItemFactory(): ConversationMessageItem { ...@@ -151,6 +151,7 @@ function messageItemFactory(): ConversationMessageItem {
reasoningContent: '', reasoningContent: '',
modelName: '', modelName: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} }
} }
...@@ -259,13 +260,19 @@ function handleMessageSend() { ...@@ -259,13 +260,19 @@ function handleMessageSend() {
reasoningContent += data.reasoningContent reasoningContent += data.reasoningContent
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent: reasoningContent }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent: reasoningContent })
emit('updatePageScroll') emit('updatePageScroll')
return
} }
// 插件 // 插件
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name })
emit('updatePageScroll') emit('updatePageScroll')
}
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
})
return return
} }
...@@ -274,6 +281,7 @@ function handleMessageSend() { ...@@ -274,6 +281,7 @@ function handleMessageSend() {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
knowledgeContentResult: data.knowledgeContentResult, knowledgeContentResult: data.knowledgeContentResult,
}) })
return
} }
// 回复消息 // 回复消息
......
...@@ -6,6 +6,7 @@ import { CheckOne, Down } from '@icon-park/vue-next' ...@@ -6,6 +6,7 @@ import { CheckOne, Down } from '@icon-park/vue-next'
import CustomLoading from './custom-loading.vue' import CustomLoading from './custom-loading.vue'
import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config' import { usePersonalAppConfigStore } from '@/store/modules/personal-app-config'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue' import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import ExecuteCodeRender from '@/components/execute-code-render/execute-code-render.vue'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
import { asBlob } from 'html-docx-js-typescript' import { asBlob } from 'html-docx-js-typescript'
import { downloadFile } from '@/utils/download-file' import { downloadFile } from '@/utils/download-file'
...@@ -187,6 +188,14 @@ const handleContentCopy = throttle( ...@@ -187,6 +188,14 @@ const handleContentCopy = throttle(
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="mb-[10px] w-full">
<ExecuteCodeRender
:title="t('common_module.database_QA_executed_successfully')"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<p class="break-all"> <p class="break-all">
<MarkdownRender <MarkdownRender
ref="markdownRenderRef" ref="markdownRenderRef"
......
...@@ -35,7 +35,8 @@ const router = useRouter() ...@@ -35,7 +35,8 @@ const router = useRouter()
const personalAppConfigStore = usePersonalAppConfigStore() const personalAppConfigStore = usePersonalAppConfigStore()
const userStore = useUserStore() const userStore = useUserStore()
const { baseInfo, commConfig, commModelConfig, knowledgeConfig, unitIds } = storeToRefs(personalAppConfigStore) const { baseInfo, commConfig, commModelConfig, knowledgeConfig, databaseConfig, unitIds } =
storeToRefs(personalAppConfigStore)
const emitter = inject<Emitter<MittEvents>>('emitter') const emitter = inject<Emitter<MittEvents>>('emitter')
...@@ -614,7 +615,10 @@ async function handleEquityInfoValidate() { ...@@ -614,7 +615,10 @@ async function handleEquityInfoValidate() {
<div class="flex-1 overflow-auto"> <div class="flex-1 overflow-auto">
<AgentPlugin v-model:unit-ids="unitIds" /> <AgentPlugin v-model:unit-ids="unitIds" />
<AgentAssociatedKnowledge v-model:knowledge-config="knowledgeConfig" /> <AgentAssociatedKnowledge
v-model:knowledge-config="knowledgeConfig"
v-model:database-config="databaseConfig"
/>
<AgentMemorySetting <AgentMemorySetting
v-model:variable-structure="commConfig.variableStructure" v-model:variable-structure="commConfig.variableStructure"
......
...@@ -4,20 +4,27 @@ import { useI18n } from 'vue-i18n' ...@@ -4,20 +4,27 @@ import { useI18n } from 'vue-i18n'
import { Plus, RightOne } from '@icon-park/vue-next' import { Plus, RightOne } from '@icon-park/vue-next'
import { fetchGetKnowledgeListByKdIds } from '@/apis/knowledge' import { fetchGetKnowledgeListByKdIds } from '@/apis/knowledge'
import AssociatedKnowledgeModal from './associated-knowledge-modal.vue' import AssociatedKnowledgeModal from './associated-knowledge-modal.vue'
import AssociatedDatabaseModal from './associated-database-modal.vue'
import AgentKnowledgeSettingModal, { KnowledgeConfigType } from './agent-knowledge-setting-modal.vue' import AgentKnowledgeSettingModal, { KnowledgeConfigType } from './agent-knowledge-setting-modal.vue'
import { KnowledgeItem } from '@/views/personal-space/personal-knowledge/types' import { KnowledgeItem } from '@/views/personal-space/personal-knowledge/types'
import { PersonalAppConfigState } from '@/store/types/personal-app-config' import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { KnowledgeTypeIcon } from '@/enums/knowledge' import { KnowledgeTypeIcon } from '@/enums/knowledge'
import { fetchGetDataListByIds } from '@/apis/database'
import { DatabaseItemInterface } from '@/views/personal-space/personal-database/type'
const { t } = useI18n() const { t } = useI18n()
const knowledgeConfig = defineModel<PersonalAppConfigState['knowledgeConfig']>('knowledgeConfig', { required: true }) const knowledgeConfig = defineModel<PersonalAppConfigState['knowledgeConfig']>('knowledgeConfig', { required: true })
const databaseConfig = defineModel<PersonalAppConfigState['databaseConfig']>('databaseConfig', { required: true })
const isShowAssociatedKnowledgeModel = ref(false) const isShowAssociatedKnowledgeModel = ref(false)
const knowledgeConfigExpandedNames = ref<string[]>([]) const knowledgeConfigExpandedNames = ref<string[]>([])
const selectKnowledgeList = ref<KnowledgeItem[]>([]) const selectKnowledgeList = ref<KnowledgeItem[]>([])
const hoverKdId = ref(0) const hoverKdId = ref(0)
const isShowAgentKnowledgeSettingModal = ref(false) const isShowAgentKnowledgeSettingModal = ref(false)
const isShowAssociatedDatabaseModal = ref(false)
const selectDatabaseList = ref<DatabaseItemInterface[]>([])
const hoverDBId = ref(0)
watch( watch(
() => knowledgeConfig.value.knowledgeIds, () => knowledgeConfig.value.knowledgeIds,
...@@ -31,7 +38,18 @@ watch( ...@@ -31,7 +38,18 @@ watch(
knowledgeConfig.value.knowledgeIds.push(knowledgeItem.id) knowledgeConfig.value.knowledgeIds.push(knowledgeItem.id)
}) })
} }
knowledgeConfig.value.knowledgeIds.length && (knowledgeConfigExpandedNames.value = ['knowledge']) knowledgeConfig.value.knowledgeIds.length && knowledgeConfigExpandedNames.value.push('knowledge')
}
},
{ once: true, immediate: true },
)
watch(
() => databaseConfig.value.ids,
async (newDBIds) => {
if (newDBIds?.length > 0) {
await handleGetDatabaseListByIds()
databaseConfig.value.ids?.length && knowledgeConfigExpandedNames.value.push('database')
} }
}, },
{ once: true, immediate: true }, { once: true, immediate: true },
...@@ -41,6 +59,10 @@ const isHoverKnowledgeItem = computed(() => (kdId: number) => { ...@@ -41,6 +59,10 @@ const isHoverKnowledgeItem = computed(() => (kdId: number) => {
return hoverKdId.value === kdId return hoverKdId.value === kdId
}) })
const isHoverDatabaseItem = computed(() => (dbId: number) => {
return hoverDBId.value === dbId
})
const isOpenDocumentParsing = computed(() => knowledgeConfig.value.isDocumentParsing === 'Y') const isOpenDocumentParsing = computed(() => knowledgeConfig.value.isDocumentParsing === 'Y')
async function handleGetKnowledgeListByIds() { async function handleGetKnowledgeListByIds() {
...@@ -83,7 +105,7 @@ function handleCloseAssociatedKnowledgeModal() { ...@@ -83,7 +105,7 @@ function handleCloseAssociatedKnowledgeModal() {
return return
} }
knowledgeConfigExpandedNames.value = ['knowledge'] !knowledgeConfigExpandedNames.value.includes('knowledge') && knowledgeConfigExpandedNames.value.push('knowledge')
handleGetKnowledgeListByIds() handleGetKnowledgeListByIds()
} }
...@@ -96,6 +118,44 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) { ...@@ -96,6 +118,44 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) {
isShowAgentKnowledgeSettingModal.value = false isShowAgentKnowledgeSettingModal.value = false
window.$message.success(t('common_module.save_success_message')) window.$message.success(t('common_module.save_success_message'))
} }
async function handleGetDatabaseListByIds() {
if (!databaseConfig.value?.ids?.length) return
const res = await fetchGetDataListByIds<DatabaseItemInterface[]>(databaseConfig.value.ids)
if (res.code === 0) {
selectDatabaseList.value = res.data
}
}
async function handleCloseAssociatedDatabaseModal() {
if (!databaseConfig.value?.ids?.length) {
selectDatabaseList.value = []
return
}
!knowledgeConfigExpandedNames.value.includes('database') && knowledgeConfigExpandedNames.value.push('database')
handleGetDatabaseListByIds()
}
function handleMouseoverDatabaseItem(dbId: number) {
hoverDBId.value = dbId
}
function handleMouseleaveDatabaseItem() {
hoverDBId.value = 0
}
function handleToDatabaseDetail(dbId: number) {
const url = `${window.location.origin}/fe/personal-space/database/table/${dbId}`
window.open(url, '_blank')
}
function handleDeleteDatabaseItem(dbId: number) {
databaseConfig.value.ids = databaseConfig.value.ids.filter((id) => id !== dbId)
selectDatabaseList.value = selectDatabaseList.value.filter((item) => item.id !== dbId)
}
</script> </script>
<template> <template>
...@@ -185,6 +245,66 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) { ...@@ -185,6 +245,66 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) {
</span> </span>
</NCollapseItem> </NCollapseItem>
<NCollapseItem :title="t('common_module.database')" name="database" class="my-[13px]!">
<template #header-extra>
<NTooltip trigger="hover">
<template #trigger>
<Plus
theme="outline"
size="22"
:stroke-width="3"
class="text-theme-color cursor-pointer"
@click="isShowAssociatedDatabaseModal = true"
/>
</template>
{{ t('personal_space_module.database_module.add_database') }}
</NTooltip>
</template>
<ul>
<li
v-for="databaseItem in selectDatabaseList"
:key="databaseItem.id"
class="rounded-theme flex cursor-pointer justify-between gap-2 border border-[#f2f5f9] px-3 py-1.5 hover:bg-[#f2f5f9]"
@mouseover="handleMouseoverDatabaseItem(databaseItem.id)"
@mouseleave="handleMouseleaveDatabaseItem"
@click="handleToDatabaseDetail(databaseItem.id)"
>
<div class="flex items-center gap-2 overflow-hidden">
<div
class="h-6 w-6 bg-[url('https://gsst-poe-sit.gz.bcebos.com/v1/icon/direct-db.svg')] bg-contain bg-no-repeat"
/>
<n-ellipsis class="flex-1">
<span class="text-xs">{{ databaseItem.title }}</span>
<template #tooltip>
<div class="max-w-[500px]">
<span class="text-xs"> {{ databaseItem.title }}</span>
</div>
</template>
</n-ellipsis>
</div>
<n-tooltip placement="top">
<template #trigger>
<div v-show="isHoverDatabaseItem(databaseItem.id)" class="flex items-center justify-center">
<i
class="hover:text-error-font-color text-font-color iconfont icon-reduce cursor-pointer outline-none"
@click.stop="handleDeleteDatabaseItem(databaseItem.id)"
/>
</div>
</template>
<span>{{ t('common_module.remove') }}</span>
</n-tooltip>
</li>
</ul>
<span v-show="!selectDatabaseList.length" class="text-xs text-[#84868c]">
{{
t('personal_space_module.agent_module.agent_setting_module.agent_config_module.associated_database_desc')
}}
</span>
</NCollapseItem>
<NCollapseItem name="uploadFile" class="my-[13px]!"> <NCollapseItem name="uploadFile" class="my-[13px]!">
<template #header> <template #header>
<span class="mr-[5px] min-w-[60px]"> <span class="mr-[5px] min-w-[60px]">
...@@ -222,4 +342,10 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) { ...@@ -222,4 +342,10 @@ function handleUpdateKnowledgeConfig(newKnowledgeConfig: KnowledgeConfigType) {
v-model:is-show-modal="isShowAgentKnowledgeSettingModal" v-model:is-show-modal="isShowAgentKnowledgeSettingModal"
@confirm="handleUpdateKnowledgeConfig" @confirm="handleUpdateKnowledgeConfig"
/> />
<AssociatedDatabaseModal
v-model:show-modal="isShowAssociatedDatabaseModal"
v-model:db-ids="databaseConfig.ids"
@close="handleCloseAssociatedDatabaseModal"
/>
</template> </template>
...@@ -60,6 +60,10 @@ async function handleGetAgentDetail(agentId: string) { ...@@ -60,6 +60,10 @@ async function handleGetAgentDetail(agentId: string) {
agentAvatar: agentAvatar:
res.data.baseInfo.agentAvatar || 'https://gsst-poe-sit.gz.bcebos.com/data/20240911/1726041369632.webp', res.data.baseInfo.agentAvatar || 'https://gsst-poe-sit.gz.bcebos.com/data/20240911/1726041369632.webp',
}, },
databaseConfig: {
...res.data.databaseConfig,
ids: res.data?.databaseConfig?.ids || [],
},
}) })
} }
}) })
......
...@@ -3,6 +3,7 @@ import { computed, onMounted, ref, useTemplateRef, watch } from 'vue' ...@@ -3,6 +3,7 @@ import { computed, onMounted, ref, useTemplateRef, watch } from 'vue'
import { useI18n } from 'vue-i18n' import { useI18n } from 'vue-i18n'
import { Search } from '@icon-park/vue-next' import { Search } from '@icon-park/vue-next'
import { DataTableInst } from 'naive-ui' import { DataTableInst } from 'naive-ui'
import { useRouter } from 'vue-router'
import useTableScrollY from '@/composables/useTableScrollY' import useTableScrollY from '@/composables/useTableScrollY'
import { usePagination } from '@/composables/usePagination.ts' import { usePagination } from '@/composables/usePagination.ts'
import CustomPagination from '@/components/custom-pagination/custom-pagination.vue' import CustomPagination from '@/components/custom-pagination/custom-pagination.vue'
...@@ -15,6 +16,8 @@ type DatabaseEditFormInterface = DatabaseFormInterface & { id: number } ...@@ -15,6 +16,8 @@ type DatabaseEditFormInterface = DatabaseFormInterface & { id: number }
const { t } = useI18n() const { t } = useI18n()
const router = useRouter()
const databaseListTableRef = useTemplateRef<DataTableInst>('databaseListTableRef') const databaseListTableRef = useTemplateRef<DataTableInst>('databaseListTableRef')
const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 32 + 18 + 16 + 28) const { pageContentWrapRef, tableContentY } = useTableScrollY(48 + 32 + 18 + 16 + 28)
...@@ -96,8 +99,11 @@ function handleDatabaseTableAction(actionType: string, dbId: number) { ...@@ -96,8 +99,11 @@ function handleDatabaseTableAction(actionType: string, dbId: number) {
} }
} }
function handleToDatabaseDetail(_dbId: number) { function handleToDatabaseDetail(dbId: number) {
// TODO router.push({
name: 'PersonalSpaceDBTable',
params: { id: dbId },
})
} }
function handleShowEditDatabaseModal(dbId: number) { function handleShowEditDatabaseModal(dbId: number) {
......
...@@ -144,6 +144,7 @@ function messageItemFactory(): ConversationMessageItem { ...@@ -144,6 +144,7 @@ function messageItemFactory(): ConversationMessageItem {
imageUrl: '', imageUrl: '',
reasoningContent: '', reasoningContent: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} }
} }
...@@ -231,13 +232,19 @@ function handleMessageSend(lastQuestionContent?: string) { ...@@ -231,13 +232,19 @@ function handleMessageSend(lastQuestionContent?: string) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent })
emit('updatePageScroll') emit('updatePageScroll')
return
} }
// 插件 // 插件
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name })
emit('updatePageScroll') emit('updatePageScroll')
}
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
})
return return
} }
......
...@@ -7,6 +7,7 @@ import CustomLoading from './custom-loading.vue' ...@@ -7,6 +7,7 @@ import CustomLoading from './custom-loading.vue'
import MusicWavesLoading from './music-waves-loading.vue' import MusicWavesLoading from './music-waves-loading.vue'
import MarkdownRender from '@/components/markdown-render/markdown-render.vue' import MarkdownRender from '@/components/markdown-render/markdown-render.vue'
import EditorDrawer from '@/components/editor-drawer/editor-drawer.vue' import EditorDrawer from '@/components/editor-drawer/editor-drawer.vue'
import ExecuteCodeRender from '@/components/execute-code-render/execute-code-render.vue'
import { PersonalAppConfigState } from '@/store/types/personal-app-config' import { PersonalAppConfigState } from '@/store/types/personal-app-config'
import { useLayoutConfig } from '@/composables/useLayoutConfig' import { useLayoutConfig } from '@/composables/useLayoutConfig'
import { useUserStore } from '@/store/modules/user' import { useUserStore } from '@/store/modules/user'
...@@ -205,6 +206,14 @@ const handleContentCopy = throttle( ...@@ -205,6 +206,14 @@ const handleContentCopy = throttle(
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="mb-[10px] w-full">
<ExecuteCodeRender
:title="t('common_module.database_QA_executed_successfully')"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<p class="break-all"> <p class="break-all">
<MarkdownRender <MarkdownRender
ref="markdownRenderRef" ref="markdownRenderRef"
......
...@@ -145,6 +145,7 @@ function messageItemFactory(): ConversationMessageItem { ...@@ -145,6 +145,7 @@ function messageItemFactory(): ConversationMessageItem {
imageUrl: '', imageUrl: '',
reasoningContent: '', reasoningContent: '',
knowledgeContentResult: [], knowledgeContentResult: [],
dbChainSQLContent: '',
} }
} }
...@@ -232,13 +233,19 @@ function handleMessageSend(lastQuestionContent?: string) { ...@@ -232,13 +233,19 @@ function handleMessageSend(lastQuestionContent?: string) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { reasoningContent })
emit('updatePageScroll') emit('updatePageScroll')
return
} }
// 插件 // 插件
if (data.function && data.function.name) { if (data.function && data.function.name) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name }) emit('updateSpecifyMessageItem', latestAssistantMessageKey, { pluginName: data.function.name })
emit('updatePageScroll') emit('updatePageScroll')
}
// 数据库
if (data.dbChainResult && data.dbChainResult?.[0]) {
emit('updateSpecifyMessageItem', latestAssistantMessageKey, {
dbChainSQLContent: data.dbChainResult?.[0]?.sql || '',
})
return return
} }
......
...@@ -135,6 +135,21 @@ function handleShowReasoningContentSwitch() { ...@@ -135,6 +135,21 @@ function handleShowReasoningContentSwitch() {
</div> </div>
<div v-else> <div v-else>
<!-- 数据库问答结果 -->
<div v-show="messageItem.dbChainSQLContent" class="database-container">
<div class="database-title-container">
<CheckOne theme="outline" size="16" fill="#40bd23" />
<span class="database-name">{{ t('common_module.database_QA_executed_successfully') }}</span>
</div>
<MarkdownRender
ref="markdownRenderRef"
dark-theme
color="#fff"
:font-size="'3.2vw'"
:raw-text-content="messageItem.dbChainSQLContent"
/>
</div>
<p class="break-all"> <p class="break-all">
<MarkdownRender <MarkdownRender
ref="markdownRenderRef" ref="markdownRenderRef"
...@@ -221,6 +236,18 @@ function handleShowReasoningContentSwitch() { ...@@ -221,6 +236,18 @@ function handleShowReasoningContentSwitch() {
} }
} }
.database-container {
@apply mb-[10px] rounded-[10px] bg-[#F3F5F9] p-[14px] pt-0 text-[12px];
.database-title-container {
@apply flex items-center gap-[5px] py-[10px];
.database-name {
@apply text-gray-font-color text-[12px] leading-4;
}
}
}
.content-loading { .content-loading {
@apply py-[6px] pl-[16px]; @apply py-[6px] pl-[16px];
} }
......
...@@ -5,6 +5,13 @@ declare interface KnowledgeContentResultItem { ...@@ -5,6 +5,13 @@ declare interface KnowledgeContentResultItem {
documentName: string documentName: string
} }
declare interface DBChainResultItem {
result: string
sql: string
sqlResult: string
status: string
}
declare interface ConversationMessageItem { declare interface ConversationMessageItem {
timestamp: number timestamp: number
role: 'user' | 'assistant' role: 'user' | 'assistant'
...@@ -21,4 +28,5 @@ declare interface ConversationMessageItem { ...@@ -21,4 +28,5 @@ declare interface ConversationMessageItem {
reasoningContent: string reasoningContent: string
modelName?: string modelName?: string
knowledgeContentResult: KnowledgeContentResultItem[] knowledgeContentResult: KnowledgeContentResultItem[]
dbChainSQLContent: string
} }
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment