This is an automated email from the ASF dual-hosted git repository.

zhengqiwei pushed a commit to branch master
in repository https://gitbox.apache.org/repos/asf/hertzbeat.git


The following commit(s) were added to refs/heads/master by this push:
     new e073bfee9 refactor: refactor ai feature (#3823)
e073bfee9 is described below

commit e073bfee91f1b57bc959b13bfb0607ebd04e0161
Author: Tomsun28 <[email protected]>
AuthorDate: Sat Oct 25 12:39:53 2025 +0800

    refactor: refactor ai feature (#3823)
    
    Signed-off-by: tomsun28 <[email protected]>
    Co-authored-by: Calvin <[email protected]>
---
 hertzbeat-ai-agent/pom.xml                         |  16 +-
 .../ai/agent/config/DynamicOpenAiApiKey.java       |  43 +++--
 .../hertzbeat/ai/agent/config/LlmConfig.java       | 137 +++++++++++---
 .../ai/agent/config/OpenAiYamlConfig.java          |  43 -----
 .../agent/controller/OpenAiConfigController.java   | 160 -----------------
 .../hertzbeat/ai/agent/dao/OpenAiConfigDao.java    |  37 ----
 ...enAiConfigDto.java => ModelProviderConfig.java} |  40 +++--
 ...enAiConfigService.java => AiConfigService.java} |  46 +----
 .../agent/service/ChatClientProviderService.java   |   8 +-
 .../ai/agent/service/impl/AiConfigServiceImpl.java | 100 +++++++++++
 .../impl/ChatClientProviderServiceImpl.java        |  36 +++-
 .../service/impl/ConversationServiceImpl.java      |  12 +-
 .../service/impl/OpenAiConfigServiceImpl.java      | 200 ---------------------
 .../common/constants/GeneralConfigTypeEnum.java    |   7 +-
 .../support/event/AiProviderConfigChangeEvent.java |  10 +-
 .../controller/GeneralConfigController.java        |   4 +-
 .../impl/ModelProviderConfigServiceImpl.java       |  69 +++++++
 .../src/main/resources/application-test.yml        |  13 --
 .../src/main/resources/application.yml             |  21 ---
 home/docs/download.md                              |   2 +-
 .../current/download.md                            |   2 +-
 .../hertzbeat-mysql-iotdb/conf/application.yml     |  10 +-
 .../hertzbeat-mysql-tdengine/conf/application.yml  |   8 -
 .../conf/application.yml                           |  10 +-
 .../conf/application.yml                           |  10 +-
 web-app/src/app/layout/basic/basic.component.html  |  30 ----
 web-app/src/app/layout/basic/basic.component.less  |   8 +-
 web-app/src/app/layout/basic/basic.component.ts    |   2 +-
 web-app/src/app/pojo/ModelProviderConfig.ts        |  57 ++++++
 web-app/src/app/service/general-config.service.ts  |   9 +
 web-app/src/app/service/openai-config.service.ts   |  58 ------
 .../shared/components/ai-bot/ai-bot.component.html |  67 -------
 .../shared/components/ai-bot/ai-bot.component.less | 147 ---------------
 .../shared/components/ai-bot/ai-bot.component.scss | 158 ----------------
 .../shared/components/ai-bot/ai-bot.component.ts   |  72 --------
 .../shared/components/ai-chat/ai-chat.module.ts    |   2 +
 .../shared/components/ai-chat/chat.component.html  |  62 ++++++-
 .../shared/components/ai-chat/chat.component.less  |  35 ++++
 .../shared/components/ai-chat/chat.component.ts    | 158 +++++++++-------
 web-app/src/app/shared/shared.module.ts            |   2 -
 40 files changed, 659 insertions(+), 1252 deletions(-)

diff --git a/hertzbeat-ai-agent/pom.xml b/hertzbeat-ai-agent/pom.xml
index b9d06e59c..9bb22303a 100644
--- a/hertzbeat-ai-agent/pom.xml
+++ b/hertzbeat-ai-agent/pom.xml
@@ -26,7 +26,7 @@
        <artifactId>hertzbeat-ai-agent</artifactId>
        <version>${hertzbeat.version}</version>
        <properties>
-               <spring-ai.version>1.0.1</spring-ai.version>
+               <spring-ai.version>1.0.3</spring-ai.version>
                <java.version>17</java.version>
        </properties>
 
@@ -51,12 +51,22 @@
                </dependency>
                <dependency>
                        <groupId>org.springframework.ai</groupId>
-                       <artifactId>spring-ai-starter-model-openai</artifactId>
+            <artifactId>spring-ai-openai</artifactId>
                </dependency>
+        <dependency>
+            <groupId>org.springframework.ai</groupId>
+            <artifactId>spring-ai-client-chat</artifactId>
+        </dependency>
                <dependency>
                        <groupId>org.apache.hertzbeat</groupId>
                        <artifactId>hertzbeat-common</artifactId>
                </dependency>
+        <!-- common -->
+        <dependency>
+            <groupId>org.apache.hertzbeat</groupId>
+            <artifactId>hertzbeat-base</artifactId>
+            <scope>provided</scope>
+        </dependency>
                <dependency>
                        <groupId>org.apache.hertzbeat</groupId>
                        <artifactId>hertzbeat-alerter</artifactId>
@@ -90,4 +100,4 @@
                </plugins>
        </build>
 
-</project>
\ No newline at end of file
+</project>
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/DynamicOpenAiApiKey.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/DynamicOpenAiApiKey.java
index 2eaa6b912..bad25be51 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/DynamicOpenAiApiKey.java
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/DynamicOpenAiApiKey.java
@@ -18,40 +18,39 @@
 package org.apache.hertzbeat.ai.agent.config;
 
 import lombok.extern.slf4j.Slf4j;
