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:
@@ -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="{
|
||||
|
||||
@@ -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
|
||||
}
|
||||
|
||||
|
||||
@@ -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" />
|
||||
|
||||
@@ -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 = "";
|
||||
}
|
||||
|
||||
@@ -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)
|
||||
}
|
||||
})
|
||||
|
||||
@@ -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 == "" {
|
||||
|
||||
Reference in New Issue
Block a user