From 8d40959f88b00a54caf359b4cfd428345006c0de Mon Sep 17 00:00:00 2001 From: wizardchen Date: Wed, 1 Jul 2026 16:55:30 +0800 Subject: [PATCH] fix lint and test failures --- frontend/src/api/chat/streame.ts | 2 +- frontend/src/api/tenant/index.ts | 2 +- .../src/components/AgentEmbedChannelPanel.vue | 2 +- frontend/src/components/AgentSelector.vue | 28 ++++-- frontend/src/components/IMChannelPanel.vue | 2 +- frontend/src/components/ModelEditorDialog.vue | 12 +-- .../components/manual-knowledge-editor.vue | 2 +- frontend/src/components/menu.vue | 1 + .../src/composables/useChatStreamHandler.ts | 3 +- frontend/src/stores/editorResources.ts | 8 +- frontend/src/stores/settings.ts | 1 + frontend/src/utils/request.ts | 30 +++---- frontend/src/utils/security.ts | 4 +- frontend/src/views/agent/AgentEditorModal.vue | 4 +- .../chat/components/AgentStreamDisplay.vue | 3 +- .../views/chat/components/McpOAuthCard.vue | 10 +-- .../chat/components/ToolResultRenderer.vue | 2 +- frontend/src/views/chat/components/botmsg.vue | 2 +- .../src/views/chat/components/deepThink.vue | 2 +- .../src/views/chat/components/docInfo.vue | 2 +- .../components/tool-results/ChunkDetail.vue | 2 - .../components/tool-results/DocumentInfo.vue | 2 +- .../tool-results/GraphQueryResults.vue | 2 +- .../tool-results/KnowledgeBaseList.vue | 2 - .../components/tool-results/RelatedChunks.vue | 2 +- .../tool-results/ThinkingDisplay.vue | 2 - .../src/views/chat/components/usermsg.vue | 2 +- frontend/src/views/chat/index.vue | 2 +- frontend/src/views/embed/EmbedChatCore.vue | 29 ++++-- .../src/views/knowledge/KnowledgeBase.vue | 10 +-- .../src/views/knowledge/KnowledgeBaseList.vue | 2 + .../knowledge/components/DocumentListView.vue | 16 ++-- .../knowledge/components/FAQEntryManager.vue | 16 ++-- .../components/KbTagManageDrawer.vue | 6 +- .../components/KbUploadSourceDropdown.vue | 2 +- .../components/UploadConfirmDialog.vue | 2 +- .../settings/DataSourceEditorDialog.vue | 2 +- .../knowledge/settings/GraphSettings.vue | 2 +- .../knowledge/settings/KBAdvancedSettings.vue | 2 +- .../knowledge/settings/KBChunkingSettings.vue | 2 +- frontend/src/views/settings/ApiInfo.vue | 3 +- .../views/settings/StorageEngineSettings.vue | 3 +- frontend/tsconfig.app.json | 2 + internal/agent/tools/mcp_oauth.go | 51 +++++++++++ .../datasource/connector/yuque/client_test.go | 4 + internal/im/service.go | 89 +++++++++++++++++++ internal/types/const.go | 6 ++ internal/types/context_helpers.go | 18 ++++ 48 files changed, 301 insertions(+), 104 deletions(-) diff --git a/frontend/src/api/chat/streame.ts b/frontend/src/api/chat/streame.ts index eaa9657b..1d08b900 100644 --- a/frontend/src/api/chat/streame.ts +++ b/frontend/src/api/chat/streame.ts @@ -200,7 +200,7 @@ export function useStream() { let chunkHandler: ((data: any) => void) | null = null // 注册块处理器 - const onChunk = (handler: () => void) => { + const onChunk = (handler: (data: any) => void) => { chunkHandler = handler } diff --git a/frontend/src/api/tenant/index.ts b/frontend/src/api/tenant/index.ts index a785a83b..162a91e1 100644 --- a/frontend/src/api/tenant/index.ts +++ b/frontend/src/api/tenant/index.ts @@ -89,7 +89,7 @@ export async function listAllTenants(): Promise<{ success: boolean; data?: { ite * 重置租户的 API Key。成功后返回新的明文 Key,旧 Key 立即失效。 */ export async function resetTenantApiKey( - tenantId: number, + tenantId: string | number, ): Promise<{ success: boolean; data?: { api_key: string }; message?: string }> { try { const response = await post(`/api/v1/tenants/${tenantId}/api-key`) diff --git a/frontend/src/components/AgentEmbedChannelPanel.vue b/frontend/src/components/AgentEmbedChannelPanel.vue index ba28ca19..674efd90 100644 --- a/frontend/src/components/AgentEmbedChannelPanel.vue +++ b/frontend/src/components/AgentEmbedChannelPanel.vue @@ -31,7 +31,7 @@
+ @click="handleChannelMenuClick($event, ch)"> diff --git a/frontend/src/components/AgentSelector.vue b/frontend/src/components/AgentSelector.vue index 7848e61a..573d3502 100644 --- a/frontend/src/components/AgentSelector.vue +++ b/frontend/src/components/AgentSelector.vue @@ -204,6 +204,10 @@ type AgentDetailTarget = { sharedMeta?: { org_name?: string; shared_by_username?: string }; }; +type SharedAgentSelection = Omit & { + agent: CustomAgent; +}; + const dropdownStyle = ref>({}); const activeDetail = ref(null); const detailAnchorEl = ref(null); @@ -232,13 +236,21 @@ const builtinAgents = computed(() => { const customAgents = computed(() => agentsList.value.filter(a => !a.is_builtin)); -const sharedAgentsList = computed(() => - (orgStore.sharedAgents || []).filter(shared => !shared.disabled_by_me), +const toCustomAgent = (agent: SharedAgentInfo['agent']): CustomAgent => ({ + is_builtin: false, + config: {}, + ...agent, +}); + +const sharedAgentsList = computed(() => + (orgStore.sharedAgents || []) + .filter(shared => !shared.disabled_by_me) + .map(shared => ({ ...shared, agent: toCustomAgent(shared.agent) })), ); const currentAgentSourceTenantId = computed(() => settingsStore.selectedAgentSourceTenantId ?? null); -const isSharedAgentSelected = (shared: SharedAgentInfo) => +const isSharedAgentSelected = (shared: SharedAgentSelection) => props.currentAgentId === shared.agent.id && currentAgentSourceTenantId.value === String(shared.source_tenant_id); const isMyAgentSelected = (agent: CustomAgent) => @@ -394,8 +406,8 @@ const onOptionEnter = (agent: CustomAgent, event: MouseEvent, sourceTenantId?: s scheduleDetailPanelPosition(); }; -const onSharedOptionEnter = (shared: SharedAgentInfo, event: MouseEvent) => { - onOptionEnter(shared.agent as CustomAgent, event, String(shared.source_tenant_id), { +const onSharedOptionEnter = (shared: SharedAgentSelection, event: MouseEvent) => { + onOptionEnter(shared.agent, event, String(shared.source_tenant_id), { org_name: shared.org_name, shared_by_username: shared.shared_by_username, }); @@ -430,13 +442,13 @@ const selectAgent = (agent: CustomAgent) => { emit('select', agent); }; -const selectSharedAgent = (shared: SharedAgentInfo) => { +const selectSharedAgent = (shared: SharedAgentSelection) => { const sourceTenantId = String(shared.source_tenant_id); if (getAgentNotReadyLabels(shared.agent, sourceTenantId).length > 0) { - emitAgentNotReady(shared.agent as CustomAgent, sourceTenantId); + emitAgentNotReady(shared.agent, sourceTenantId); return; } - emit('select', shared.agent as CustomAgent, sourceTenantId); + emit('select', shared.agent, sourceTenantId); }; const goToSettings = (agent: CustomAgent, sourceTenantId?: string) => { diff --git a/frontend/src/components/IMChannelPanel.vue b/frontend/src/components/IMChannelPanel.vue index 398ee378..9600220b 100644 --- a/frontend/src/components/IMChannelPanel.vue +++ b/frontend/src/components/IMChannelPanel.vue @@ -33,7 +33,7 @@
+ @click="handleChannelMenuClick($event, channel)"> diff --git a/frontend/src/components/ModelEditorDialog.vue b/frontend/src/components/ModelEditorDialog.vue index 40a0762d..90f38c99 100644 --- a/frontend/src/components/ModelEditorDialog.vue +++ b/frontend/src/components/ModelEditorDialog.vue @@ -967,7 +967,7 @@ const selectModelType = async (type: EditorModelType) => { formData.value.provider = 'generic' formData.value.baseUrl = '' } else { - handleProviderChange(formData.value.provider) + handleProviderChange(formData.value.provider || 'generic') } if (showThinkingControlField.value && !isEdit.value) { thinkingControlManual.value = false @@ -1319,7 +1319,7 @@ const checkRemoteAPI = async () => { // 对话模型(KnowledgeQA) result = await checkRemoteModel({ modelName: formData.value.modelName, - baseUrl: formData.value.baseUrl, + baseUrl: formData.value.baseUrl || '', apiKey: formData.value.apiKey || '', provider: formData.value.provider, ...idPayload, @@ -1332,7 +1332,7 @@ const checkRemoteAPI = async () => { result = await testEmbeddingModel({ source: 'remote', modelName: formData.value.modelName, - baseUrl: formData.value.baseUrl, + baseUrl: formData.value.baseUrl || '', apiKey: formData.value.apiKey || '', dimension: formData.value.dimension, supportsDimensionOverride: formData.value.supportsDimensionOverride ?? false, @@ -1360,7 +1360,7 @@ const checkRemoteAPI = async () => { : {} result = await checkRerankModel({ modelName: formData.value.modelName, - baseUrl: formData.value.baseUrl, + baseUrl: formData.value.baseUrl || '', apiKey: formData.value.apiKey || '', provider: formData.value.provider, ...idPayload, @@ -1375,7 +1375,7 @@ const checkRemoteAPI = async () => { // VLLM 使用 checkRemoteModel 进行基础连接测试 result = await checkRemoteModel({ modelName: formData.value.modelName, - baseUrl: formData.value.baseUrl, + baseUrl: formData.value.baseUrl || '', apiKey: formData.value.apiKey || '', provider: formData.value.provider, ...idPayload, @@ -1387,7 +1387,7 @@ const checkRemoteAPI = async () => { // ASR 模型(语音识别)— 使用专用的 ASR 测试接口(/v1/audio/transcriptions) result = await checkASRModel({ modelName: formData.value.modelName, - baseUrl: formData.value.baseUrl, + baseUrl: formData.value.baseUrl || '', apiKey: formData.value.apiKey || '', provider: formData.value.provider, ...idPayload, diff --git a/frontend/src/components/manual-knowledge-editor.vue b/frontend/src/components/manual-knowledge-editor.vue index 84a14773..560951c9 100644 --- a/frontend/src/components/manual-knowledge-editor.vue +++ b/frontend/src/components/manual-knowledge-editor.vue @@ -414,7 +414,7 @@ const previewHTML = computed(() => { return `

${t('manualEditor.preview.empty')}

` } const safeMarkdown = safeMarkdownToHTML(form.content) - const html = marked.parse(safeMarkdown) + const html = marked.parse(safeMarkdown, { async: false }) return sanitizeHTML(html) }) diff --git a/frontend/src/components/menu.vue b/frontend/src/components/menu.vue index ababf30c..1924c5f5 100644 --- a/frontend/src/components/menu.vue +++ b/frontend/src/components/menu.vue @@ -482,6 +482,7 @@ const filteredGroupedSessions = computed(() => { bucket.items.map((item) => ({ ...item, path: `chat/${item.id}`, + title: item.title || '', })), dateBucketLabels.value, (session) => classifyDateBucket(session.updated_at || session.created_at), diff --git a/frontend/src/composables/useChatStreamHandler.ts b/frontend/src/composables/useChatStreamHandler.ts index 3f33095e..f3e53b24 100644 --- a/frontend/src/composables/useChatStreamHandler.ts +++ b/frontend/src/composables/useChatStreamHandler.ts @@ -259,7 +259,8 @@ export function useChatStreamHandler(options: UseChatStreamHandlerOptions) { const events: ChatMessage[] = [] if (agentSteps && Array.isArray(agentSteps) && agentSteps.length > 0) { - agentSteps.forEach((step: ChatMessage) => { + agentSteps.forEach((rawStep) => { + const step = rawStep as ChatMessage const stepTimestamp = step.timestamp ? new Date(String(step.timestamp)).getTime() : 0 const toolCalls = step.tool_calls const hasToolCalls = toolCalls && Array.isArray(toolCalls) && toolCalls.length > 0 diff --git a/frontend/src/stores/editorResources.ts b/frontend/src/stores/editorResources.ts index ee9b0293..c49e786c 100644 --- a/frontend/src/stores/editorResources.ts +++ b/frontend/src/stores/editorResources.ts @@ -13,7 +13,7 @@ import { } from '@/api/system' import { listMCPServices, type MCPService } from '@/api/mcp-service' import { listSkills, type SkillInfo } from '@/api/skill' -import { getAgentTypePresets, getPlaceholders, type AgentTypePreset, type PlaceholderDefinition } from '@/api/agent' +import { getAgentTypePresets, getPlaceholders, type AgentTypePreset, type PlaceholdersResponse } from '@/api/agent' import { getTenantRetrievalConfig } from '@/api/retrieval' const CACHE_TTL_MS = 60_000 @@ -38,7 +38,7 @@ export const useEditorResourcesStore = defineStore('editorResources', () => { const skillsAvailable = ref(true) const agentTypePresets = ref([]) const promptTemplates = ref(null) - const placeholders = ref([]) + const placeholders = ref(null) const tenantRetrievalConfig = ref | null>(null) const parserEngines = ref([]) const systemInfo = ref(null) @@ -114,7 +114,7 @@ export const useEditorResourcesStore = defineStore('editorResources', () => { async function ensurePlaceholders(force = false): Promise { return runOnce('placeholders', force, async () => { const placeholdersRes = await getPlaceholders() - placeholders.value = placeholdersRes?.data ?? [] + placeholders.value = placeholdersRes?.data ?? null loadedAt.value.placeholders = Date.now() }) } @@ -166,7 +166,7 @@ export const useEditorResourcesStore = defineStore('editorResources', () => { skills.value = [] agentTypePresets.value = [] promptTemplates.value = null - placeholders.value = [] + placeholders.value = null tenantRetrievalConfig.value = null parserEngines.value = [] systemInfo.value = null diff --git a/frontend/src/stores/settings.ts b/frontend/src/stores/settings.ts index f7cf8e56..9af40c66 100644 --- a/frontend/src/stores/settings.ts +++ b/frontend/src/stores/settings.ts @@ -18,6 +18,7 @@ interface Settings { selectedTags: Array<{ id: string; name: string; kbId: string; kbName?: string }>; selectedMCPServices: string[]; selectedSkills: string[]; + selectedTools?: string[]; modelConfig: ModelConfig; // 模型配置 ollamaConfig: OllamaConfig; // Ollama配置 webSearchEnabled: boolean; // 网络搜索是否启用 diff --git a/frontend/src/utils/request.ts b/frontend/src/utils/request.ts index 152e9be9..bc90c901 100644 --- a/frontend/src/utils/request.ts +++ b/frontend/src/utils/request.ts @@ -246,14 +246,14 @@ instance.interceptors.response.use( } ); -export function get(url: string, config?: any) { - return instance.get(url, config); +export function get(url: string, config?: any): Promise { + return instance.get(url, config) as unknown as Promise; } -export async function getDown(url: string) { - let res = await instance.get(url, { +export async function getDown(url: string): Promise { + const res = await instance.get(url, { responseType: "blob", - }); + }) as unknown as Blob; return res } @@ -262,7 +262,7 @@ export function postUpload( data = {}, onUploadProgress?: (progressEvent: any) => void, config: any = {}, -) { +): Promise { return instance.post(url, data, { ...config, headers: { @@ -271,26 +271,26 @@ export function postUpload( ...(config.headers || {}), }, onUploadProgress: onUploadProgress || config.onUploadProgress, - }); + }) as unknown as Promise; } -export function postChat(url: string, data = {}) { +export function postChat(url: string, data = {}): Promise { return instance.post(url, data, { headers: { "Content-Type": "text/event-stream;charset=utf-8", "X-Request-ID": `${generateRandomString(12)}`, }, - }); + }) as unknown as Promise; } -export function post(url: string, data = {}, config?: any) { - return instance.post(url, data, config); +export function post(url: string, data = {}, config?: any): Promise { + return instance.post(url, data, config) as unknown as Promise; } -export function put(url: string, data = {}) { - return instance.put(url, data); +export function put(url: string, data = {}, config?: any): Promise { + return instance.put(url, data, config) as unknown as Promise; } -export function del(url: string, data?: any) { - return instance.delete(url, { data }); +export function del(url: string, data?: any): Promise { + return instance.delete(url, { data }) as unknown as Promise; } diff --git a/frontend/src/utils/security.ts b/frontend/src/utils/security.ts index 22a70f18..f462c767 100644 --- a/frontend/src/utils/security.ts +++ b/frontend/src/utils/security.ts @@ -69,7 +69,7 @@ export function sanitizeHTML(html: string): string { try { const preparedHTML = protectProviderImageSrcInHTML(html); - return DOMPurify.sanitize(preparedHTML, DOMPurifyConfig); + return DOMPurify.sanitize(preparedHTML, DOMPurifyConfig as unknown as Config); } catch (error) { console.error('HTML sanitization failed:', error); // 如果清理失败,返回转义的纯文本 @@ -85,7 +85,7 @@ export function sanitizeMarkdownHTML(html: string): string { try { const preparedHTML = protectProviderImageSrcInHTML(html); - return DOMPurify.sanitize(preparedHTML, markdownDomPurifyConfig as Config); + return DOMPurify.sanitize(preparedHTML, markdownDomPurifyConfig as unknown as Config); } catch (error) { console.error('Markdown HTML sanitization failed:', error); return escapeHTML(html); diff --git a/frontend/src/views/agent/AgentEditorModal.vue b/frontend/src/views/agent/AgentEditorModal.vue index 4d7af6ef..c5d58ff1 100644 --- a/frontend/src/views/agent/AgentEditorModal.vue +++ b/frontend/src/views/agent/AgentEditorModal.vue @@ -2994,7 +2994,9 @@ const loadDependencies = async () => { webSearchProviderList.value = chatResources.webSearchProviders as WebSearchProviderEntity[]; - placeholderData.value = editorResources.placeholders as PlaceholderDefinition[]; + if (editorResources.placeholders) { + placeholderData.value = editorResources.placeholders; + } const rc = editorResources.tenantRetrievalConfig as Record | null; if (rc?.embedding_top_k) defaultEmbeddingTopK.value = rc.embedding_top_k; diff --git a/frontend/src/views/chat/components/AgentStreamDisplay.vue b/frontend/src/views/chat/components/AgentStreamDisplay.vue index 06bf5b12..aadda260 100644 --- a/frontend/src/views/chat/components/AgentStreamDisplay.vue +++ b/frontend/src/views/chat/components/AgentStreamDisplay.vue @@ -719,6 +719,7 @@ interface SessionData { isAgentMode?: boolean; agentEventStream?: any[]; knowledge_references?: any[]; + [key: string]: unknown; } const props = defineProps<{ @@ -1617,7 +1618,7 @@ const onRootClick = (e: Event) => { const slug = wikiEl.getAttribute('data-slug'); // Determine the relevant KB ID - const kbId = getKbIdForWiki(slug); + const kbId = getKbIdForWiki(slug || ''); if (kbId && slug) { openWikiDrawer(kbId, slug); diff --git a/frontend/src/views/chat/components/McpOAuthCard.vue b/frontend/src/views/chat/components/McpOAuthCard.vue index 1ece3bb0..0efb990d 100644 --- a/frontend/src/views/chat/components/McpOAuthCard.vue +++ b/frontend/src/views/chat/components/McpOAuthCard.vue @@ -81,8 +81,8 @@ const { t } = useI18n() const authorizing = ref(false) const canceling = ref(false) const now = ref(Date.now()) -let clock: ReturnType | null = null -let poll: ReturnType | null = null +let clock: number | null = null +let poll: number | null = null const deadline = computed(() => { const base = (props.requestedAt || 0) * 1000 @@ -138,7 +138,7 @@ function formatCountdown(s: number): string { function stopPoll() { if (poll) { - clearInterval(poll) + window.clearInterval(poll) poll = null } } @@ -248,13 +248,13 @@ const authorize = async () => { } onMounted(() => { - clock = setInterval(() => { + clock = window.setInterval(() => { now.value = Date.now() }, 1000) }) onBeforeUnmount(() => { - if (clock) clearInterval(clock) + if (clock) window.clearInterval(clock) stopPoll() }) diff --git a/frontend/src/views/chat/components/ToolResultRenderer.vue b/frontend/src/views/chat/components/ToolResultRenderer.vue index d3e29263..3c64818c 100644 --- a/frontend/src/views/chat/components/ToolResultRenderer.vue +++ b/frontend/src/views/chat/components/ToolResultRenderer.vue @@ -59,7 +59,7 @@