From 9f969feb894ad317be8520524c9226530822b65f Mon Sep 17 00:00:00 2001 From: Haruko386 Date: Tue, 2 Jun 2026 19:32:41 +0800 Subject: [PATCH] feat[Go] implement check connection by using apikey and region (#15475) ### What problem does this PR solve? **Verified from PostMan** GET http://127.0.0.1:9384/api/v1/providers/gitee/connection ```json body: { "api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX", "region": "default" } resp: { "code": 0, "message": "success" } ``` GET http://127.0.0.1:9384/api/v1/providers/gitee/connection ```json body: { "api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX", "region": "deprecated" } resp: { "code": 0, "message": "success" } ``` GET http://127.0.0.1:9384/api/v1/providers/gitee/connection ```json body: { "api_key": "XXXXXXXXXXXXXXXXXXXXXXXXXXXX", "region": "china" } resp: { "code": 0, "message": "success" } ``` GET http://127.0.0.1:9384/api/v1/providers/lmstudio/connection ```json body: { "api_key": "", "region": "test" } resp: { "code": 0, "message": "success" } ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --- internal/dao/tenant_model_instance.go | 9 +++++ internal/handler/providers.go | 52 +++++++++++++++++++++++++-- internal/router/router.go | 3 +- internal/service/model_service.go | 51 +++++++++++++++++++++++++- 4 files changed, 111 insertions(+), 4 deletions(-) diff --git a/internal/dao/tenant_model_instance.go b/internal/dao/tenant_model_instance.go index 97eb4304e..d8d7f6026 100644 --- a/internal/dao/tenant_model_instance.go +++ b/internal/dao/tenant_model_instance.go @@ -41,6 +41,15 @@ func (dao *TenantModelInstanceDAO) GetAllInstancesByProviderID(providerID string return instances, nil } +func (dao *TenantModelInstanceDAO) GetInstanceByApiKey(apiKey, providerID string) (*entity.TenantModelInstance, error) { + var instance entity.TenantModelInstance + err := DB.Where("api_key = ? && provider_id = ?", apiKey, providerID).First(&instance).Error + if err != nil { + return nil, err + } + return &instance, nil +} + func (dao *TenantModelInstanceDAO) GetByProviderIDAndInstanceName(providerID, instanceName string) (*entity.TenantModelInstance, error) { var instance entity.TenantModelInstance err := DB.Where("provider_id = ? AND instance_name = ?", providerID, instanceName).First(&instance).Error diff --git a/internal/handler/providers.go b/internal/handler/providers.go index 9c7425050..ef5fc0066 100644 --- a/internal/handler/providers.go +++ b/internal/handler/providers.go @@ -387,7 +387,42 @@ func (h *ProviderHandler) ShowInstanceBalance(c *gin.Context) { }) } -func (h *ProviderHandler) CheckProviderConnection(c *gin.Context) { +func (h *ProviderHandler) CheckConnection(c *gin.Context) { + providerName := c.Param("provider_name") + if providerName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Provider name is required", + }) + return + } + + var req service.CheckConnectionRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": err.Error(), + }) + return + } + + userID := c.GetString("user_id") + errCode, err := h.modelProviderService.CheckConnection(providerName, req.APIKey, req.Region, req.BaseURL, userID) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": errCode, + "message": err.Error(), + }) + return + } + + c.JSON(http.StatusOK, gin.H{ + "code": 0, + "message": "success", + }) +} + +func (h *ProviderHandler) CheckInstanceConnection(c *gin.Context) { providerName := c.Param("provider_name") if providerName == "" { c.JSON(http.StatusBadRequest, gin.H{ @@ -408,8 +443,21 @@ func (h *ProviderHandler) CheckProviderConnection(c *gin.Context) { userID := c.GetString("user_id") + instanceInfo, code, err := h.modelProviderService.ShowProviderInstance(providerName, instanceName, userID) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": code, + "message": err.Error(), + }) + return + } + + apikey, _ := instanceInfo["apikey"].(string) + region, _ := instanceInfo["region"].(string) + baseURL, _ := instanceInfo["base_url"].(string) + // Get tenant ID from user - errorCode, err := h.modelProviderService.CheckProviderConnection(providerName, instanceName, userID) + errorCode, err := h.modelProviderService.CheckConnection(providerName, apikey, region, baseURL, userID) if err != nil { c.JSON(http.StatusOK, gin.H{ "code": errorCode, diff --git a/internal/router/router.go b/internal/router/router.go index 9ea1075b4..bc56eb5aa 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -319,7 +319,8 @@ func (r *Router) Setup(engine *gin.Engine) { provider.GET("/:provider_name/instances", r.providerHandler.ListProviderInstances) provider.GET("/:provider_name/instances/:instance_name", r.providerHandler.ShowProviderInstance) provider.GET("/:provider_name/instances/:instance_name/balance", r.providerHandler.ShowInstanceBalance) - provider.GET("/:provider_name/instances/:instance_name/connection", r.providerHandler.CheckProviderConnection) + provider.GET("/:provider_name/instances/:instance_name/connection", r.providerHandler.CheckInstanceConnection) + provider.GET("/:provider_name/connection", r.providerHandler.CheckConnection) provider.GET("/:provider_name/instances/:instance_name/tasks", r.providerHandler.ListTasks) provider.GET("/:provider_name/instances/:instance_name/tasks/:task_id", r.providerHandler.ShowTask) provider.PUT("/:provider_name/instances/:instance_name", r.providerHandler.AlterProviderInstance) diff --git a/internal/service/model_service.go b/internal/service/model_service.go index 768fe0dae..cee461b59 100644 --- a/internal/service/model_service.go +++ b/internal/service/model_service.go @@ -88,6 +88,14 @@ type ModelProviderService struct { userTenantDAO *dao.UserTenantDAO } +// CheckConnectionRequest carries the credentials and optional instance selector +// for checking provider connectivity without creating a new model instance. +type CheckConnectionRequest struct { + APIKey string `json:"api_key"` + Region string `json:"region"` + BaseURL string `json:"base_url"` +} + func (m *ModelProviderService) AddModelProvider(providerName, userID string) (common.ErrorCode, error) { _, err := dao.GetModelProviderManager().GetProviderByName(providerName) @@ -364,7 +372,9 @@ func (m *ModelProviderService) ShowProviderInstance(providerName, instanceName, "instanceName": instance.InstanceName, "providerID": instance.ProviderID, "status": instance.Status, + "apikey": instance.APIKey, "region": extra["region"], + "base_url": extra["base_url"], } return result, common.CodeSuccess, nil @@ -423,7 +433,46 @@ func (m *ModelProviderService) ShowInstanceBalance(providerName, instanceName, u return result, common.CodeSuccess, nil } -func (m *ModelProviderService) CheckProviderConnection(providerName, instanceName, userID string) (common.ErrorCode, error) { +func (m *ModelProviderService) CheckConnection(providerName, apiKey, region, baseURL string, userID string) (common.ErrorCode, error) { + providerInfo := dao.GetModelProviderManager().FindProvider(providerName) + if providerInfo == nil { + return common.CodeServerError, fmt.Errorf("provider %s not found", providerName) + } + + apiKey = strings.TrimSpace(apiKey) + region = strings.TrimSpace(region) + baseURL = strings.TrimSpace(baseURL) + if region == "" { + region = "default" + } + + driver := providerInfo.ModelDriver + if strings.EqualFold(providerInfo.Class, "local") { + if baseURL == "" { + return common.CodeDataError, fmt.Errorf("base_url is required for local provider %s", providerName) + } + + var err error + driver, err = newModelDriverForBaseURL(driver, providerName, region, baseURL) + if err != nil { + return common.CodeServerError, err + } + } + + apiConfig := &modelModule.APIConfig{ + ApiKey: &apiKey, + Region: ®ion, + } + + err := driver.CheckConnection(apiConfig) + if err != nil { + return common.CodeServerError, err + } + + return common.CodeSuccess, nil +} + +func (m *ModelProviderService) CheckInstanceConnection(providerName, instanceName, userID string) (common.ErrorCode, error) { // Get tenant ID from user tenants, err := m.userTenantDAO.GetByUserIDAndRole(userID, "owner")