diff --git a/plugin/ai/client.go b/plugin/ai/client.go index f9418e9e7..236a8203d 100644 --- a/plugin/ai/client.go +++ b/plugin/ai/client.go @@ -11,7 +11,7 @@ import ( "github.com/openai/openai-go/v2" "github.com/openai/openai-go/v2/option" - + storepb "github.com/usememos/memos/proto/gen/store" ) @@ -51,9 +51,9 @@ func LoadConfigFromEnv() *Config { TimeoutSeconds: timeoutSeconds, } - // Enable AI if all required fields are provided - config.Enabled = config.BaseURL != "" && config.APIKey != "" && config.Model != "" - + // Enable AI if all required fields are provided (API Key is optional for local services like Ollama) + config.Enabled = config.BaseURL != "" && config.Model != "" + return config } @@ -81,7 +81,7 @@ func LoadConfigFromDatabase(aiSetting *storepb.WorkspaceAISetting) *Config { // Environment variables take precedence if they are set func (c *Config) MergeWithEnv() *Config { envConfig := LoadConfigFromEnv() - + // Start with current config merged := &Config{ Enabled: c.Enabled, @@ -90,7 +90,7 @@ func (c *Config) MergeWithEnv() *Config { Model: c.Model, TimeoutSeconds: c.TimeoutSeconds, } - + // Override with env vars if they are set if envConfig.BaseURL != "" { merged.BaseURL = envConfig.BaseURL @@ -104,16 +104,16 @@ func (c *Config) MergeWithEnv() *Config { if os.Getenv("AI_TIMEOUT_SECONDS") != "" { merged.TimeoutSeconds = envConfig.TimeoutSeconds } - - // Enable if all required fields are present - merged.Enabled = merged.BaseURL != "" && merged.APIKey != "" && merged.Model != "" - + + // Enable if all required fields are present (API Key is optional for local services like Ollama) + merged.Enabled = merged.BaseURL != "" && merged.Model != "" + return merged } // IsConfigured returns true if AI is properly configured func (c *Config) IsConfigured() bool { - return c.Enabled && c.BaseURL != "" && c.APIKey != "" && c.Model != "" + return c.Enabled && c.BaseURL != "" && c.Model != "" } // Client wraps OpenAI client with convenience methods diff --git a/server/router/api/v1/workspace_service.go b/server/router/api/v1/workspace_service.go index e2f6b87f7..ee0bf3a28 100644 --- a/server/router/api/v1/workspace_service.go +++ b/server/router/api/v1/workspace_service.go @@ -397,12 +397,6 @@ func (s *APIV1Service) TestAiConnection(ctx context.Context, request *v1pb.TestA Message: "Base URL is required", }, nil } - if request.ApiKey == "" { - return &v1pb.TestAiConnectionResponse{ - Success: false, - Message: "API Key is required", - }, nil - } if request.Model == "" { return &v1pb.TestAiConnectionResponse{ Success: false, diff --git a/web/src/components/Settings/AISettings.tsx b/web/src/components/Settings/AISettings.tsx index 42af19fae..04ddb6494 100644 --- a/web/src/components/Settings/AISettings.tsx +++ b/web/src/components/Settings/AISettings.tsx @@ -48,8 +48,8 @@ const AISettings = observer(() => { }; const updateSetting = async () => { - if (aiSetting.enableAi && (!aiSetting.apiKey || !aiSetting.model)) { - toast.error(t("setting.ai-section.api-key-model-required")); + if (aiSetting.enableAi && !aiSetting.model) { + toast.error(t("setting.ai-section.model-required")); return; } @@ -65,7 +65,7 @@ const AISettings = observer(() => { const resetSetting = () => setAiSetting(originalSetting); const testConnection = async () => { - if (!aiSetting.baseUrl || !aiSetting.apiKey || !aiSetting.model) { + if (!aiSetting.baseUrl || !aiSetting.model) { toast.error(t("setting.ai-section.test-connection-incomplete")); return; } @@ -222,7 +222,7 @@ const AISettings = observer(() => { diff --git a/web/src/locales/en.json b/web/src/locales/en.json index 491a9c5a7..e4efcd3ff 100644 --- a/web/src/locales/en.json +++ b/web/src/locales/en.json @@ -331,7 +331,7 @@ "base-url": "Base URL", "base-url-description": "API endpoint for your AI service (e.g., https://api.openai.com/v1)", "api-key": "API Key", - "api-key-description": "Authentication key for your AI service", + "api-key-description": "Authentication key for your AI service (optional for local services like Ollama)", "model": "Model", "model-description": "AI model to use for processing requests (e.g., gpt-4o, claude-3-5-sonnet-20241022)", "timeout": "Timeout (seconds)", @@ -341,9 +341,8 @@ "test-connection-description": "Verify that your AI service configuration is working", "test-connection-success": "Connection successful! AI service is properly configured.", "test-connection-failed": "Connection failed. Please check your configuration.", - "test-connection-incomplete": "Please fill in all required fields before testing.", - "fields-required": "All fields are required when AI is enabled.", - "api-key-model-required": "API key and model are required when AI is enabled." + "test-connection-incomplete": "Please fill in Base URL and Model before testing.", + "model-required": "Model is required when AI is enabled." }, "tag-recommendation": { "title": "Tag Recommendation", diff --git a/web/src/locales/zh-Hans.json b/web/src/locales/zh-Hans.json index be602819f..3d1e1db9d 100644 --- a/web/src/locales/zh-Hans.json +++ b/web/src/locales/zh-Hans.json @@ -329,7 +329,7 @@ "base-url": "基础 URL", "base-url-description": "AI 服务的 API 端点(例如:https://api.openai.com/v1)", "api-key": "API 密钥", - "api-key-description": "AI 服务的身份验证密钥", + "api-key-description": "AI 服务的身份验证密钥(本地服务如 Ollama 可选)", "model": "模型", "model-description": "用于处理请求的 AI 模型(例如:gpt-4o, claude-3-5-sonnet-20241022)", "timeout": "超时(秒)", @@ -339,9 +339,8 @@ "test-connection-description": "验证您的 AI 服务配置是否正常工作", "test-connection-success": "连接成功!AI 服务配置正确。", "test-connection-failed": "连接失败。请检查您的配置。", - "test-connection-incomplete": "请在测试前填写所有必填字段。", - "fields-required": "启用 AI 时,所有字段都是必填的。", - "api-key-model-required": "启用 AI 时,API 密钥和模型是必填的。" + "test-connection-incomplete": "请在测试前填写基础 URL 和模型。", + "model-required": "启用 AI 时,模型是必填的。" }, "sso": "单点登录", "sso-section": {