From bbf4e30847c7b0487a29ba7d4349d56ebfa4b249 Mon Sep 17 00:00:00 2001 From: ShiYu Date: Sat, 7 Jun 2025 10:00:08 +0800 Subject: [PATCH] fix(AI,Web): Optimize the experience --- Services/AI/AiService.cs | 38 ++++++++++++++++++-------- Web/src/layouts/components/Header.tsx | 2 +- Web/src/layouts/components/Sidebar.tsx | 2 +- 3 files changed, 28 insertions(+), 14 deletions(-) diff --git a/Services/AI/AiService.cs b/Services/AI/AiService.cs index fa0947e..1dba755 100644 --- a/Services/AI/AiService.cs +++ b/Services/AI/AiService.cs @@ -17,13 +17,27 @@ public class AiService(IHttpClientFactory httpClientFactory, IConfigService conf string apiKey = configService["AI:ApiKey"]; string baseUrl = configService["AI:ApiEndpoint"]; + if (string.IsNullOrWhiteSpace(apiKey)) + { + throw new InvalidOperationException("AI API Key 未配置或为空。请检查配置文件中的 AI:ApiKey 设置。"); + } + + if (string.IsNullOrWhiteSpace(baseUrl)) + { + throw new InvalidOperationException("AI API Endpoint 未配置或为空。请检查配置文件中的 AI:ApiEndpoint 设置。"); + } + + if (!Uri.TryCreate(baseUrl, UriKind.Absolute, out Uri? validUri)) + { + throw new InvalidOperationException($"AI API Endpoint 格式无效: {baseUrl}。请提供有效的 URL。"); + } + if (_httpClient == null || _currentApiKey != apiKey || _currentBaseUrl != baseUrl) { + _httpClient?.Dispose(); _httpClient = httpClientFactory.CreateClient(); - _httpClient.BaseAddress = new Uri(baseUrl); + _httpClient.BaseAddress = validUri; _httpClient.DefaultRequestHeaders.Authorization = new AuthenticationHeaderValue("Bearer", apiKey); - - // 更新当前配置记录 _currentApiKey = apiKey; _currentBaseUrl = baseUrl; } @@ -51,7 +65,7 @@ public class AiService(IHttpClientFactory httpClientFactory, IConfigService conf var textContent = new TextContent { Type = "text", - Text = configService["AI:ImageAnalysisPrompt"] ?? + Text = configService["AI:ImageAnalysisPrompt"] ?? "请详细分析这张图片,并提供全面的描述,以便用于向量嵌入和基于文本的图像搜索。描述需要包含:主体对象、场景环境、色彩特点、构图布局、风格特征、情绪氛围、细节特征等关键元素。请提供一个简短有力的标题,然后提供详细描述。\n\n请以JSON格式返回,格式如下:\n{\"title\": \"简短概括图片的核心内容\", \"description\": \"全面详细的描述,包含上述所有元素,使用丰富精确的词汇,避免笼统表达\"}\n\n请确保返回有效的JSON格式。" }; @@ -102,15 +116,15 @@ public class AiService(IHttpClientFactory httpClientFactory, IConfigService conf string model = configService["AI:Model"]; var tagsText = string.Join(", ", availableTags); - - string promptTemplate = configService["AI:TagMatchingPrompt"] ?? + + string promptTemplate = configService["AI:TagMatchingPrompt"] ?? "以下是一组标签:[{tagsText}]。\n\n请从这些标签中严格选择与下面描述内容高度相关的标签(最多选择5个)。只选择确实匹配的标签,如果找不到完全匹配或高度相关的标签,宁可返回空数组也不要选择不太相关的标签。\n\n描述内容:{description}\n\n请以JSON格式返回,格式如下:\n{{\"tags\": [\"标签1\", \"标签2\", \"标签3\"]}}\n\n请确保返回有效的JSON格式前面不要加```,并且只包含确实匹配的标签名称。"; - + // 替换占位符 string promptText = promptTemplate .Replace("{tagsText}", tagsText) .Replace("{description}", description); - + var textContent = new TextContent { Type = "text", @@ -232,9 +246,9 @@ public class AiService(IHttpClientFactory httpClientFactory, IConfigService conf if (allowNewTags) { // 获取配置的标签生成提示词,如果没有则使用默认提示词 - string defaultPrompt = configService["AI:TagGenerationPrompt"] ?? + string defaultPrompt = configService["AI:TagGenerationPrompt"] ?? "请为图片生成5个最相关的标签,每个标签应该是简短且描述性的词语或短语。\n\n请以JSON格式返回,格式如下:\n{\"tags\": [\"标签1\", \"标签2\", \"标签3\", \"标签4\", \"标签5\"]}\n\n请确保返回有效的JSON格式。"; - + // 如果允许新标签,则提供现有标签作为参考,但允许生成新标签 promptText = availableTags.Count > 0 ? $"可以参考这些现有标签:[{string.Join(", ", availableTags)}],但也可以生成其他与图片内容相关的新标签。\n\n{defaultPrompt}" @@ -247,9 +261,9 @@ public class AiService(IHttpClientFactory httpClientFactory, IConfigService conf return new List(); var tagsText = string.Join(", ", availableTags); - string templatePrompt = configService["AI:TagMatchingPrompt"] ?? + string templatePrompt = configService["AI:TagMatchingPrompt"] ?? "以下是一组标签:[{tagsText}]。\n\n请从这些标签中严格选择与图片内容高度相关的标签(最多选择5个)。只选择确实匹配的标签,如果找不到完全匹配或高度相关的标签,宁可返回空数组也不要选择不太相关的标签。\n\n请以JSON格式返回,格式如下:\n{{\"tags\": [\"标签1\", \"标签2\", \"标签3\"]}}\n\n请确保返回有效的JSON格式,并且只包含上述列表中的标签名称。"; - + promptText = templatePrompt.Replace("{tagsText}", tagsText); } diff --git a/Web/src/layouts/components/Header.tsx b/Web/src/layouts/components/Header.tsx index 2da6e7c..27f7e9e 100644 --- a/Web/src/layouts/components/Header.tsx +++ b/Web/src/layouts/components/Header.tsx @@ -13,7 +13,7 @@ import { Link, useNavigate, useLocation } from 'react-router'; import { useAuth } from '../../auth/AuthContext'; import { getMainRoutes, getAdminRoutes, type RouteConfig } from '../../routes'; import UserAvatar from '../../components/UserAvatar'; -import { UserRole } from '../../api/types'; +import { UserRole } from '../../api'; import SearchDialog from '../../components/search/SearchDialog'; import useIsMobile from '../../hooks/useIsMobile'; diff --git a/Web/src/layouts/components/Sidebar.tsx b/Web/src/layouts/components/Sidebar.tsx index 903acdf..0dd009b 100644 --- a/Web/src/layouts/components/Sidebar.tsx +++ b/Web/src/layouts/components/Sidebar.tsx @@ -181,7 +181,7 @@ const Sidebar: React.FC = ({ collapsed, isMobile = false, onClose, height: isMobile ? '56px' : '64px', display: 'flex', alignItems: 'center', - justifyContent: collapsed ? 'center' : 'flex-start', + justifyContent: 'center', padding: collapsed ? '0' : '0 20px', color: '#001529', fontWeight: 'bold',