-import org.apache.hertzbeat.ai.agent.pojo.dto.OpenAiConfigDto;
-import org.apache.hertzbeat.ai.agent.service.OpenAiConfigService;
+import org.apache.hertzbeat.ai.agent.pojo.dto.ModelProviderConfig;
+import org.apache.hertzbeat.base.dao.GeneralConfigDao;
+import org.apache.hertzbeat.common.entity.manager.GeneralConfig;
+import org.apache.hertzbeat.common.util.JsonUtil;
+import org.jetbrains.annotations.NotNull;
 import org.springframework.ai.model.ApiKey;
 import org.springframework.stereotype.Component;
 
 /**
- * Dynamic OpenAI API Key implementation that retrieves the API key
- * from our configuration service (database first, then YAML fallback)
+ * Dynamic LLM Provider API Key implementation that retrieves the API key
  */
 @Slf4j
 @Component
 public class DynamicOpenAiApiKey implements ApiKey {
 
-    private final OpenAiConfigService openAiConfigService;
-
-    public DynamicOpenAiApiKey(OpenAiConfigService openAiConfigService) {
-        this.openAiConfigService = openAiConfigService;
+    private final GeneralConfigDao generalConfigDao;
+    
+    public DynamicOpenAiApiKey(GeneralConfigDao generalConfigDao) {
+        this.generalConfigDao = generalConfigDao;
     }
 
+    @NotNull
     @Override
     public String getValue() {
-        try {
-            OpenAiConfigDto effectiveConfig = 
openAiConfigService.getEffectiveConfig();
-            
-            if (effectiveConfig != null && effectiveConfig.isEnable() && 
effectiveConfig.getApiKey() != null) {
-                log.debug("Retrieved OpenAI API key from configuration 
service");
-                return effectiveConfig.getApiKey();
-            } else {
-                log.warn("No valid OpenAI API key found in configuration");
-                return null;
-            }
-        } catch (Exception e) {
-            log.error("Error retrieving OpenAI API key from configuration", e);
-            return null;
+        GeneralConfig providerConfig = generalConfigDao.findByType("provider");
+        ModelProviderConfig modelProviderConfig = 
JsonUtil.fromJson(providerConfig.getContent(), ModelProviderConfig.class);
+
+        if (modelProviderConfig != null && modelProviderConfig.isEnable() && 
modelProviderConfig.isStatus()) {
+            log.debug("Retrieved {} API key from configuration service", 
modelProviderConfig.getCode());
+            return modelProviderConfig.getApiKey();
+        } else {
+            log.warn("No valid LLM Provider API key found in configuration");
+            return "";
         }
     }
-}
\ No newline at end of file
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/LlmConfig.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/LlmConfig.java
index cfb3ecf52..7f8c65733 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/LlmConfig.java
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/LlmConfig.java
@@ -18,59 +18,144 @@
 
 package org.apache.hertzbeat.ai.agent.config;
 
+import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.common.support.event.AiProviderConfigChangeEvent;
+import org.apache.hertzbeat.ai.agent.pojo.dto.ModelProviderConfig;
+import org.apache.hertzbeat.base.dao.GeneralConfigDao;
+import org.apache.hertzbeat.common.entity.manager.GeneralConfig;
+import org.apache.hertzbeat.common.util.JsonUtil;
 import org.springframework.ai.chat.client.ChatClient;
 import org.springframework.ai.openai.OpenAiChatModel;
 import org.springframework.ai.openai.OpenAiChatOptions;
 import org.springframework.ai.openai.api.OpenAiApi;
-import org.springframework.beans.factory.annotation.Value;
+import org.springframework.beans.factory.support.DefaultListableBeanFactory;
+import org.springframework.context.ApplicationContext;
+import org.springframework.context.ConfigurableApplicationContext;
 import org.springframework.context.annotation.Bean;
 import org.springframework.context.annotation.Configuration;
+import org.springframework.context.event.EventListener;
 
 /**
  * Configuration class for Large Language Model (LLM) settings.
  */
 
 @Configuration
+@Slf4j
 public class LlmConfig {
 
-    @Value("${spring.ai.openai.chat.options.model}")
-    private String model;
+    private final GeneralConfigDao generalConfigDao;
+    
+    private ApplicationContext applicationContext;
 
-    /**
-     * Create OpenAI API instance with dynamic API key
-     */
-    @Bean
-    public OpenAiApi openAiApi(DynamicOpenAiApiKey dynamicApiKey) {
-        return OpenAiApi.builder()
-                .apiKey(dynamicApiKey)
-                .build();
+    public LlmConfig(GeneralConfigDao generalConfigDao, ApplicationContext 
applicationContext) {
+        this.generalConfigDao = generalConfigDao;
+        this.applicationContext = applicationContext;
     }
 
     /**
-     * Create OpenAI Chat Options with custom settings
+     * Create ChatClient bean with all dependencies created internally
      */
     @Bean
-    public OpenAiChatOptions openAiChatOptions() {
-        return OpenAiChatOptions.builder()
-                .model(model)
-                .temperature(0.3)
-                .build();
+    public ChatClient openAiChatClient() {
+        return createChatClient();
     }
 
     /**
-     * Create OpenAI Chat Model with custom API configuration
+     * Create ChatClient with all necessary components
      */
-    @Bean
-    public OpenAiChatModel openAiChatModel(OpenAiApi openAiApi, 
OpenAiChatOptions openAiChatOptions) {
-        return OpenAiChatModel.builder()
-                .openAiApi(openAiApi)
+    private ChatClient createChatClient() {
+
+        GeneralConfig providerConfig = generalConfigDao.findByType("provider");
+        if (providerConfig == null || providerConfig.getContent() == null) {
+            log.warn("LLM Provider is not set, ChatClient bean will not be 
created");
+            return null;
+        }
+        ModelProviderConfig modelProviderConfig = 
JsonUtil.fromJson(providerConfig.getContent(), ModelProviderConfig.class);
+
+        if (!modelProviderConfig.isEnable() || 
!modelProviderConfig.isStatus()) {
+            log.warn("LLM Provider is not enabled or status is not valid, 
ChatClient bean will not be created");
+            return null;
+        }
+
+        if (modelProviderConfig.getApiKey() == null) {
+            log.warn("LLM Provider configuration is incomplete, ChatClient 
bean will not be created");
+            return null;
+        }
+
+        if (modelProviderConfig.getBaseUrl() == null) {
+            if ("openai".equals(modelProviderConfig.getCode())) {
+                modelProviderConfig.setBaseUrl("https://api.openai.com/v1";);
+            } else if ("zhipu".equals(modelProviderConfig.getCode())) {
+                
modelProviderConfig.setBaseUrl("https://open.bigmodel.cn/api/paas/v4";);
+            } else if ("zai".equals(modelProviderConfig.getCode())) {
+                modelProviderConfig.setBaseUrl("https://api.z.ai/api/paas/v4";);
+            } else {
+                modelProviderConfig.setBaseUrl("https://api.openai.com/v1";);
+            }
+        }
+        
+        if (modelProviderConfig.getModel() == null) {
+            if ("openai".equals(modelProviderConfig.getCode())) {
+                modelProviderConfig.setModel("gpt-5");
+            } else if ("zhipu".equals(modelProviderConfig.getCode())) {
+                modelProviderConfig.setModel("glm-4.6");
+            } else if ("zai".equals(modelProviderConfig.getCode())) {
+                modelProviderConfig.setModel("glm-4.6");
+            } else {
+                modelProviderConfig.setModel("gpt-5");
+            }
+        }
+
+        OpenAiApi.Builder builder = new OpenAiApi.Builder();
+        builder.baseUrl(modelProviderConfig.getBaseUrl());
+        builder.apiKey(modelProviderConfig.getApiKey());
+        builder.completionsPath("/chat/completions");
+        
+        // Create Chat Options
+        OpenAiChatOptions openAiChatOptions = OpenAiChatOptions.builder()
+                .model(modelProviderConfig.getModel())
+                .temperature(0.3)
+                .build();
+        
+        // Create Chat Model
+        OpenAiChatModel openAiChatModel = OpenAiChatModel.builder()
+                .openAiApi(builder.build())
                 .defaultOptions(openAiChatOptions)
                 .build();
+        
+        // Create and return ChatClient
+        return ChatClient.create(openAiChatModel);
     }
 
-    @Bean
-    public ChatClient openAiChatClient(OpenAiChatModel openAiChatModel) {
-        return ChatClient.create(openAiChatModel);
+    /**
+     * AI configuration change event listener
+     * Uses ApplicationContext to unregister and re-register the ChatClient 
bean
+     */
+    @EventListener(AiProviderConfigChangeEvent.class)
+    public void onAiProviderConfigChange(AiProviderConfigChangeEvent event) {
+        log.info("Provider configuration change event received, refreshing 
ChatClient bean");
+        
+        try {
+            ConfigurableApplicationContext configurableContext = 
(ConfigurableApplicationContext) applicationContext;
+            DefaultListableBeanFactory beanFactory = 
(DefaultListableBeanFactory) configurableContext.getBeanFactory();
+            
+            // Remove the existing ChatClient bean
+            if (beanFactory.containsSingleton("openAiChatClient")) {
+                beanFactory.destroySingleton("openAiChatClient");
+                log.info("Existing ChatClient bean destroyed");
+            }
+                        
+            // Create new ChatClient with updated configuration
+            ChatClient newChatClient = createChatClient();
+            
+            // Register the new ChatClient bean
+            beanFactory.registerSingleton("openAiChatClient", newChatClient);
+            
+            log.info("ChatClient bean refreshed successfully with new AI 
provider configuration");
+            
+        } catch (Exception e) {
+            log.error("Failed to refresh ChatClient bean after configuration 
change", e);
+        }
     }
 
-}
\ No newline at end of file
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/OpenAiYamlConfig.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/OpenAiYamlConfig.java
deleted file mode 100644
index 3a0bf507d..000000000
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/config/OpenAiYamlConfig.java
+++ /dev/null
@@ -1,43 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hertzbeat.ai.agent.config;
-
-import lombok.Data;
-import org.springframework.boot.context.properties.ConfigurationProperties;
-import org.springframework.stereotype.Component;
-
-/**
- * OpenAI YAML Configuration - reads from spring.ai.openai.api-key
- */
-@Data
-@Component
-@ConfigurationProperties(prefix = "spring.ai.openai")
-public class OpenAiYamlConfig {
-
-    /**
-     * OpenAI API key from spring.ai.openai.api-key
-     */
-    private String apiKey;
-
-    /**
-     * Check if OpenAI is enabled (has API key)
-     */
-    public boolean isEnable() {
-        return apiKey != null && !apiKey.trim().isEmpty();
-    }
-}
\ No newline at end of file
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/controller/OpenAiConfigController.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/controller/OpenAiConfigController.java
deleted file mode 100644
index 195febf59..000000000
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/controller/OpenAiConfigController.java
+++ /dev/null
@@ -1,160 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hertzbeat.ai.agent.controller;
-
-import io.swagger.v3.oas.annotations.Operation;
-import io.swagger.v3.oas.annotations.tags.Tag;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.hertzbeat.ai.agent.pojo.dto.OpenAiConfigDto;
-import org.apache.hertzbeat.ai.agent.service.OpenAiConfigService;
-import org.springframework.http.ResponseEntity;
-
-import jakarta.validation.Valid;
-import org.springframework.web.bind.annotation.PostMapping;
-import org.springframework.web.bind.annotation.GetMapping;
-import org.springframework.web.bind.annotation.RequestBody;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
-
-import java.util.HashMap;
-import java.util.Map;
-
-import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
-
-/**
- * OpenAI Configuration API
- */
-@RestController
-@RequestMapping(value = "/api/ai-agent/config", produces = 
{APPLICATION_JSON_VALUE})
-@Tag(name = "OpenAI Configuration API")
-@Slf4j
-public class OpenAiConfigController {
-
-    private final OpenAiConfigService openAiConfigService;
-
-    public OpenAiConfigController(OpenAiConfigService openAiConfigService) {
-        this.openAiConfigService = openAiConfigService;
-    }
-
-    @PostMapping("/openai")
-    @Operation(summary = "Save OpenAI configuration", description = "Save or 
update OpenAI configuration")
-    public ResponseEntity<Map<String, Object>> saveOpenAiConfig(@Valid 
@RequestBody OpenAiConfigDto config) {
-        try {
-            Map<String, Object> response = new HashMap<>();
-            
-            // Validate API key if enabled
-            if (config.isEnable() && config.getApiKey() != null && 
!config.getApiKey().trim().isEmpty()) {
-                OpenAiConfigService.ValidationResult validationResult = 
openAiConfigService.validateApiKey(config.getApiKey());
-                
-                if (!validationResult.isValid()) {
-                    log.warn("API key validation failed during save: {}", 
validationResult.getMessage());
-                    response.put("code", 1);
-                    response.put("msg", "API key validation failed: " + 
validationResult.getMessage());
-                    return ResponseEntity.ok(response);
-                }
-                log.info("API key validation successful during save");
-            }
-            
-            // Save the configuration
-            openAiConfigService.saveConfig(config);
-            
-            response.put("code", 0);
-            response.put("msg", "OpenAI configuration saved successfully");
-            
-            return ResponseEntity.ok(response);
-        } catch (Exception e) {
-            log.error("Failed to save OpenAI configuration", e);
-            
-            Map<String, Object> response = new HashMap<>();
-            response.put("code", 1);
-            response.put("msg", "Failed to save configuration: " + 
e.getMessage());
-            
-            return ResponseEntity.ok(response);
-        }
-    }
-
-    @GetMapping("/openai")
-    @Operation(summary = "Get OpenAI configuration", description = "Get 
current OpenAI configuration")
-    public ResponseEntity<Map<String, Object>> getOpenAiConfig() {
-        try {
-            OpenAiConfigDto config = openAiConfigService.getConfig();
-            
-            Map<String, Object> response = new HashMap<>();
-            response.put("code", 0);
-            response.put("data", config);
-            response.put("msg", "Success");
-            
-            return ResponseEntity.ok(response);
-        } catch (Exception e) {
-            log.error("Failed to get OpenAI configuration", e);
-            
-            Map<String, Object> response = new HashMap<>();
-            response.put("code", 1);
-            response.put("msg", "Failed to get configuration: " + 
e.getMessage());
-            
-            return ResponseEntity.ok(response);
-        }
-    }
-
-    @GetMapping("/openai/status")
-    @Operation(summary = "Check OpenAI configuration status", description = 
"Check if OpenAI is properly configured")
-    public ResponseEntity<Map<String, Object>> getOpenAiConfigStatus() {
-        try {
-            boolean configured = openAiConfigService.isConfigured();
-            OpenAiConfigDto effectiveConfig = 
openAiConfigService.getEffectiveConfig();
-            boolean hasDbConfig = openAiConfigService.getConfig() != null;
-            boolean hasYamlConfig = effectiveConfig != null && !hasDbConfig;
-            
-            // Validate the effective configuration
-            boolean validationPassed = false;
-            String validationMessage = "No configuration found";
-            
-            if (effectiveConfig != null && effectiveConfig.isEnable() && 
effectiveConfig.getApiKey() != null && 
!effectiveConfig.getApiKey().trim().isEmpty()) {
-                OpenAiConfigService.ValidationResult validationResult = 
openAiConfigService.validateApiKey(effectiveConfig.getApiKey());
-                validationPassed = validationResult.isValid();
-                validationMessage = validationResult.getMessage();
-                
-                if (!validationPassed) {
-                    log.warn("OpenAI API key validation failed during status 
check: {}", validationMessage);
-                }
-            }
-            
-            Map<String, Object> response = new HashMap<>();
-            response.put("code", 0);
-            response.put("data", Map.of(
-                "configured", configured && validationPassed,
-                "hasDbConfig", hasDbConfig,
-                "hasYamlConfig", hasYamlConfig,
-                "validationPassed", validationPassed,
-                "validationMessage", validationMessage
-            ));
-            response.put("msg", "Success");
-            
-            return ResponseEntity.ok(response);
-        } catch (Exception e) {
-            log.error("Failed to get OpenAI configuration status", e);
-            
-            Map<String, Object> response = new HashMap<>();
-            response.put("code", 1);
-            response.put("msg", "Failed to get status: " + e.getMessage());
-            
-            return ResponseEntity.ok(response);
-        }
-    }
-
-}
\ No newline at end of file
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/dao/OpenAiConfigDao.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/dao/OpenAiConfigDao.java
deleted file mode 100644
index b1ba96da2..000000000
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/dao/OpenAiConfigDao.java
+++ /dev/null
@@ -1,37 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hertzbeat.ai.agent.dao;
-
-import org.apache.hertzbeat.ai.agent.entity.OpenAiConfig;
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.JpaSpecificationExecutor;
-import org.springframework.stereotype.Repository;
-
-/**
- * OpenAI Agent Configuration Dao
- */
-@Repository
-public interface OpenAiConfigDao extends JpaRepository<OpenAiConfig, String>, 
JpaSpecificationExecutor<OpenAiConfig> {
-    
-    /**
-     * Query by type
-     * @param type type
-     * @return Return the queried configuration information
-     */
-    OpenAiConfig findByType(String type);
-}
\ No newline at end of file
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/pojo/dto/OpenAiConfigDto.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/pojo/dto/ModelProviderConfig.java
similarity index 57%
rename from 
hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/pojo/dto/OpenAiConfigDto.java
rename to 
hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/pojo/dto/ModelProviderConfig.java
index c443e4b49..af96cfd03 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/pojo/dto/OpenAiConfigDto.java
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/pojo/dto/ModelProviderConfig.java
@@ -24,24 +24,36 @@ import lombok.Data;
 import lombok.NoArgsConstructor;
 
 /**
- * OpenAI Configuration DTO - simplified to handle only API key
+ * Model Provider Configuration
  */
 @Data
 @AllArgsConstructor
 @NoArgsConstructor
-@Schema(description = "OpenAI configuration")
-public class OpenAiConfigDto {
-
-    /**
-     * Whether to enable OpenAI, default is false
-     */
-    @Schema(title = "Enable OpenAI", description = "Whether OpenAI is 
enabled", example = "true")
+@Schema(description = "LLM Model Provider configuration")
+public class ModelProviderConfig {
+    
+    @Schema(title = "Enable Provider", description = "Whether Provider is 
enabled", example = "true")
     private boolean enable = false;
-
-    /**
-     * OpenAI API key
-     */
-    @Schema(title = "API Key", description = "OpenAI API key", example = 
"sk-...")
+    
+    @Schema(title = "Check the provider available status")
+    private boolean status = false;
+    
+    @Schema(title = "The error message when provider status check failed")
+    private String error;
+    
+    @Schema(title = "Model type, text-generate, vision")
+    private String type;
+    
+    @Schema(title = "Model Provider code, like openai, zai, bigmodel")
+    private String code;
+    
+    @Schema(title = "custom the provider server base url")
+    private String baseUrl;
+    
+    @Schema(title = "use the model id name, eg: gpt-5, glm-4.6")
+    private String model;
+    
+    @Schema(title = "API Key", description = "API key", example = "sk-...")
     @NotBlank(message = "API Key cannot be empty when enabled")
     private String apiKey;
-}
\ No newline at end of file
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/OpenAiConfigService.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/AiConfigService.java
similarity index 59%
rename from 
hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/OpenAiConfigService.java
rename to 
hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/AiConfigService.java
index 1d15c46d5..796c46986 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/OpenAiConfigService.java
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/AiConfigService.java
@@ -17,38 +17,10 @@
 
 package org.apache.hertzbeat.ai.agent.service;
 
-import org.apache.hertzbeat.ai.agent.event.OpenAiConfigChangeEvent;
-import org.apache.hertzbeat.ai.agent.pojo.dto.OpenAiConfigDto;
-
 /**
- * OpenAI Configuration Service
- * Consolidated service for OpenAI configuration, validation, and client 
factory management
+ * Ai Configuration Service
  */
-public interface OpenAiConfigService {
-
-    /**
-     * Save OpenAI configuration
-     * @param config OpenAI configuration
-     */
-    void saveConfig(OpenAiConfigDto config);
-
-    /**
-     * Get OpenAI configuration
-     * @return OpenAI configuration
-     */
-    OpenAiConfigDto getConfig();
-
-    /**
-     * Check if OpenAI is properly configured
-     * @return true if configured and enabled
-     */
-    boolean isConfigured();
-
-    /**
-     * Get effective OpenAI configuration (DB first, then YAML fallback)
-     * @return effective configuration or null if not configured
-     */
-    OpenAiConfigDto getEffectiveConfig();
+public interface AiConfigService {
 
     /**
      * Validate OpenAI API key by calling the OpenAI API
@@ -57,18 +29,6 @@ public interface OpenAiConfigService {
      */
     ValidationResult validateApiKey(String apiKey);
 
-    /**
-     * Force reload of OpenAI configuration cache
-     * This method is typically called when configuration changes
-     */
-    void reloadConfig();
-
-    /**
-     * Handle OpenAI configuration change events
-     * @param event OpenAI configuration change event
-     */
-    void onOpenAiConfigChange(OpenAiConfigChangeEvent event);
-
     /**
      * Validation result class
      */
@@ -97,4 +57,4 @@ public interface OpenAiConfigService {
             return message;
         }
     }
-}
\ No newline at end of file
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/ChatClientProviderService.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/ChatClientProviderService.java
index 2f65fb4a2..4b6de16f1 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/ChatClientProviderService.java
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/ChatClientProviderService.java
@@ -33,4 +33,10 @@ public interface ChatClientProviderService {
      * @return Flux of string chunks from the LLM response
      */
     Flux<String> streamChat(ChatRequestContext context);
-}
\ No newline at end of file
+
+    /**
+     * Check if provider is properly configured
+     * @return true if configured and enabled
+     */
+    boolean isConfigured();
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/AiConfigServiceImpl.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/AiConfigServiceImpl.java
new file mode 100644
index 000000000..1c4031a68
--- /dev/null
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/AiConfigServiceImpl.java
@@ -0,0 +1,100 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.ai.agent.service.impl;
+
+import lombok.extern.slf4j.Slf4j;
+import org.apache.hertzbeat.ai.agent.service.AiConfigService;
+import org.apache.hertzbeat.base.dao.GeneralConfigDao;
+import org.springframework.http.HttpEntity;
+import org.springframework.http.HttpHeaders;
+import org.springframework.http.HttpMethod;
+import org.springframework.http.HttpStatus;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.util.StringUtils;
+import org.springframework.web.client.RestTemplate;
+
+/**
+ * Ai Configuration Service Implementation
+ */
+@Slf4j
+@Service
+public class AiConfigServiceImpl implements AiConfigService {
+    
+    private static final String OPENAI_MODELS_ENDPOINT = 
"https://api.openai.com/v1/models";;
+    private final RestTemplate restTemplate;
+
+    private final GeneralConfigDao generalConfigDao;
+
+    public AiConfigServiceImpl(GeneralConfigDao generalConfigDao, RestTemplate 
restTemplate) {
+        this.restTemplate = restTemplate;
+        this.generalConfigDao = generalConfigDao;
+    }
+
+    @Override
+    public ValidationResult validateApiKey(String apiKey) {
+        if (!StringUtils.hasText(apiKey)) {
+            return ValidationResult.failure("API key cannot be empty");
+        }
+
+        if (!apiKey.startsWith("sk-")) {
+            return ValidationResult.failure("Invalid API key format. OpenAI 
API keys should start with 'sk-'");
+        }
+
+        try {
+            HttpHeaders headers = new HttpHeaders();
+            headers.set("Authorization", "Bearer " + apiKey);
+            headers.set("Content-Type", "application/json");
+            
+            HttpEntity<String> entity = new HttpEntity<>(headers);
+            
+            log.debug("Validating OpenAI API key by calling models endpoint");
+            ResponseEntity<String> response = restTemplate.exchange(
+                OPENAI_MODELS_ENDPOINT,
+                HttpMethod.GET,
+                entity,
+                String.class
+            );
+
+            if (response.getStatusCode() == HttpStatus.OK) {
+                log.info("OpenAI API key validation successful");
+                return ValidationResult.success("API key is valid");
+            } else {
+                log.warn("OpenAI API key validation failed with status: {}", 
response.getStatusCode());
+                return ValidationResult.failure("API key validation failed: " 
+ response.getStatusCode());
+            }
+
+        } catch (Exception e) {
+            log.error("Error validating OpenAI API key", e);
+            String errorMessage = e.getMessage();
+            
+            // Parse common error messages
+            if (errorMessage.contains("401")) {
+                return ValidationResult.failure("Invalid API key - 
authentication failed");
+            } else if (errorMessage.contains("403")) {
+                return ValidationResult.failure("API key does not have 
permission to access models");
+            } else if (errorMessage.contains("429")) {
+                return ValidationResult.failure("Rate limit exceeded - please 
try again later");
+            } else if (errorMessage.contains("timeout") || 
errorMessage.contains("connect")) {
+                return ValidationResult.failure("Network error - unable to 
connect to OpenAI API");
+            } else {
+                return ValidationResult.failure("API key validation failed: " 
+ errorMessage);
+            }
+        }
+    }
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ChatClientProviderServiceImpl.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ChatClientProviderServiceImpl.java
index 51d73b5b9..d8593a363 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ChatClientProviderServiceImpl.java
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ChatClientProviderServiceImpl.java
@@ -21,7 +21,11 @@ package org.apache.hertzbeat.ai.agent.service.impl;
 import lombok.extern.slf4j.Slf4j;
 import org.apache.hertzbeat.ai.agent.config.PromptProvider;
 import org.apache.hertzbeat.ai.agent.pojo.dto.MessageDto;
+import org.apache.hertzbeat.ai.agent.pojo.dto.ModelProviderConfig;
 import org.apache.hertzbeat.ai.agent.service.ChatClientProviderService;
+import org.apache.hertzbeat.base.dao.GeneralConfigDao;
+import org.apache.hertzbeat.common.entity.manager.GeneralConfig;
+import org.apache.hertzbeat.common.util.JsonUtil;
 import org.springframework.stereotype.Service;
 import org.apache.hertzbeat.ai.agent.pojo.dto.ChatRequestContext;
 import org.springframework.ai.chat.client.ChatClient;
@@ -31,6 +35,7 @@ import org.springframework.ai.chat.messages.UserMessage;
 import org.springframework.ai.tool.ToolCallbackProvider;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.beans.factory.annotation.Qualifier;
+import org.springframework.context.ApplicationContext;
 import reactor.core.publisher.Flux;
 
 import java.util.ArrayList;
@@ -45,19 +50,25 @@ import java.util.List;
 @Service
 public class ChatClientProviderServiceImpl implements 
ChatClientProviderService {
 
-    private final ChatClient chatClient;
+    private final ApplicationContext applicationContext;
+
+    private final GeneralConfigDao generalConfigDao;
 
     @Qualifier("hertzbeatTools")
     @Autowired
     private ToolCallbackProvider toolCallbackProvider;
+    
+    private boolean isConfigured = false;
 
     @Autowired
-    public ChatClientProviderServiceImpl(ChatClient openAiChatClient) {
-        this.chatClient = openAiChatClient;
+    public ChatClientProviderServiceImpl(ApplicationContext 
applicationContext, GeneralConfigDao generalConfigDao) {
+        this.applicationContext = applicationContext;
+        this.generalConfigDao = generalConfigDao;
     }
 
     public String complete(String message) {
-        return this.chatClient.prompt()
+        ChatClient chatClient = applicationContext.getBean("openAiChatClient", 
ChatClient.class);
+        return chatClient.prompt()
                 .user(message)
                 .call()
                 .content();
@@ -66,6 +77,9 @@ public class ChatClientProviderServiceImpl implements 
ChatClientProviderService
     @Override
     public Flux<String> streamChat(ChatRequestContext context) {
         try {
+            // Get the current (potentially refreshed) ChatClient instance
+            ChatClient chatClient = 
applicationContext.getBean("openAiChatClient", ChatClient.class);
+            
             List<Message> messages = new ArrayList<>();
 
             // Add conversation history if available
@@ -83,7 +97,7 @@ public class ChatClientProviderServiceImpl implements 
ChatClientProviderService
 
             log.info("Starting streaming chat for conversation: {}", 
context.getConversationId());
 
-            return this.chatClient.prompt()
+            return chatClient.prompt()
                     .messages(messages)
                     .system(PromptProvider.HERTZBEAT_SYSTEM_PROMPT)
                     .toolCallbacks(toolCallbackProvider)
@@ -97,4 +111,14 @@ public class ChatClientProviderServiceImpl implements 
ChatClientProviderService
             return Flux.error(e);
         }
     }
-}
\ No newline at end of file
+
+    @Override
+    public boolean isConfigured() {
+        if (!isConfigured) {
+            GeneralConfig providerConfig = 
generalConfigDao.findByType("provider");
+            ModelProviderConfig modelProviderConfig = 
JsonUtil.fromJson(providerConfig.getContent(), ModelProviderConfig.class);
+            isConfigured = modelProviderConfig != null && 
modelProviderConfig.isStatus();   
+        }
+        return isConfigured;
+    }
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ConversationServiceImpl.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ConversationServiceImpl.java
index c63bb29b6..86ff84ff5 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ConversationServiceImpl.java
+++ 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/ConversationServiceImpl.java
@@ -24,7 +24,6 @@ import org.apache.hertzbeat.ai.agent.pojo.dto.ConversationDto;
 import org.apache.hertzbeat.ai.agent.pojo.dto.MessageDto;
 import org.apache.hertzbeat.ai.agent.service.ChatClientProviderService;
 import org.apache.hertzbeat.ai.agent.service.ConversationService;
-import org.apache.hertzbeat.ai.agent.service.OpenAiConfigService;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.http.codec.ServerSentEvent;
 import org.springframework.stereotype.Service;
@@ -51,10 +50,7 @@ public class ConversationServiceImpl implements 
ConversationService {
 
     @Autowired
     private ChatClientProviderService chatClientProviderService;
-
-    @Autowired
-    private OpenAiConfigService openAiConfigService;
-
+    
     @Override
     public ConversationDto createConversation() {
         String conversationId = createNewConversation();
@@ -75,10 +71,10 @@ public class ConversationServiceImpl implements 
ConversationService {
         }
 
         // Check if OpenAI is properly configured
-        if (!openAiConfigService.isConfigured()) {
+        if (!chatClientProviderService.isConfigured()) {
             ChatResponseDto errorResponse = ChatResponseDto.builder()
                     .conversationId(conversationId)
-                    .response("OpenAI is not configured. Please configure your 
OpenAI API key in the settings or application.yml file.")
+                    .response("Provider is not configured. Please configure 
your AI Provider.")
                     .build();
             return Flux.just(ServerSentEvent.builder(errorResponse)
                     .event("error")
@@ -273,4 +269,4 @@ public class ConversationServiceImpl implements 
ConversationService {
 
 
 
-}
\ No newline at end of file
+}
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/OpenAiConfigServiceImpl.java
 
b/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/OpenAiConfigServiceImpl.java
deleted file mode 100644
index 7014de2e4..000000000
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/service/impl/OpenAiConfigServiceImpl.java
+++ /dev/null
@@ -1,200 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one or more
- * contributor license agreements.  See the NOTICE file distributed with
- * this work for additional information regarding copyright ownership.
- * The ASF licenses this file to You under the Apache License, Version 2.0
- * (the "License"); you may not use this file except in compliance with
- * the License.  You may obtain a copy of the License at
- *
- *     http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package org.apache.hertzbeat.ai.agent.service.impl;
-
-import com.fasterxml.jackson.core.JsonProcessingException;
-import com.fasterxml.jackson.databind.ObjectMapper;
-import lombok.extern.slf4j.Slf4j;
-import org.apache.hertzbeat.ai.agent.config.OpenAiYamlConfig;
-import org.apache.hertzbeat.ai.agent.dao.OpenAiConfigDao;
-import org.apache.hertzbeat.ai.agent.entity.OpenAiConfig;
-import org.apache.hertzbeat.ai.agent.event.OpenAiConfigChangeEvent;
-import org.apache.hertzbeat.ai.agent.pojo.dto.OpenAiConfigDto;
-import org.apache.hertzbeat.ai.agent.service.OpenAiConfigService;
-import org.springframework.context.ApplicationContext;
-import org.springframework.context.event.EventListener;
-import org.springframework.http.HttpEntity;
-import org.springframework.http.HttpHeaders;
-import org.springframework.http.HttpMethod;
-import org.springframework.http.HttpStatus;
-import org.springframework.http.ResponseEntity;
-import org.springframework.stereotype.Service;
-import org.springframework.transaction.annotation.Transactional;
-import org.springframework.util.StringUtils;
-import org.springframework.web.client.RestTemplate;
-
-/**
- * OpenAI Configuration Service Implementation
- * Consolidated service for OpenAI configuration, validation, and client 
factory management
- */
-@Slf4j
-@Service
-public class OpenAiConfigServiceImpl implements OpenAiConfigService {
-
-    private static final String CONFIG_TYPE = "openai";
-    private static final String OPENAI_MODELS_ENDPOINT = 
"https://api.openai.com/v1/models";;
-    
-    private final OpenAiConfigDao openAiConfigDao;
-    private final ObjectMapper objectMapper;
-    private final ApplicationContext applicationContext;
-    private final OpenAiYamlConfig yamlConfig;
-    private final RestTemplate restTemplate;
-    
-    // Client factory cache
-    private volatile OpenAiConfigDto currentConfig;
-
-    public OpenAiConfigServiceImpl(OpenAiConfigDao openAiConfigDao, 
-                                  ObjectMapper objectMapper,
-                                  ApplicationContext applicationContext,
-                                  OpenAiYamlConfig yamlConfig) {
-        this.openAiConfigDao = openAiConfigDao;
-        this.objectMapper = objectMapper;
-        this.applicationContext = applicationContext;
-        this.yamlConfig = yamlConfig;
-        this.restTemplate = new RestTemplate();
-    }
-
-    @Override
-    @Transactional(rollbackFor = Exception.class)
-    public void saveConfig(OpenAiConfigDto config) {
-        try {
-            String contentJson = objectMapper.writeValueAsString(config);
-            
-            OpenAiConfig openAiConfig = OpenAiConfig.builder()
-                    .type(CONFIG_TYPE)
-                    .content(contentJson)
-                    .build();
-            
-            openAiConfigDao.save(openAiConfig);
-            log.info("OpenAI configuration saved successfully");
-            
-            applicationContext.publishEvent(new 
OpenAiConfigChangeEvent(applicationContext));
-            
-        } catch (JsonProcessingException e) {
-            throw new IllegalArgumentException("Failed to save OpenAI 
configuration: " + e.getMessage());
-        }
-    }
-
-    @Override
-    public OpenAiConfigDto getConfig() {
-        OpenAiConfig config = openAiConfigDao.findByType(CONFIG_TYPE);
-        if (config == null || !StringUtils.hasText(config.getContent())) {
-            return null;
-        }
-        
-        try {
-            return objectMapper.readValue(config.getContent(), 
OpenAiConfigDto.class);
-        } catch (JsonProcessingException e) {
-            throw new IllegalArgumentException("Failed to parse OpenAI 
configuration: " + e.getMessage());
-        }
-    }
-
-    @Override
-    public boolean isConfigured() {
-        OpenAiConfigDto effective = getEffectiveConfig();
-        return effective != null && effective.isEnable() && 
StringUtils.hasText(effective.getApiKey());
-    }
-
-    @Override
-    public OpenAiConfigDto getEffectiveConfig() {
-        OpenAiConfigDto dbConfig = getConfig();
-        if (dbConfig != null && dbConfig.isEnable() && 
StringUtils.hasText(dbConfig.getApiKey())) {
-            log.debug("Using database OpenAI configuration");
-            return dbConfig;
-        }
-        
-        if (yamlConfig != null && yamlConfig.isEnable() && 
StringUtils.hasText(yamlConfig.getApiKey())) {
-            log.debug("Using YAML OpenAI configuration from 
spring.ai.openai.api-key");
-            OpenAiConfigDto yamlDto = new OpenAiConfigDto();
-            yamlDto.setEnable(true);
-            yamlDto.setApiKey(yamlConfig.getApiKey());
-            return yamlDto;
-        }
-        
-        log.debug("No valid OpenAI configuration found");
-        return null;
-    }
-
-    @Override
-    public ValidationResult validateApiKey(String apiKey) {
-        if (!StringUtils.hasText(apiKey)) {
-            return ValidationResult.failure("API key cannot be empty");
-        }
-
-        if (!apiKey.startsWith("sk-")) {
-            return ValidationResult.failure("Invalid API key format. OpenAI 
API keys should start with 'sk-'");
-        }
-
-        try {
-            HttpHeaders headers = new HttpHeaders();
-            headers.set("Authorization", "Bearer " + apiKey);
-            headers.set("Content-Type", "application/json");
-            
-            HttpEntity<String> entity = new HttpEntity<>(headers);
-            
-            log.debug("Validating OpenAI API key by calling models endpoint");
-            ResponseEntity<String> response = restTemplate.exchange(
-                OPENAI_MODELS_ENDPOINT,
-                HttpMethod.GET,
-                entity,
-                String.class
-            );
-
-            if (response.getStatusCode() == HttpStatus.OK) {
-                log.info("OpenAI API key validation successful");
-                return ValidationResult.success("API key is valid");
-            } else {
-                log.warn("OpenAI API key validation failed with status: {}", 
response.getStatusCode());
-                return ValidationResult.failure("API key validation failed: " 
+ response.getStatusCode());
-            }
-
-        } catch (Exception e) {
-            log.error("Error validating OpenAI API key", e);
-            String errorMessage = e.getMessage();
-            
-            // Parse common error messages
-            if (errorMessage.contains("401")) {
-                return ValidationResult.failure("Invalid API key - 
authentication failed");
-            } else if (errorMessage.contains("403")) {
-                return ValidationResult.failure("API key does not have 
permission to access models");
-            } else if (errorMessage.contains("429")) {
-                return ValidationResult.failure("Rate limit exceeded - please 
try again later");
-            } else if (errorMessage.contains("timeout") || 
errorMessage.contains("connect")) {
-                return ValidationResult.failure("Network error - unable to 
connect to OpenAI API");
-            } else {
-                return ValidationResult.failure("API key validation failed: " 
+ errorMessage);
-            }
-        }
-    }
-
-    @Override
-    public void reloadConfig() {
-        synchronized (this) {
-            currentConfig = null; // Force reload
-        }
-    }
-
-    /**
-     * OpenAI configuration change event listener
-     */
-    @EventListener(OpenAiConfigChangeEvent.class)
-    public void onOpenAiConfigChange(OpenAiConfigChangeEvent event) {
-        log.info("[OpenAiConfigService] OpenAI configuration change event 
received");
-        reloadConfig();
-    }
-}
\ No newline at end of file
diff --git 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/GeneralConfigTypeEnum.java
 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/GeneralConfigTypeEnum.java
index 9a461dfcd..ce54a7030 100644
--- 
a/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/GeneralConfigTypeEnum.java
+++ 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/constants/GeneralConfigTypeEnum.java
@@ -55,5 +55,10 @@ public enum GeneralConfigTypeEnum {
     /**
      * system store config
      */
-    oss
+    oss,
+
+    /**
+     * model llm provider
+     */
+    provider
 }
diff --git 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/event/OpenAiConfigChangeEvent.java
 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/AiProviderConfigChangeEvent.java
similarity index 80%
rename from 
hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/event/OpenAiConfigChangeEvent.java
rename to 
hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/AiProviderConfigChangeEvent.java
index b6bb92f37..f539fb7d6 100644
--- 
a/hertzbeat-ai-agent/src/main/java/org/apache/hertzbeat/ai/agent/event/OpenAiConfigChangeEvent.java
+++ 
b/hertzbeat-common/src/main/java/org/apache/hertzbeat/common/support/event/AiProviderConfigChangeEvent.java
@@ -15,17 +15,17 @@
  * limitations under the License.
  */
 
-package org.apache.hertzbeat.ai.agent.event;
+package org.apache.hertzbeat.common.support.event;
 
 import org.springframework.context.ApplicationContext;
 import org.springframework.context.ApplicationEvent;
 
 /**
- * OpenAI configuration change event
+ * Ai Provider configuration change event
  */
-public class OpenAiConfigChangeEvent extends ApplicationEvent {
+public class AiProviderConfigChangeEvent extends ApplicationEvent {
 
-    public OpenAiConfigChangeEvent(ApplicationContext source) {
+    public AiProviderConfigChangeEvent(ApplicationContext source) {
         super(source);
     }
-}
\ No newline at end of file
+}
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
index f88444e7b..5d365431f 100644
--- 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/controller/GeneralConfigController.java
@@ -51,11 +51,11 @@ import java.util.stream.Collectors;
 import static org.springframework.http.MediaType.APPLICATION_JSON_VALUE;
 
 /**
- * Alert sender Configuration API
+ * Generate Configuration API
  */
 @RestController
 @RequestMapping(value = "/api/config", produces = {APPLICATION_JSON_VALUE})
-@Tag(name = "Alert sender Configuration API")
+@Tag(name = "Generate Configuration API")
 @Slf4j
 public class GeneralConfigController {
 
diff --git 
a/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ModelProviderConfigServiceImpl.java
 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ModelProviderConfigServiceImpl.java
new file mode 100644
index 000000000..82c637e36
--- /dev/null
+++ 
b/hertzbeat-manager/src/main/java/org/apache/hertzbeat/manager/service/impl/ModelProviderConfigServiceImpl.java
@@ -0,0 +1,69 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one or more
+ * contributor license agreements.  See the NOTICE file distributed with
+ * this work for additional information regarding copyright ownership.
+ * The ASF licenses this file to You under the Apache License, Version 2.0
+ * (the "License"); you may not use this file except in compliance with
+ * the License.  You may obtain a copy of the License at
+ *
+ *     http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+package org.apache.hertzbeat.manager.service.impl;
+
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import java.lang.reflect.Type;
+import org.apache.hertzbeat.ai.agent.pojo.dto.ModelProviderConfig;
+import org.apache.hertzbeat.base.dao.GeneralConfigDao;
+import org.apache.hertzbeat.common.constants.GeneralConfigTypeEnum;
+import org.apache.hertzbeat.common.support.event.AiProviderConfigChangeEvent;
+import org.springframework.context.ApplicationContext;
+import org.springframework.stereotype.Service;
+
+/**
+ * llm model provider config service impl
+ */
+@Service
+public class ModelProviderConfigServiceImpl extends 
AbstractGeneralConfigServiceImpl<ModelProviderConfig> {
+
+    private final ApplicationContext applicationContext;
+    
+    /**
+     *
+     * <p>Constructor, passing in GeneralConfigDao, ObjectMapper and type.</p>
+     *
+     * @param generalConfigDao ConfigDao object
+     * @param objectMapper     JSON tool object
+     */
+    public ModelProviderConfigServiceImpl(ApplicationContext 
applicationContext, GeneralConfigDao generalConfigDao, ObjectMapper 
objectMapper) {
+        super(generalConfigDao, objectMapper);
+        this.applicationContext = applicationContext;
+    }
+    
+    @Override
+    public String type() {
+        return GeneralConfigTypeEnum.provider.name();
+    }
+
+    @Override
+    public void handler(ModelProviderConfig config) {
+        applicationContext.publishEvent(new 
AiProviderConfigChangeEvent(applicationContext));
+    }
+
+    @Override
+    public TypeReference<ModelProviderConfig> getTypeReference() {
+        return new TypeReference<>() {
+            @Override
+            public Type getType() {
+                return ModelProviderConfig.class;
+            }
+        };
+    }
+}
diff --git a/hertzbeat-manager/src/main/resources/application-test.yml 
b/hertzbeat-manager/src/main/resources/application-test.yml
index bd76e9d93..4aa13715f 100644
--- a/hertzbeat-manager/src/main/resources/application-test.yml
+++ b/hertzbeat-manager/src/main/resources/application-test.yml
@@ -189,16 +189,3 @@ grafana:
   expose-url: http://127.0.0.1:3000
   username: admin
   password: admin
-
-# See the documentation for details : 
https://hertzbeat.apache.org/zh-cn/docs/help/aiConfig
-ai:
-  # AI Type:zhiPu、alibabaAi、kimiAi、sparkDesk、ollama、openRouter
-  type:
-  # Model name:glm-4、qwen-turboo、moonshot-v1-8k、generalv3.5
-  model:
-  # api key
-  api-key:
-  #At present, only IFLYTEK large model needs to be filled in
-  api-secret:
-  # The URL of the ollama AI service
-  api-url:
diff --git a/hertzbeat-manager/src/main/resources/application.yml 
b/hertzbeat-manager/src/main/resources/application.yml
index 9679a359b..c4f463601 100644
--- a/hertzbeat-manager/src/main/resources/application.yml
+++ b/hertzbeat-manager/src/main/resources/application.yml
@@ -37,14 +37,6 @@ spring:
           resource: true
           prompt: true
           completion: true
-    chat:
-      client:
-        enabled: false
-    openai:
-      api-key: OPENAI_API_KEY
-      chat:
-        options:
-          model: gpt-4.1-nano-2025-04-14
 
   mvc:
     static-path-pattern: /**
@@ -321,16 +313,3 @@ grafana:
   expose-url: http://127.0.0.1:3000
   username: admin
   password: admin
-
-# See the documentation for details : 
https://hertzbeat.apache.org/zh-cn/docs/help/aiConfig
-ai:
-  # AI Type:zhiPu、alibabaAi、kimiAi、sparkDesk、ollama、openRouter
-  type:
-  # Model name:glm-4、qwen-turboo、moonshot-v1-8k、generalv3.5
-  model:
-  # api key
-  api-key:
-  #At present, only IFLYTEK large model needs to be filled in
-  api-secret:
-  # The URL of the ollama AI service
-  api-url:
diff --git a/home/docs/download.md b/home/docs/download.md
index 269cf1f09..ed0edb64f 100644
--- a/home/docs/download.md
+++ b/home/docs/download.md
@@ -22,7 +22,7 @@ Previous releases of HertzBeat may be affected by security 
issues, please use th
 
 | Version | Date       | Download                                              
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
 
|---------|------------|------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
-| v1.7.3  | 2025.09.05 | 
[apache-hertzbeat-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz)
 (Server) ( 
[signature](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.asc)
 , 
[sha512](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.sha512)
 ) <br/> 
[apache-hertzbeat-collector-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-collector-1.
 [...]
+| v1.7.3  | 2025.09.05 | 
[apache-hertzbeat-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz)
 (Server) ( 
[signature](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.asc)
 , 
[sha512](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.sha512)
 ) <br/> 
[apache-hertzbeat-collector-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-collector-1.
 [...]
 
 ## Release Docker Image
 
diff --git a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/download.md 
b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/download.md
index 72c0abbd4..04ec4a2a6 100644
--- a/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/download.md
+++ b/home/i18n/zh-cn/docusaurus-plugin-content-docs/current/download.md
@@ -22,7 +22,7 @@ sidebar_label: Download
 
 | 版本     | 日期         | 下载                                                     
                                                                                
                                                                                
                                                                                
                                                                                
                                                                                
              [...]
 
|--------|------------|-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
 [...]
-| v1.7.3 | 2025.09.05 | 
[apache-hertzbeat-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz)
 (Server) ( 
[signature](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.asc)
 , 
[sha512](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.sha512)
 ) <br/> 
[apache-hertzbeat-collector-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-collector-1.7
 [...]
+| v1.7.3 | 2025.09.05 | 
[apache-hertzbeat-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz)
 (Server) ( 
[signature](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.asc)
 , 
[sha512](https://downloads.apache.org/hertzbeat/1.7.3/apache-hertzbeat-1.7.3-bin.tar.gz.sha512)
 ) <br/> 
[apache-hertzbeat-collector-1.7.3-bin.tar.gz](https://www.apache.org/dyn/closer.lua/hertzbeat/1.7.3/apache-hertzbeat-collector-1.7
 [...]
 
 ## Docker 镜像版本
 
diff --git a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
index 2c4b3cc2a..d98df3fe9 100644
--- a/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-iotdb/conf/application.yml
@@ -38,14 +38,6 @@ spring:
           resource: true
           prompt: true
           completion: true
-    chat:
-      client:
-        enabled: false
-    openai:
-      api-key: OPENAI_API_KEY
-      chat:
-        options:
-          model: gpt-4.1-nano-2025-04-14
   mvc:
     static-path-pattern: /**
   jackson:
@@ -254,4 +246,4 @@ ai:
   model:
   api-key:
   api-secret:
-  api-url:
\ No newline at end of file
+  api-url:
diff --git 
a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
index 49f438421..b2f0e77af 100644
--- a/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
+++ b/script/docker-compose/hertzbeat-mysql-tdengine/conf/application.yml
@@ -38,14 +38,6 @@ spring:
           resource: true
           prompt: true
           completion: true
-    chat:
-      client:
-        enabled: false
-    openai:
-      api-key: OPENAI_API_KEY
-      chat:
-        options:
-          model: gpt-4.1-nano-2025-04-14
   mvc:
     static-path-pattern: /**
   jackson:
diff --git 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
index f9f047195..c48aa31d8 100644
--- 
a/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-mysql-victoria-metrics/conf/application.yml
@@ -38,14 +38,6 @@ spring:
           resource: true
           prompt: true
           completion: true
-    chat:
-      client:
-        enabled: false
-    openai:
-      api-key: OPENAI_API_KEY
-      chat:
-        options:
-          model: gpt-4.1-nano-2025-04-14
   mvc:
     static-path-pattern: /**
   jackson:
@@ -254,4 +246,4 @@ ai:
   model:
   api-key:
   api-secret:
-  api-url:
\ No newline at end of file
+  api-url:
diff --git 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
index 08590e289..15f3660b5 100644
--- 
a/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
+++ 
b/script/docker-compose/hertzbeat-postgresql-victoria-metrics/conf/application.yml
@@ -38,14 +38,6 @@ spring:
           resource: true
           prompt: true
           completion: true
-    chat:
-      client:
-        enabled: false
-    openai:
-      api-key: OPENAI_API_KEY
-      chat:
-        options:
-          model: gpt-4.1-nano-2025-04-14
   mvc:
     static-path-pattern: /**
   jackson:
@@ -253,4 +245,4 @@ ai:
   model:
   api-key:
   api-secret:
-  api-url:
\ No newline at end of file
+  api-url:
diff --git a/web-app/src/app/layout/basic/basic.component.html 
b/web-app/src/app/layout/basic/basic.component.html
deleted file mode 100644
index c52473284..000000000
--- a/web-app/src/app/layout/basic/basic.component.html
+++ /dev/null
@@ -1,30 +0,0 @@
-<div class="alain-default__content">
-  <router-outlet></router-outlet>
-</div>
-
-<div class="ai-chatbot-container">
-  <div class="ai-chatbot-button" (click)="toggleChatbot()">
-    <span *ngIf="!isChatbotOpen">AI</span>
-    <span *ngIf="isChatbotOpen">X</span>
-  </div>
-
-  <div class="ai-chatbot-window" *ngIf="isChatbotOpen">
-    <div class="chatbot-header">
-      <div class="chatbot-title">{{ 'ai.bot.title' | i18n }}</div>
-      <div class="chatbot-close" (click)="toggleChatbot()">X</div>
-    </div>
-    <div class="chatbot-messages">
-      <div *ngFor="let message of chatMessages" 
[class.user-message]="message.isUser" [class.bot-message]="!message.isUser" 
class="message">
-        <div class="message-content">{{ message.content }}</div>
-        <div class="message-time">{{ message.timestamp | date : 'HH:mm' 
}}</div>
-      </div>
-      <div *ngIf="isLoading" class="bot-message loading-message">
-        <nz-spin nzSimple></nz-spin>
-      </div>
-    </div>
-    <div class="chatbot-input">
-      <input nz-input placeholder="{{ 'ai.bot.input.placeholder' | i18n }}" 
[(ngModel)]="currentMessage" (keyup.enter)="sendMessage()" />
-      <button nz-button nzType="primary" [disabled]="!currentMessage.trim()" 
(click)="sendMessage()"> {{ 'ai.bot.send' | i18n }} </button>
-    </div>
-  </div>
-</div>
diff --git a/web-app/src/app/layout/basic/basic.component.less 
b/web-app/src/app/layout/basic/basic.component.less
index 0533e2857..9407b354b 100644
--- a/web-app/src/app/layout/basic/basic.component.less
+++ b/web-app/src/app/layout/basic/basic.component.less
@@ -338,9 +338,9 @@ global-footer {
 }
 
 .ai-chat-button {
-  width: 56px;
-  height: 56px;
-  border-radius: 50%;
+  width: 44px;
+  height: 44px;
+  border-radius: 8px;
   background-color: @primary-color;
   color: white;
   display: flex;
@@ -371,7 +371,7 @@ global-footer {
     overflow: hidden !important;
   }
 
-  
+
 
   .ant-modal-header {
     border-radius: 16px 16px 0 0 !important;
diff --git a/web-app/src/app/layout/basic/basic.component.ts 
b/web-app/src/app/layout/basic/basic.component.ts
index 30cf51ae7..38d7ad3c5 100644
--- a/web-app/src/app/layout/basic/basic.component.ts
+++ b/web-app/src/app/layout/basic/basic.component.ts
@@ -83,7 +83,7 @@ import { AiChatModalService } from 
'../../shared/services/ai-chat-modal.service'
           width="28"
           height="28"
           fill="white"
-          style="min-width:28px; min-height:28px;"
+          style="min-width:36px; min-height:36px;"
         >
           <path
             d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 
1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 
1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7h1V5.73c-.6-.34-1-.99-1-1.73a2 2 0 0 1 
2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18a2.5 2.5 0 0 0 2.5-2.5A2.5 
2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 
2.5-2.5a2.5 2.5 0 0 0-2.5-2.5z"
diff --git a/web-app/src/app/pojo/ModelProviderConfig.ts 
b/web-app/src/app/pojo/ModelProviderConfig.ts
new file mode 100644
index 000000000..5fe6dbbe3
--- /dev/null
+++ b/web-app/src/app/pojo/ModelProviderConfig.ts
@@ -0,0 +1,57 @@
+/*
+ * Licensed to the Apache Software Foundation (ASF) under one
+ * or more contributor license agreements.  See the NOTICE file
+ * distributed with this work for additional information
+ * regarding copyright ownership.  The ASF licenses this file
+ * to you under the Apache License, Version 2.0 (the
+ * "License"); you may not use this file except in compliance
+ * with the License.  You may obtain a copy of the License at
+ *
+ *   http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing,
+ * software distributed under the License is distributed on an
+ * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
+ * KIND, either express or implied.  See the License for the
+ * specific language governing permissions and limitations
+ * under the License.
+ */
+
+export class ModelProviderConfig {
+  enable: boolean = false;
+  status: boolean = true;
+  error!: string;
+  type!: string;
+  code: string = 'openai';
+  baseUrl: string = '';
+  model: string = '';
+  apiKey!: string;
+}
+
+export interface ProviderOption {
+  value: string;
+  label: string;
+  defaultBaseUrl: string;
+  defaultModel: string;
+}
+
+export const PROVIDER_OPTIONS: ProviderOption[] = [
+  {
+    value: 'openai',
+    label: 'OpenAI',
+    defaultBaseUrl: 'https://api.openai.com/v1',
+    defaultModel: 'gpt-4'
+  },
+  {
+    value: 'zai',
+    label: 'ZAI',
+    defaultBaseUrl: 'https://api.z.ai/api/paas/v4',
+    defaultModel: 'glm-4.6'
+  },
+  {
+    value: 'zhipu',
+    label: 'ZhiPu',
+    defaultBaseUrl: 'https://open.bigmodel.cn/api/paas/v4',
+    defaultModel: 'glm-4.6'
+  }
+];
diff --git a/web-app/src/app/service/general-config.service.ts 
b/web-app/src/app/service/general-config.service.ts
index 02141ee5b..f72c9b11b 100644
--- a/web-app/src/app/service/general-config.service.ts
+++ b/web-app/src/app/service/general-config.service.ts
@@ -22,6 +22,7 @@ import { Injectable } from '@angular/core';
 import { Observable } from 'rxjs';
 
 import { Message } from '../pojo/Message';
+import { ModelProviderConfig } from '../pojo/ModelProviderConfig';
 
 const general_config_uri = '/config';
 
@@ -39,6 +40,14 @@ export class GeneralConfigService {
     return this.http.get<Message<any>>(`${general_config_uri}/${type}`);
   }
 
+  public saveModelProviderConfig(body: ModelProviderConfig): 
Observable<Message<any>> {
+    return this.http.post<Message<any>>(`${general_config_uri}/provider`, 
body);
+  }
+
+  public getModelProviderConfig(): Observable<Message<ModelProviderConfig>> {
+    return 
this.http.get<Message<ModelProviderConfig>>(`${general_config_uri}/provider`);
+  }
+
   public updateAppTemplateConfig(body: any, app: string): 
Observable<Message<void>> {
     return 
this.http.put<Message<void>>(`${general_config_uri}/template/${app}`, body);
   }
diff --git a/web-app/src/app/service/openai-config.service.ts 
b/web-app/src/app/service/openai-config.service.ts
deleted file mode 100644
index a82c47815..000000000
--- a/web-app/src/app/service/openai-config.service.ts
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { HttpClient } from '@angular/common/http';
-import { Injectable } from '@angular/core';
-import { Observable } from 'rxjs';
-
-import { Message } from '../pojo/Message';
-
-export interface OpenAiConfig {
-  enable: boolean;
-  apiKey: string;
-}
-
-export interface OpenAiConfigStatus {
-  configured: boolean;
-  hasDbConfig: boolean;
-  hasYamlConfig: boolean;
-  validationPassed: boolean;
-  validationMessage: string;
-}
-
-const openai_config_uri = '/ai-agent/config';
-
-@Injectable({
-  providedIn: 'root'
-})
-export class OpenAiConfigService {
-  constructor(private http: HttpClient) {}
-
-  public saveOpenAiConfig(config: OpenAiConfig): Observable<Message<any>> {
-    return this.http.post<Message<any>>(`${openai_config_uri}/openai`, config);
-  }
-
-  public getOpenAiConfig(): Observable<Message<OpenAiConfig>> {
-    return this.http.get<Message<OpenAiConfig>>(`${openai_config_uri}/openai`);
-  }
-
-  public getOpenAiConfigStatus(): Observable<Message<OpenAiConfigStatus>> {
-    return 
this.http.get<Message<OpenAiConfigStatus>>(`${openai_config_uri}/openai/status`);
-  }
-}
diff --git a/web-app/src/app/shared/components/ai-bot/ai-bot.component.html 
b/web-app/src/app/shared/components/ai-bot/ai-bot.component.html
deleted file mode 100644
index 8c06951e9..000000000
--- a/web-app/src/app/shared/components/ai-bot/ai-bot.component.html
+++ /dev/null
@@ -1,67 +0,0 @@
-<!--
-  ~ Licensed to the Apache Software Foundation (ASF) under one
-  ~ or more contributor license agreements.  See the NOTICE file
-  ~ distributed with this work for additional information
-  ~ regarding copyright ownership.  The ASF licenses this file
-  ~ to you under the Apache License, Version 2.0 (the
-  ~ "License"); you may not use this file except in compliance
-  ~ with the License.  You may obtain a copy of the License at
-  ~
-  ~   http://www.apache.org/licenses/LICENSE-2.0
-  ~
-  ~ Unless required by applicable law or agreed to in writing,
-  ~ software distributed under the License is distributed on an
-  ~ "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-  ~ KIND, either express or implied.  See the License for the
-  ~ specific language governing permissions and limitations
-  ~ under the License.
--->
-
-<div class="ai-bot-container">
-  <div class="ai-bot-icon" (click)="toggleChat()" [class.active]="isOpen">
-    <div *ngIf="!isOpen" class="robot-icon">
-      <svg xmlns="http://www.w3.org/2000/svg"; viewBox="0 0 24 24" width="24" 
height="24" fill="currentColor">
-        <path
-          d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 1 
0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 
1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7h1V5.73c-.6-.34-1-.99-1-1.73a2 2 0 0 1 
2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18a2.5 2.5 0 0 0 2.5-2.5A2.5 
2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 
2.5-2.5a2.5 2.5 0 0 0-2.5-2.5z"
-        />
-      </svg>
-    </div>
-    <div *ngIf="isOpen" class="close-icon">X</div>
-  </div>
-
-  <div class="ai-bot-chat-window" *ngIf="isOpen">
-    <div class="chat-header">
-      <div class="chat-title">
-        <svg
-          xmlns="http://www.w3.org/2000/svg";
-          viewBox="0 0 24 24"
-          width="20"
-          height="20"
-          fill="currentColor"
-          style="margin-right: 6px; vertical-align: middle"
-        >
-          <path
-            d="M12 2a2 2 0 0 1 2 2c0 .74-.4 1.39-1 1.73V7h1a7 7 0 0 1 7 7h1a1 
1 0 0 1 1 1v3a1 1 0 0 1-1 1h-1v1a2 2 0 0 1-2 2H5a2 2 0 0 1-2-2v-1H2a1 1 0 0 
1-1-1v-3a1 1 0 0 1 1-1h1a7 7 0 0 1 7-7h1V5.73c-.6-.34-1-.99-1-1.73a2 2 0 0 1 
2-2M7.5 13A2.5 2.5 0 0 0 5 15.5A2.5 2.5 0 0 0 7.5 18a2.5 2.5 0 0 0 2.5-2.5A2.5 
2.5 0 0 0 7.5 13m9 0a2.5 2.5 0 0 0-2.5 2.5a2.5 2.5 0 0 0 2.5 2.5a2.5 2.5 0 0 0 
2.5-2.5a2.5 2.5 0 0 0-2.5-2.5z"
-          />
-        </svg>
-        {{ 'ai.bot.title' | i18n }}
-      </div>
-      <span class="close-btn" (click)="toggleChat()">X</span>
-    </div>
-
-    <div class="chat-messages">
-      <div *ngFor="let message of messages" class="message" 
[class.user-message]="message.isUser" [class.bot-message]="!message.isUser">
-        <div class="message-content">{{ message.content }}</div>
-        <div class="message-time">{{ message.timestamp | date : 'HH:mm' 
}}</div>
-      </div>
-      <div *ngIf="isLoading" class="bot-message loading-message">
-        <nz-spin nzSimple></nz-spin>
-      </div>
-    </div>
-
-    <div class="chat-input">
-      <input nz-input placeholder="{{ 'ai.bot.input.placeholder' | i18n }}" 
[(ngModel)]="currentMessage" (keyup.enter)="sendMessage()" />
-      <button nz-button nzType="primary" [disabled]="!currentMessage.trim()" 
(click)="sendMessage()"> {{ 'ai.bot.send' | i18n }} </button>
-    </div>
-  </div>
-</div>
diff --git a/web-app/src/app/shared/components/ai-bot/ai-bot.component.less 
b/web-app/src/app/shared/components/ai-bot/ai-bot.component.less
deleted file mode 100644
index 03fe1519e..000000000
--- a/web-app/src/app/shared/components/ai-bot/ai-bot.component.less
+++ /dev/null
@@ -1,147 +0,0 @@
-.ai-bot-container {
-  position: fixed;
-  bottom: 30px;
-  right: 30px;
-  z-index: 10000;
-}
-
-.ai-bot-icon {
-  width: 60px;
-  height: 60px;
-  border-radius: 50%;
-  background-color: #1890ff;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  cursor: pointer;
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
-  transition: all 0.3s;
-  color: white;
-  font-weight: bold;
-  font-size: 18px;
-
-  &:hover {
-    transform: scale(1.05);
-    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
-  }
-
-  &.active {
-    background-color: #ff4d4f;
-  }
-}
-
-.ai-bot-icon-text {
-  width: 60px;
-  height: 60px;
-  border-radius: 50%;
-  background-color: #1890ff;
-  display: flex;
-  align-items: center;
-  justify-content: center;
-  cursor: pointer;
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
-  transition: all 0.3s;
-  color: white;
-  font-weight: bold;
-  font-size: 14px;
-  text-align: center;
-  
-  &:hover {
-    transform: scale(1.05);
-    box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
-  }
-}
-
-.ai-bot-chat-window {
-  position: absolute;
-  bottom: 80px;
-  right: 0;
-  width: 350px;
-  height: 500px;
-  background-color: white;
-  border-radius: 8px;
-  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.12);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-  z-index: 10000;
-}
-
-.chat-header {
-  padding: 12px 16px;
-  border-bottom: 1px solid #f0f0f0;
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  background-color: #1890ff;
-  color: white;
-
-  .chat-title {
-    font-weight: 500;
-    font-size: 16px;
-  }
-
-  .close-btn {
-    cursor: pointer;
-    font-size: 16px;
-    color: white;
-    font-weight: bold;
-  }
-}
-
-.chat-messages {
-  flex: 1;
-  padding: 16px;
-  overflow-y: auto;
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  background-color: #f5f5f5;
-}
-
-.message {
-  max-width: 80%;
-  padding: 10px 12px;
-  border-radius: 8px;
-  position: relative;
-
-  .message-content {
-    word-break: break-word;
-  }
-
-  .message-time {
-    font-size: 11px;
-    margin-top: 4px;
-    opacity: 0.7;
-    text-align: right;
-  }
-}
-
-.bot-message {
-  align-self: flex-start;
-  background-color: white;
-  border: 1px solid #e8e8e8;
-
-  &.loading-message {
-    padding: 16px;
-    display: flex;
-    justify-content: center;
-  }
-}
-
-.user-message {
-  align-self: flex-end;
-  background-color: #e6f7ff;
-  color: #0050b3;
-}
-
-.chat-input {
-  padding: 12px;
-  border-top: 1px solid #f0f0f0;
-  display: flex;
-  gap: 8px;
-
-  input {
-    flex: 1;
-  }
-} 
\ No newline at end of file
diff --git a/web-app/src/app/shared/components/ai-bot/ai-bot.component.scss 
b/web-app/src/app/shared/components/ai-bot/ai-bot.component.scss
deleted file mode 100644
index b33c34ecc..000000000
--- a/web-app/src/app/shared/components/ai-bot/ai-bot.component.scss
+++ /dev/null
@@ -1,158 +0,0 @@
-// Licensed to the Apache Software Foundation (ASF) under one
-// or more contributor license agreements.  See the NOTICE file
-// distributed with this work for additional information
-// regarding copyright ownership.  The ASF licenses this file
-// to you under the Apache License, Version 2.0 (the
-// "License"); you may not use this file except in compliance
-// with the License.  You may obtain a copy of the License at
-//
-//   http://www.apache.org/licenses/LICENSE-2.0
-//
-// Unless required by applicable law or agreed to in writing,
-// software distributed under the License is distributed on an
-// "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
-// KIND, either express or implied.  See the License for the
-// specific language governing permissions and limitations
-// under the License.
-
-.ai-bot-container {
-  position: fixed;
-  bottom: 20px;
-  right: 20px;
-  z-index: 1000;
-}
-
-.ai-bot-icon {
-  width: 50px;
-  height: 50px;
-  border-radius: 50%;
-  background-color: #1890ff;
-  color: white;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-  cursor: pointer;
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
-  transition: all 0.3s ease;
-}
-
-.ai-bot-icon:hover {
-  transform: scale(1.05);
-  box-shadow: 0 6px 16px rgba(0, 0, 0, 0.2);
-}
-
-.ai-bot-icon.active {
-  background-color: #f5222d;
-}
-
-/* 机器人图标样式 */
-.robot-icon {
-  width: 30px;
-  height: 30px;
-  display: flex;
-  justify-content: center;
-  align-items: center;
-}
-
-.robot-icon svg {
-  width: 100%;
-  height: 100%;
-  fill: white; /* 确保SVG是白色 */
-}
-
-/* 关闭图标样式 */
-.close-icon {
-  font-weight: bold;
-  font-size: 18px;
-}
-
-.ai-bot-chat-window {
-  position: absolute;
-  bottom: 70px;
-  right: 0;
-  width: 320px;
-  height: 450px;
-  border-radius: 8px;
-  background-color: white;
-  box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
-  display: flex;
-  flex-direction: column;
-  overflow: hidden;
-}
-
-.chat-header {
-  display: flex;
-  justify-content: space-between;
-  align-items: center;
-  padding: 12px 16px;
-  background-color: #1890ff;
-  color: white;
-}
-
-.chat-title {
-  font-weight: 500;
-  display: flex;
-  align-items: center;
-}
-
-.close-btn {
-  cursor: pointer;
-  font-weight: bold;
-}
-
-.chat-messages {
-  flex: 1;
-  overflow-y: auto;
-  padding: 16px;
-  display: flex;
-  flex-direction: column;
-  gap: 12px;
-  background-color: #f5f5f5;
-}
-
-.message {
-  max-width: 80%;
-  padding: 8px 12px;
-  border-radius: 12px;
-}
-
-.user-message {
-  align-self: flex-end;
-  background-color: #1890ff;
-  color: white;
-}
-
-.bot-message {
-  align-self: flex-start;
-  background-color: white;
-  color: #333;
-  border: 1px solid #e8e8e8;
-}
-
-.message-content {
-  word-break: break-word;
-  white-space: pre-wrap;
-}
-
-.message-time {
-  font-size: 12px;
-  margin-top: 4px;
-  opacity: 0.7;
-  text-align: right;
-}
-
-.loading-message {
-  padding: 8px;
-  text-align: center;
-}
-
-.chat-input {
-  display: flex;
-  padding: 12px;
-  border-top: 1px solid #e8e8e8;
-}
-
-.chat-input input {
-  flex: 1;
-  margin-right: 8px;
-}
diff --git a/web-app/src/app/shared/components/ai-bot/ai-bot.component.ts 
b/web-app/src/app/shared/components/ai-bot/ai-bot.component.ts
deleted file mode 100644
index 7975eab9e..000000000
--- a/web-app/src/app/shared/components/ai-bot/ai-bot.component.ts
+++ /dev/null
@@ -1,72 +0,0 @@
-/*
- * Licensed to the Apache Software Foundation (ASF) under one
- * or more contributor license agreements.  See the NOTICE file
- * distributed with this work for additional information
- * regarding copyright ownership.  The ASF licenses this file
- * to you under the Apache License, Version 2.0 (the
- * "License"); you may not use this file except in compliance
- * with the License.  You may obtain a copy of the License at
- *
- *   http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing,
- * software distributed under the License is distributed on an
- * "AS IS" BASIS, WITHOUT WARRANTIES OR CONDITIONS OF ANY
- * KIND, either express or implied.  See the License for the
- * specific language governing permissions and limitations
- * under the License.
- */
-
-import { Component, OnInit } from '@angular/core';
-import { I18NService } from '@core';
-
-import { AiBotService, ChatMessage } from '../../services/ai-bot.service';
-
-@Component({
-  selector: 'app-ai-bot',
-  templateUrl: './ai-bot.component.html',
-  styleUrls: ['./ai-bot.component.less']
-})
-export class AiBotComponent implements OnInit {
-  isOpen = false;
-  messages: ChatMessage[] = [];
-  currentMessage = '';
-  isLoading = false;
-
-  constructor(private aiBotService: AiBotService, private i18nSvc: 
I18NService) {}
-
-  ngOnInit(): void {
-    // add greeting message
-    this.messages.push({
-      content: this.i18nSvc.fanyi('ai.bot.greeting'),
-      isUser: false,
-      timestamp: new Date()
-    });
-    console.log('The AI assistant component has loaded.');
-  }
-
-  toggleChat(): void {
-    this.isOpen = !this.isOpen;
-  }
-
-  sendMessage(): void {
-    if (!this.currentMessage.trim()) return;
-
-    // add user message
-    this.messages.push({
-      content: this.currentMessage,
-      isUser: true,
-      timestamp: new Date()
-    });
-
-    const userMessage = this.currentMessage;
-    this.currentMessage = '';
-    this.isLoading = true;
-
-    // call service to fetch AI response
-    this.aiBotService.sendMessage(userMessage).subscribe(response => {
-      this.messages.push(response);
-      this.isLoading = false;
-    });
-  }
-}
diff --git a/web-app/src/app/shared/components/ai-chat/ai-chat.module.ts 
b/web-app/src/app/shared/components/ai-chat/ai-chat.module.ts
index 0f3ac1a0e..ca16c108b 100644
--- a/web-app/src/app/shared/components/ai-chat/ai-chat.module.ts
+++ b/web-app/src/app/shared/components/ai-chat/ai-chat.module.ts
@@ -28,6 +28,7 @@ import { NzIconModule } from 'ng-zorro-antd/icon';
 import { NzInputModule } from 'ng-zorro-antd/input';
 import { NzMessageModule } from 'ng-zorro-antd/message';
 import { NzModalModule } from 'ng-zorro-antd/modal';
+import { NzSelectModule } from 'ng-zorro-antd/select';
 import { NzSpinModule } from 'ng-zorro-antd/spin';
 import { MarkdownPipe } from 'ngx-markdown';
 
@@ -47,6 +48,7 @@ import { ChatComponent } from './chat.component';
     NzInputModule,
     NzMessageModule,
     NzModalModule,
+    NzSelectModule,
     NzSpinModule,
     MarkdownPipe
   ],
diff --git a/web-app/src/app/shared/components/ai-chat/chat.component.html 
b/web-app/src/app/shared/components/ai-chat/chat.component.html
index 5642b1276..a44748bb7 100644
--- a/web-app/src/app/shared/components/ai-chat/chat.component.html
+++ b/web-app/src/app/shared/components/ai-chat/chat.component.html
@@ -160,23 +160,73 @@
 <!-- OpenAI Configuration Modal -->
 <nz-modal
   [(nzVisible)]="showConfigModal"
-  nzTitle="OpenAI Configuration"
+  nzTitle="AI Provider Configuration"
   (nzOnCancel)="onCloseConfigModal()"
-  (nzOnOk)="onSaveOpenAiConfig()"
+  (nzOnOk)="onSaveAiProviderConfig()"
   nzMaskClosable="false"
   [nzClosable]="false"
-  nzWidth="600px"
+  nzWidth="700px"
   [nzOkLoading]="configLoading"
   nzOkText="Validate & Save"
   nzCancelText="Cancel"
 >
   <div *nzModalContent class="-inner-content">
     <form nz-form nzLayout="vertical">
+      <!-- Provider Selection -->
       <nz-form-item>
-        <nz-form-label nzRequired="true">OpenAI API Key</nz-form-label>
+        <nz-form-label nzRequired="true">AI Provider</nz-form-label>
+        <nz-form-control nzErrorTip="Please select an AI provider">
+          <nz-select
+            [(ngModel)]="aiProviderConfig.code"
+            name="provider"
+            nzPlaceHolder="Select AI Provider"
+            (ngModelChange)="onProviderChange($event)"
+            style="width: 100%"
+          >
+            <nz-option *ngFor="let option of providerOptions" 
[nzValue]="option.value" [nzLabel]="option.label"></nz-option>
+          </nz-select>
+          <p class="form-help">Choose your AI provider (OpenAI, ZhiPu, or 
ZAI)</p>
+        </nz-form-control>
+      </nz-form-item>
+
+      <!-- API Key -->
+      <nz-form-item>
+        <nz-form-label nzRequired="true">API Key</nz-form-label>
         <nz-form-control nzErrorTip="API Key is required">
-          <input nz-input [(ngModel)]="openAiConfig.apiKey" name="apiKey" 
type="password" placeholder="sk-..." required />
-          <p class="form-help">Your OpenAI API key (starts with sk-). The key 
will be validated when saved.</p>
+          <input nz-input [(ngModel)]="aiProviderConfig.apiKey" name="apiKey" 
type="password" placeholder="sk-..." required />
+          <p class="form-help">Your Provider API key. The key will be 
validated when saved.</p>
+        </nz-form-control>
+      </nz-form-item>
+
+      <!-- Base URL -->
+      <nz-form-item>
+        <nz-form-label>Base URL</nz-form-label>
+        <nz-form-control>
+          <nz-input-group [nzSuffix]="resetBtn">
+            <input nz-input [(ngModel)]="aiProviderConfig.baseUrl" 
name="baseUrl" placeholder="https://api.openai.com/v1"; />
+            <ng-template #resetBtn>
+              <button
+                nz-button
+                nzType="link"
+                nzSize="small"
+                (click)="resetToDefaults()"
+                nzTooltipTitle="Reset to default values"
+                nz-tooltip
+              >
+                <i nz-icon nzType="reload"></i>
+              </button>
+            </ng-template>
+          </nz-input-group>
+          <p class="form-help">Custom API endpoint URL. Leave empty to use 
default for selected provider.</p>
+        </nz-form-control>
+      </nz-form-item>
+
+      <!-- Model -->
+      <nz-form-item>
+        <nz-form-label>Model</nz-form-label>
+        <nz-form-control>
+          <input nz-input [(ngModel)]="aiProviderConfig.model" name="model" 
placeholder="gpt-4" />
+          <p class="form-help">Model name to use. Leave empty to use default 
for selected provider.</p>
         </nz-form-control>
       </nz-form-item>
     </form>
diff --git a/web-app/src/app/shared/components/ai-chat/chat.component.less 
b/web-app/src/app/shared/components/ai-chat/chat.component.less
index 91e6c0005..8edbb3a1c 100644
--- a/web-app/src/app/shared/components/ai-chat/chat.component.less
+++ b/web-app/src/app/shared/components/ai-chat/chat.component.less
@@ -634,6 +634,41 @@ body[data-theme='dark'] .chat-container {
   }
 }
 
+// Provider configuration form styles
+.provider-config-form {
+  .provider-select-row {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    
+    nz-select {
+      flex: 1;
+    }
+  }
+  
+  .baseurl-input-row {
+    display: flex;
+    align-items: center;
+    gap: 8px;
+    
+    nz-input-group {
+      flex: 1;
+    }
+    
+    .reset-btn {
+      flex-shrink: 0;
+    }
+  }
+  
+  .form-item-with-help {
+    margin-bottom: 8px;
+    
+    + .form-help {
+      margin-bottom: 16px;
+    }
+  }
+}
+
 nz-modal {
   .ant-modal-body {
     padding: 24px;
diff --git a/web-app/src/app/shared/components/ai-chat/chat.component.ts 
b/web-app/src/app/shared/components/ai-chat/chat.component.ts
index 3a3efa5a4..b47365bcd 100644
--- a/web-app/src/app/shared/components/ai-chat/chat.component.ts
+++ b/web-app/src/app/shared/components/ai-chat/chat.component.ts
@@ -22,8 +22,9 @@ import { I18NService } from '@core';
 import { NzMessageService } from 'ng-zorro-antd/message';
 import { NzModalService } from 'ng-zorro-antd/modal';
 
+import { ModelProviderConfig, PROVIDER_OPTIONS, ProviderOption } from 
'../../../pojo/ModelProviderConfig';
 import { AiChatService, ChatMessage, ConversationDto } from 
'../../../service/ai-chat.service';
-import { OpenAiConfigService, OpenAiConfig, OpenAiConfigStatus } from 
'../../../service/openai-config.service';
+import { GeneralConfigService } from '../../../service/general-config.service';
 import { ThemeService } from '../../../service/theme.service';
 
 @Component({
@@ -46,10 +47,8 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
   isOpenAiConfigured = false;
   showConfigModal = false;
   configLoading = false;
-  openAiConfig: OpenAiConfig = {
-    enable: false,
-    apiKey: ''
-  };
+  aiProviderConfig: ModelProviderConfig = new ModelProviderConfig();
+  providerOptions: ProviderOption[] = PROVIDER_OPTIONS;
 
   constructor(
     private aiChatService: AiChatService,
@@ -58,12 +57,12 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
     private i18n: I18NService,
     private cdr: ChangeDetectorRef,
     private themeSvc: ThemeService,
-    private openAiConfigService: OpenAiConfigService
+    private generalConfigSvc: GeneralConfigService
   ) {}
 
   ngOnInit(): void {
     this.theme = this.themeSvc.getTheme() || 'default';
-    this.checkOpenAiConfiguration();
+    this.checkAiConfiguration();
   }
 
   ngAfterViewChecked(): void {
@@ -74,10 +73,8 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
    * Load all conversations
    */
   loadConversations(): void {
-    console.log('Loading conversations...');
     this.aiChatService.getConversations().subscribe({
       next: response => {
-        console.log('Conversations response:', response);
         if (response.code === 0 && response.data) {
           this.conversations = response.data;
           // If no current conversation, create a new one
@@ -107,10 +104,8 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
    * Create a new conversation
    */
   createNewConversation(): void {
-    console.log('Creating new conversation...');
     this.aiChatService.createConversation().subscribe({
       next: response => {
-        console.log('Create conversation response:', response);
         if (response.code === 0 && response.data) {
           const newConversation = response.data;
           this.conversations.unshift(newConversation);
@@ -165,7 +160,6 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
     this.aiChatService.getConversation(conversationId).subscribe({
       next: response => {
         this.isLoading = false;
-        console.log('Conversation history response:', response);
 
         if (response.code === 0 && response.data) {
           this.messages = response.data.messages || [];
@@ -322,7 +316,6 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
         this.scrollToBottom();
       },
       complete: () => {
-        console.log('Chat stream completed');
         this.isLoading = false;
         this.cdr.detectChanges();
 
@@ -428,61 +421,68 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
   }
 
   /**
-   * Check OpenAI configuration status
+   * Check provider configuration status
    */
-  checkOpenAiConfiguration(): void {
-    this.openAiConfigService.getOpenAiConfigStatus().subscribe({
+  checkAiConfiguration(): void {
+    this.generalConfigSvc.getModelProviderConfig().subscribe({
       next: response => {
-        if (response.code === 0) {
-          this.isOpenAiConfigured = response.data.configured;
-          if (this.isOpenAiConfigured) {
+        if (response.code === 0 && response.data) {
+          this.aiProviderConfig = response.data;
+          // Ensure default values are set if not present
+          if (!this.aiProviderConfig.code) {
+            this.aiProviderConfig.code = 'openai';
+          }
+          if (!this.aiProviderConfig.baseUrl) {
+            const defaultProvider = this.providerOptions.find(p => p.value === 
this.aiProviderConfig.code);
+            if (defaultProvider) {
+              this.aiProviderConfig.baseUrl = defaultProvider.defaultBaseUrl;
+            }
+          }
+          if (!this.aiProviderConfig.model) {
+            const defaultProvider = this.providerOptions.find(p => p.value === 
this.aiProviderConfig.code);
+            if (defaultProvider) {
+              this.aiProviderConfig.model = defaultProvider.defaultModel;
+            }
+          }
+
+          if (response.data.enable) {
             this.loadConversations();
           } else {
-            this.showOpenAiConfigDialog(response.data);
+            this.showAiProviderConfigDialog(response.data.error);
           }
         } else {
-          console.error('Failed to check OpenAI configuration:', response.msg);
-          this.showOpenAiConfigDialog();
+          // Initialize with default values if no config exists
+          this.aiProviderConfig = new ModelProviderConfig();
+          this.showAiProviderConfigDialog();
         }
       },
       error: error => {
-        console.error('Error checking OpenAI configuration:', error);
-        this.showOpenAiConfigDialog();
+        console.error('Failed to load model provider config:', error);
+        this.aiProviderConfig = new ModelProviderConfig();
+        this.showAiProviderConfigDialog();
       }
     });
   }
 
   /**
-   * Show OpenAI configuration dialog
+   * Show ai configuration dialog
    */
-  showOpenAiConfigDialog(status?: OpenAiConfigStatus): void {
-    // Load existing configuration if available
-    this.loadOpenAiConfig();
-
+  showAiProviderConfigDialog(error?: string): void {
     let contentMessage = `
       <div style="margin-bottom: 16px;">
-        <p>To use AI Agent Chat, please configure your OpenAI API key.</p>
+        <p>To use AI Agent Chat, please configure your Model Provider.</p>
     `;
 
-    if (status && !status.validationPassed && status.validationMessage) {
+    if (error) {
       contentMessage += `
         <div style="margin-bottom: 12px; padding: 8px; background: #fff2f0; 
border: 1px solid #ffccc7; border-radius: 4px;">
-          <strong>Configuration Issue:</strong> ${status.validationMessage}
+          <strong>Configuration Issue:</strong> ${error}
         </div>
       `;
     }
 
-    contentMessage += `
-        <p>You can either:</p>
-        <ul>
-          <li>Configure it here (stored in database)</li>
-          <li>Add it to your application.yml file under 
<code>spring.ai.openai.api-key</code></li>
-        </ul>
-      </div>
-    `;
-
     const modalRef = this.modal.create({
-      nzTitle: 'OpenAI Configuration Required',
+      nzTitle: 'Ai Model Provider Configuration Required',
       nzContent: contentMessage,
       nzWidth: 600,
       nzClosable: false,
@@ -500,27 +500,11 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
     });
   }
 
-  /**
-   * Load OpenAI configuration
-   */
-  loadOpenAiConfig(): void {
-    this.openAiConfigService.getOpenAiConfig().subscribe({
-      next: response => {
-        if (response.code === 0 && response.data) {
-          this.openAiConfig = { ...this.openAiConfig, ...response.data };
-        }
-      },
-      error: error => {
-        console.error('Failed to load OpenAI config:', error);
-      }
-    });
-  }
-
   /**
    * Show configuration modal
    */
   onShowConfigModal(): void {
-    this.loadOpenAiConfig();
+    this.checkAiConfiguration();
     this.showConfigModal = true;
   }
 
@@ -534,23 +518,39 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
   /**
    * Save OpenAI configuration
    */
-  onSaveOpenAiConfig(): void {
-    if (!this.openAiConfig.apiKey.trim()) {
-      this.message.error('API Key is required');
+  onSaveAiProviderConfig(): void {
+    if (!this.aiProviderConfig.apiKey?.trim()) {
+      this.message.error('Please enter API Key');
+      return;
+    }
+
+    if (!this.aiProviderConfig.code?.trim()) {
+      this.message.error('Please select a provider');
+      return;
+    }
+
+    if (!this.aiProviderConfig.baseUrl?.trim()) {
+      this.message.error('Please enter Base URL');
+      return;
+    }
+
+    if (!this.aiProviderConfig.model?.trim()) {
+      this.message.error('Please enter Model');
       return;
     }
 
     // Always enable when saving an API key
-    this.openAiConfig.enable = true;
+    this.aiProviderConfig.enable = true;
+    this.aiProviderConfig.status = true;
 
     this.configLoading = true;
     this.message.info('Validating API key...', { nzDuration: 2000 });
 
-    this.openAiConfigService.saveOpenAiConfig(this.openAiConfig).subscribe({
+    
this.generalConfigSvc.saveModelProviderConfig(this.aiProviderConfig).subscribe({
       next: response => {
         this.configLoading = false;
         if (response.code === 0) {
-          this.message.success('OpenAI API key validated and saved 
successfully!');
+          this.message.success('Model Provider configuration saved 
successfully!');
           this.showConfigModal = false;
           this.isOpenAiConfigured = true;
           this.loadConversations();
@@ -569,4 +569,32 @@ export class ChatComponent implements OnInit, 
AfterViewChecked {
       }
     });
   }
+
+  /**
+   * Handle provider selection change
+   */
+  onProviderChange(provider: string): void {
+    const selectedProvider = this.providerOptions.find(p => p.value === 
provider);
+    if (selectedProvider) {
+      this.aiProviderConfig.code = provider;
+      // Auto-fill default values if current values are empty
+      if (!this.aiProviderConfig.baseUrl) {
+        this.aiProviderConfig.baseUrl = selectedProvider.defaultBaseUrl;
+      }
+      if (!this.aiProviderConfig.model) {
+        this.aiProviderConfig.model = selectedProvider.defaultModel;
+      }
+    }
+  }
+
+  /**
+   * Reset to default values for selected provider
+   */
+  resetToDefaults(): void {
+    const selectedProvider = this.providerOptions.find(p => p.value === 
this.aiProviderConfig.code);
+    if (selectedProvider) {
+      this.aiProviderConfig.baseUrl = selectedProvider.defaultBaseUrl;
+      this.aiProviderConfig.model = selectedProvider.defaultModel;
+    }
+  }
 }
diff --git a/web-app/src/app/shared/shared.module.ts 
b/web-app/src/app/shared/shared.module.ts
index ee9e6e62f..0d499cddf 100644
--- a/web-app/src/app/shared/shared.module.ts
+++ b/web-app/src/app/shared/shared.module.ts
@@ -20,7 +20,6 @@ import { NzTagModule } from 'ng-zorro-antd/tag';
 // Icon to be used for registration
 const icons: IconDefinition[] = [RobotOutline, CloseOutline, SendOutline];
 
-import { AiBotComponent } from './components/ai-bot/ai-bot.component';
 import { AiChatModule } from './components/ai-chat/ai-chat.module';
 import { ConfigurableFieldComponent } from 
'./components/configurable-field/configurable-field.component';
 import { FormFieldComponent } from 
'./components/form-field/form-field.component';
@@ -43,7 +42,6 @@ const COMPONENTS: Array<Type<void>> = [
   ConfigurableFieldComponent,
   FormFieldComponent,
   MonitorSelectMenuComponent,
-  AiBotComponent,
   LabelSelectorComponent
 ];
 const DIRECTIVES: Array<Type<void>> = [TimezonePipe, I18nElsePipe, 
ElapsedTimePipe];


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to