feat: Enhance agent settings navigation and error handling in UI components

- Introduced a new method to handle navigation to agent settings, improving code organization and reusability.
- Updated tooltip content in the agent mode button to include a link for quick access to agent settings.
- Enhanced error handling in the KnowledgeBase component to provide actionable links for VLM configuration errors.
- Added initial section handling in the KnowledgeBaseEditorModal for improved user experience during knowledge base editing.
This commit is contained in:
wizardchen
2025-11-07 16:40:53 +08:00
parent d387fd672f
commit f30720f99b
10 changed files with 133 additions and 48 deletions
+28 -7
View File
@@ -100,6 +100,15 @@ const onKeydown = (val: string, event: { e: { preventDefault(): unknown; keyCode
}
}
const handleGoToAgentSettings = () => {
// 使用 uiStore 打开设置并跳转到 agent 部分
uiStore.openSettings('agent');
// 如果当前不在设置页面,导航到设置页面
if (route.path !== '/platform/settings') {
router.push('/platform/settings');
}
}
const toggleAgentMode = () => {
// 如果要启用 Agent,先检查是否就绪
// 注意:isAgentReady 是从 store 中计算的,需要确保 store 中的配置是最新的
@@ -114,12 +123,7 @@ const toggleAgentMode = () => {
href: '#',
onClick: (e: Event) => {
e.preventDefault();
// 使用 uiStore 打开设置并跳转到 agent 部分
uiStore.openSettings('agent');
// 如果当前不在设置页面,导航到设置页面
if (route.path !== '/platform/settings') {
router.push('/platform/settings');
}
handleGoToAgentSettings();
},
style: 'color: #07C05F; text-decoration: none; font-weight: 500; cursor: pointer; white-space: nowrap; flex-shrink: 0;',
onMouseenter: (e: Event) => {
@@ -169,9 +173,26 @@ onBeforeRouteUpdate((to, from, next) => {
<div class="control-left">
<!-- Agent 模式按钮 -->
<t-tooltip
:content="isAgentEnabled ? 'Agent 模式(已启用)' : (settingsStore.isAgentReady ? 'Agent 模式(已禁用,点击启用)' : 'Agent 未就绪,请先完成配置')"
placement="top"
:show-arrow="true"
:duration="0"
>
<template #content>
<div style="display: flex; flex-direction: column; gap: 4px; align-items: flex-start;">
<span>
{{ isAgentEnabled ? 'Agent 模式(已启用)' : (settingsStore.isAgentReady ? 'Agent 模式(已禁用,点击启用)' : 'Agent 未就绪,请先完成配置') }}
</span>
<a
href="#"
@click.prevent="handleGoToAgentSettings"
style="color: #07C05F; text-decoration: none; font-size: 12px; cursor: pointer;"
@mouseenter="(e) => e.target.style.textDecoration = 'underline'"
@mouseleave="(e) => e.target.style.textDecoration = 'none'"
>
去设置 Agent →
</a>
</div>
</template>
<div
class="control-btn agent-btn"
:class="{
+11 -11
View File
@@ -159,9 +159,9 @@
<t-input
v-model.number="formData.dimension"
type="number"
:min="128"
:max="4096"
placeholder="如:1536"
:min="128"
:max="4096"
placeholder="如:1536"
:disabled="formData.source === 'local' && checking"
/>
<!-- Ollama 本地模型:自动检测维度按钮 -->
@@ -642,14 +642,14 @@ watch(() => formData.value.modelName, async (newValue, oldValue) => {
// 处理下载逻辑
if (newValue.startsWith('__download__')) {
// 提取模型名称
const modelName = newValue.replace('__download__', '')
// 重置选择(避免显示 __download__ 前缀)
formData.value.modelName = ''
// 开始下载
await startDownload(modelName)
// 提取模型名称
const modelName = newValue.replace('__download__', '')
// 重置选择(避免显示 __download__ 前缀)
formData.value.modelName = ''
// 开始下载
await startDownload(modelName)
return
}
-4
View File
@@ -24,10 +24,6 @@
<t-icon name="server" class="menu-icon" />
<span>Ollama</span>
</div>
<div class="menu-item" @click="handleQuickNav('knowledge')">
<t-icon name="folder-open" class="menu-icon" />
<span>知识库</span>
</div>
<div class="menu-divider"></div>
<div class="menu-item" @click="handleSettings">
<t-icon name="setting" class="menu-icon" />
+31 -2
View File
@@ -88,7 +88,7 @@
<script setup lang="ts">
import { storeToRefs } from 'pinia';
import { onMounted, watch, computed, ref, reactive, nextTick } from 'vue';
import { onMounted, watch, computed, ref, reactive, nextTick, h } from 'vue';
import { useRoute, useRouter } from 'vue-router';
import { getSessionsList, delSession } from "@/api/chat/index";
import { getKnowledgeBaseById, listKnowledgeBases, uploadKnowledgeFile } from '@/api/knowledge-base';
@@ -342,7 +342,36 @@ const upload = async (e: any) => {
} else if (err.message) {
errorMessage = err.message;
}
MessagePlugin.error(errorMessage);
// 如果是 VLM 配置错误,添加跳转链接
if (errorMessage.includes('VLM配置') || errorMessage.includes('多模态') || errorMessage.includes('对象存储')) {
const messageContent = h('div', { style: 'display: flex; align-items: center; gap: 8px; flex-wrap: wrap;' }, [
h('span', { style: 'flex: 1; min-width: 0;' }, errorMessage),
h('a', {
href: '#',
onClick: (e: Event) => {
e.preventDefault();
// 打开知识库设置弹框,并导航到高级设置部分
console.log('Opening KB editor for:', currentKbId, 'section: advanced');
uiStore.openEditKB(currentKbId, 'advanced');
},
style: 'color: #07C05F; text-decoration: none; font-weight: 500; cursor: pointer; white-space: nowrap; flex-shrink: 0;',
onMouseenter: (e: Event) => {
(e.target as HTMLElement).style.textDecoration = 'underline';
},
onMouseleave: (e: Event) => {
(e.target as HTMLElement).style.textDecoration = 'none';
}
}, '去设置 →')
]);
MessagePlugin.error({
content: messageContent,
duration: 5000
});
} else {
MessagePlugin.error(errorMessage);
}
} finally {
uploadInput.value.value = "";
}
+9 -1
View File
@@ -6,6 +6,7 @@ export const useUIStore = defineStore('ui', {
showKBEditorModal: false,
kbEditorMode: 'create' as 'create' | 'edit',
currentKBId: null as string | null,
kbEditorInitialSection: null as string | null,
settingsInitialSection: null as string | null,
settingsInitialSubSection: null as string | null
}),
@@ -27,21 +28,28 @@ export const useUIStore = defineStore('ui', {
this.showSettingsModal = !this.showSettingsModal
},
openKBSettings(kbId: string) {
openKBSettings(kbId: string, initialSection?: string) {
this.currentKBId = kbId
this.kbEditorMode = 'edit'
this.kbEditorInitialSection = initialSection || null
this.showKBEditorModal = true
},
openEditKB(kbId: string, initialSection?: string) {
this.openKBSettings(kbId, initialSection)
},
openCreateKB() {
this.currentKBId = null
this.kbEditorMode = 'create'
this.kbEditorInitialSection = null
this.showKBEditorModal = true
},
closeKBEditor() {
this.showKBEditorModal = false
this.currentKBId = null
this.kbEditorInitialSection = null
}
}
})
@@ -378,7 +378,7 @@
</div>
<!-- 多模态配置区域 -->
<div class="config-section">
<div id="multimodal-config-section" class="config-section">
<h3><t-icon name="image" class="section-icon" />多模态配置</h3>
<div class="form-row">
<t-form-item name="multimodal.enabled">
@@ -7,8 +7,11 @@ import { useRoute, useRouter } from 'vue-router';
import EmptyKnowledge from '@/components/empty-knowledge.vue';
import { getSessionsList, createSessions, generateSessionsTitle } from "@/api/chat/index";
import { useMenuStore } from '@/stores/menu';
import { useUIStore } from '@/stores/ui';
import { MessagePlugin } from 'tdesign-vue-next';
import KnowledgeBaseEditorModal from './KnowledgeBaseEditorModal.vue';
const usemenuStore = useMenuStore();
const uiStore = useUIStore();
const router = useRouter();
import {
batchQueryKnowledge,
@@ -175,6 +178,14 @@ const sendMsg = (value: string) => {
createNewSession(value);
};
// 处理知识库编辑成功后的回调
const handleKBEditorSuccess = (kbIdValue: string) => {
// 如果编辑的是当前知识库,刷新文件列表
if (kbIdValue === kbId.value) {
loadKnowledgeFiles(kbIdValue);
}
};
const getTitle = (session_id: string, value: string) => {
const now = new Date().toISOString();
let obj = {
@@ -265,6 +276,15 @@ async function createNewSession(value: string): Promise<void> {
<DocContent :visible="isCardDetails" :details="details" @closeDoc="closeDoc" @getDoc="getDoc"></DocContent>
</div>
<EmptyKnowledge v-show="!cardList.length"></EmptyKnowledge>
<!-- 知识库编辑器(创建/编辑统一组件) -->
<KnowledgeBaseEditorModal
:visible="uiStore.showKBEditorModal"
:mode="uiStore.kbEditorMode"
:kb-id="uiStore.currentKBId || undefined"
@update:visible="(val) => val ? null : uiStore.closeKBEditor()"
@success="handleKBEditorSuccess"
/>
</template>
<style>
.card-more {
@@ -119,10 +119,13 @@ import { MessagePlugin } from 'tdesign-vue-next'
import { createKnowledgeBase, getKnowledgeBaseById, listKnowledgeFiles, updateKnowledgeBase } from '@/api/knowledge-base'
import { updateKBConfig, type KBModelConfigRequest } from '@/api/initialization'
import { listModels } from '@/api/model'
import { useUIStore } from '@/stores/ui'
import KBModelConfig from './settings/KBModelConfig.vue'
import KBChunkingSettings from './settings/KBChunkingSettings.vue'
import KBAdvancedSettings from './settings/KBAdvancedSettings.vue'
const uiStore = useUIStore()
// Props
const props = defineProps<{
visible: boolean
@@ -506,6 +509,11 @@ watch(() => props.visible, async (newVal) => {
// 打开弹窗时,先重置状态
resetState()
// 检查是否有初始 section,如果有则跳转
if (uiStore.kbEditorInitialSection) {
currentSection.value = uiStore.kbEditorInitialSection
}
// 加载模型列表
await loadAllModels()
@@ -521,6 +529,7 @@ watch(() => props.visible, async (newVal) => {
// 关闭弹窗时,延迟重置状态(等待动画结束)
setTimeout(() => {
resetState()
currentSection.value = 'basic' // 重置为默认 section
}, 300)
}
})
+20 -20
View File
@@ -34,7 +34,7 @@
</p>
</div>
</div>
</div>
</div>
<div class="settings-group">
@@ -298,7 +298,7 @@ onMounted(async () => {
// 再等待一帧,确保所有事件监听器都已设置好
requestAnimationFrame(() => {
// 初始化完成,现在可以允许保存操作
isInitializing.value = false
isInitializing.value = false
})
} catch (error) {
console.error('加载Agent配置失败:', error)
@@ -362,8 +362,8 @@ const handleMaxIterationsChangeDebounced = (value: number) => {
// 清除之前的定时器
if (maxIterationsDebounceTimer) {
clearTimeout(maxIterationsDebounceTimer)
}
}
// 设置新的定时器,300ms 后保存(减少延迟,提升响应速度)
maxIterationsDebounceTimer = setTimeout(async () => {
// 再次检查值是否变化(可能在等待期间值又变了)
@@ -371,28 +371,28 @@ const handleMaxIterationsChangeDebounced = (value: number) => {
maxIterationsDebounceTimer = null
return
}
try {
const config: AgentConfig = {
try {
const config: AgentConfig = {
enabled: isAgentReady.value, // 自动根据配置状态设置
max_iterations: numValue, // 确保是数字类型
reflection_enabled: false,
allowed_tools: localAllowedTools.value,
temperature: localTemperature.value,
thinking_model_id: localThinkingModelId.value,
rerank_model_id: localRerankModelId.value
}
await updateAgentConfig(config)
reflection_enabled: false,
allowed_tools: localAllowedTools.value,
temperature: localTemperature.value,
thinking_model_id: localThinkingModelId.value,
rerank_model_id: localRerankModelId.value
}
await updateAgentConfig(config)
settingsStore.updateAgentConfig({ maxIterations: numValue })
lastSavedValue = numValue // 记录已保存的值
MessagePlugin.success('最大迭代次数已保存')
} catch (error) {
console.error('保存失败:', error)
MessagePlugin.error(getErrorMessage(error))
MessagePlugin.success('最大迭代次数已保存')
} catch (error) {
console.error('保存失败:', error)
MessagePlugin.error(getErrorMessage(error))
} finally {
maxIterationsDebounceTimer = null
}
}
}, 300)
}
@@ -16,6 +16,8 @@ import (
"gorm.io/gorm"
)
const MAX_ITERATIONS = 30 // Max iterations for agent execution
// agentService implements agent-related business logic
type agentService struct {
cfg *config.Config
@@ -194,8 +196,8 @@ func (s *agentService) ValidateConfig(config *types.AgentConfig) error {
config.MaxIterations = 5 // Default
}
if config.MaxIterations > 20 {
return fmt.Errorf("max iterations too high: %d (max 20)", config.MaxIterations)
if config.MaxIterations > MAX_ITERATIONS {
return fmt.Errorf("max iterations too high: %d (max %d)", config.MaxIterations, MAX_ITERATIONS)
}
if config.ThinkingModelID == "" {