From b661e9c19e36fd6d22430d79815a2fc384673aba Mon Sep 17 00:00:00 2001 From: Jin Hai Date: Tue, 23 Jun 2026 10:26:31 +0800 Subject: [PATCH] Go CLI: admin list providers (#16243) ### What problem does this PR solve? ``` RAGFlow(admin)> list providers; +----------------------+-------------------------------------------------------------+ | command | error | +----------------------+-------------------------------------------------------------+ | list_model_providers | 'list model providers' is implemented in enterprise edition | +----------------------+-------------------------------------------------------------+ RAGFlow(admin)> add provider 'zhipu-ai'; +-------------+-----------------------------------------------------------+ | field | value | +-------------+-----------------------------------------------------------+ | command | add_model_provider | | error | 'add model provider' is implemented in enterprise edition | | provider_id | admin | | user_id | zhipu-ai | +-------------+-----------------------------------------------------------+ RAGFlow(admin)> delete provider 'zhipu-ai'; +-------------+--------------------------------------------------------------+ | field | value | +-------------+--------------------------------------------------------------+ | command | delete_model_provider | | error | 'delete model provider' is implemented in enterprise edition | | provider_id | admin | | user_id | zhipu-ai | +-------------+--------------------------------------------------------------+ RAGFlow(admin)> add provider 'zhipu-ai' instance 'instance1'; +---------------+-----------------------------------------------------------+ | field | value | +---------------+-----------------------------------------------------------+ | command | add_model_instance | | error | 'add model instance' is implemented in enterprise edition | | instance_name | instance1 | | provider_id | zhipu-ai | | user_id | admin | +---------------+-----------------------------------------------------------+ RAGFlow(admin)> delete provider 'zhipu-ai' instance 'test' +-------------+--------------------------------------------------------------+ | field | value | +-------------+--------------------------------------------------------------+ | instances | [test] | | provider_id | zhipu-ai | | user_id | admin | | command | delete_model_provider | | error | 'delete model instance' is implemented in enterprise edition | +-------------+--------------------------------------------------------------+ RAGFlow(admin)> add provider 'zhipu-ai' instance 'instance1' model 'xxx'; +---------------+--------------------------------------------------+ | field | value | +---------------+--------------------------------------------------+ | command | add_model | | error | 'add model' is implemented in enterprise edition | | instance_name | instance1 | | model_names | [xxx] | | provider_id | zhipu-ai | | user_id | admin | +---------------+--------------------------------------------------+ RAGFlow(admin)> delete provider 'zhipu-ai' instance 'test' model 'xxx'; +---------------+------------------------------------------------------+ | field | value | +---------------+------------------------------------------------------+ | command | delete_model_provider | | error | 'delete models' is implemented in enterprise edition | | instance_name | test | | models | [xxx] | | provider_id | zhipu-ai | | user_id | admin | +---------------+------------------------------------------------------+ ``` ### Type of change - [x] New Feature (non-breaking change which adds functionality) --------- Signed-off-by: Jin Hai --- internal/admin/enterprise_handler.go | 230 ++++++++++++++++++-- internal/admin/enterprise_service.go | 79 +++++++ internal/admin/router.go | 14 +- internal/cli/admin_command.go | 300 +++++++++++++++++++++++++++ internal/cli/admin_parser.go | 190 ++++++++++++++--- internal/cli/cli_http.go | 16 +- internal/cli/parser.go | 4 +- internal/cli/response.go | 8 + internal/cli/user_command.go | 1 - internal/cli/user_parser.go | 11 - internal/handler/providers.go | 2 +- 11 files changed, 790 insertions(+), 65 deletions(-) diff --git a/internal/admin/enterprise_handler.go b/internal/admin/enterprise_handler.go index 863df29a76..282b13530a 100644 --- a/internal/admin/enterprise_handler.go +++ b/internal/admin/enterprise_handler.go @@ -291,7 +291,7 @@ func (h *Handler) ResetRoleDefaultModel(c *gin.Context) { success(c, result, "Role default model set successfully") } -func (h *Handler) ListProviders(c *gin.Context) { +func (h *Handler) ListModelProviders(c *gin.Context) { keywords := "" if queryKeywords := c.Query("available"); queryKeywords != "" { @@ -300,23 +300,40 @@ func (h *Handler) ListProviders(c *gin.Context) { // convert keywords to small case keywords = strings.ToLower(keywords) - if keywords == "true" { - // list pool providers - providers, err := dao.GetModelProviderManager().ListProviders() - if err != nil { - c.JSON(http.StatusOK, gin.H{ - "code": common.CodeNotFound, - "message": err.Error(), - }) - return - } - c.JSON(http.StatusOK, gin.H{ - "code": 0, - "message": "success", - "data": providers, - }) + result, err := h.service.ListModelProviders() + if err != nil { + errorResponse(c, err.Error(), 500) + return } + + success(c, result, "List model providers successfully") +} + +type AddProviderRequest struct { + ProviderName string `json:"provider_name" binding:"required"` +} + +func (h *Handler) AddModelProvider(c *gin.Context) { + var req AddProviderRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + "data": false, + }) + return + } + + userID := c.GetString("user_id") + + result, err := h.service.AddModelProvider(req.ProviderName, userID) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model provider added successfully") } func (h *Handler) ShowProvider(c *gin.Context) { @@ -344,6 +361,31 @@ func (h *Handler) ShowProvider(c *gin.Context) { }) } +type DeleteProviderRequest struct { + ProviderNames []string `json:"provider_names" binding:"required"` +} + +func (h *Handler) DeleteModelProvider(c *gin.Context) { + var req DeleteProviderRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Provider name is required", + }) + return + } + + userID := c.GetString("user_id") + + result, err := h.service.DeleteModelProviders(userID, req.ProviderNames) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model provider deleted successfully") +} + func (h *Handler) ListModels(c *gin.Context) { providerName := c.Param("provider_name") if providerName == "" { @@ -400,6 +442,162 @@ func (h *Handler) ShowModel(c *gin.Context) { }) } +type AddModelInstanceRequest struct { + InstanceName string `json:"instance_name" binding:"required"` +} + +func (h *Handler) AddModelInstance(c *gin.Context) { + var req AddModelInstanceRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + "data": false, + }) + return + } + + providerName := c.Param("provider_name") + if providerName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Provider name is required", + }) + return + } + + userID := c.GetString("user_id") + + result, err := h.service.AddModelInstance(userID, providerName, req.InstanceName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model instance added successfully") +} + +type DropModelInstanceRequest struct { + InstanceNames []string `json:"instance_names" binding:"required"` +} + +func (h *Handler) DeleteModelInstance(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 DropModelInstanceRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + + userID := c.GetString("user_id") + + result, err := h.service.DeleteModelInstances(userID, providerName, req.InstanceNames) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model provider added successfully") +} + +type AddModelsRequest struct { + ModelNames []string `json:"model_names" binding:"required"` +} + +func (h *Handler) AddModels(c *gin.Context) { + var req AddModelsRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + "data": false, + }) + return + } + + providerName := c.Param("provider_name") + if providerName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Provider name is required", + }) + return + } + + instanceName := c.Param("instance_name") + if instanceName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Instance name is required", + }) + return + } + + userID := c.GetString("user_id") + + result, err := h.service.AddModels(userID, providerName, instanceName, req.ModelNames) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Models added successfully") +} + +type DropModelsRequest struct { + ModelNames []string `json:"model_names" binding:"required"` +} + +func (h *Handler) DeleteModels(c *gin.Context) { + providerName := c.Param("provider_name") + if providerName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Provider name is required", + }) + return + } + + instanceName := c.Param("instance_name") + if instanceName == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": 400, + "message": "Instance name is required", + }) + return + } + + var req DropModelsRequest + if err := c.ShouldBindJSON(&req); err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + + userID := c.GetString("user_id") + + result, err := h.service.DeleteModels(userID, providerName, instanceName, req.ModelNames) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model deleted successfully") +} + type ListModelsOrShowModelRequest struct { ModelName string `json:"model_name"` } diff --git a/internal/admin/enterprise_service.go b/internal/admin/enterprise_service.go index fd25592008..7396e5e168 100644 --- a/internal/admin/enterprise_service.go +++ b/internal/admin/enterprise_service.go @@ -163,6 +163,85 @@ func (s *Service) ResetRoleDefaultModel(roleName, modelType string) (map[string] }, nil } +// ListModelProviders list model providers +func (s *Service) ListModelProviders() ([]map[string]interface{}, error) { + return []map[string]interface{}{ + { + "command": "list_model_providers", + "error": "'list model providers' is implemented in enterprise edition", + }, + }, nil +} + +// AddModelProvider Add model provider +func (s *Service) AddModelProvider(userID, providerName string) (map[string]interface{}, error) { + + return map[string]interface{}{ + "command": "add_model_provider", + "user_id": userID, + "provider_id": providerName, + "error": "'add model provider' is implemented in enterprise edition", + }, nil +} + +// DeleteModelProviders delete model providers +func (s *Service) DeleteModelProviders(userID string, providerNames []string) (map[string]interface{}, error) { + return map[string]interface{}{ + "command": "delete_model_providers", + "user_id": userID, + "provider_names": providerNames, + "error": "'delete model providers' is implemented in enterprise edition", + }, nil +} + +// AddModelInstance Add model instance +func (s *Service) AddModelInstance(userID, providerName, instanceName string) (map[string]interface{}, error) { + + return map[string]interface{}{ + "command": "add_model_instance", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "error": "'add model instance' is implemented in enterprise edition", + }, nil +} + +// DeleteModelInstances delete model instances +func (s *Service) DeleteModelInstances(userID, providerName string, instances []string) (map[string]interface{}, error) { + return map[string]interface{}{ + "command": "delete_model_instances", + "user_id": userID, + "provider_id": providerName, + "instances": instances, + "error": "'delete model instances' is implemented in enterprise edition", + }, nil +} + +// AddModels Add models +func (s *Service) AddModels(userID, providerName, instanceName string, modelNames []string) (map[string]interface{}, error) { + + return map[string]interface{}{ + "command": "add_model", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "model_names": modelNames, + "error": "'add model' is implemented in enterprise edition", + }, nil +} + +// DeleteModels delete models +func (s *Service) DeleteModels(userID, providerName, instanceName string, models []string) (map[string]interface{}, error) { + return map[string]interface{}{ + "command": "delete_models", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "models": models, + "error": "'delete models' is implemented in enterprise edition", + }, nil +} + // ListAllModels list all models func (s *Service) ListAllModels() ([]map[string]interface{}, error) { return []map[string]interface{}{ diff --git a/internal/admin/router.go b/internal/admin/router.go index 35b4b47132..c5e9420cf4 100644 --- a/internal/admin/router.go +++ b/internal/admin/router.go @@ -175,24 +175,24 @@ func (r *Router) Setup(engine *gin.Engine) { // Providers and models provider := protected.Group("/providers") { - provider.GET("/", r.handler.ListProviders) - // provider.PUT("/", r.handler.AddProvider) + provider.GET("/", r.handler.ListModelProviders) + provider.POST("/", r.handler.AddModelProvider) provider.GET("/:provider_name", r.handler.ShowProvider) - // provider.DELETE("/:provider_name", r.handler.DeleteProvider) + provider.DELETE("/", r.handler.DeleteModelProvider) provider.GET("/:provider_name/models", r.handler.ListModels) provider.GET("/:provider_name/models/:model_name", r.handler.ShowModel) - // provider.POST("/:provider_name/instances", r.handler.CreateProviderInstance) + provider.POST("/:provider_name/instances", r.handler.AddModelInstance) // provider.GET("/:provider_name/instances", r.handler.ListProviderInstances) // provider.GET("/:provider_name/instances/:instance_name", r.handler.ShowProviderInstance) // provider.GET("/:provider_name/instances/:instance_name/balance", r.handler.ShowInstanceBalance) // provider.GET("/:provider_name/instances/:instance_name/connection", r.handler.CheckInstanceConnection) // provider.POST("/:provider_name/connection", r.handler.CheckProviderConnection) // provider.PUT("/:provider_name/instances/:instance_name", r.handler.AlterProviderInstance) - // provider.DELETE("/:provider_name/instances", r.handler.DropProviderInstance) + provider.DELETE("/:provider_name/instances", r.handler.DeleteModelInstance) // provider.GET("/:provider_name/instances/:instance_name/models", r.handler.ListInstanceModels) // provider.PATCH("/:provider_name/instances/:instance_name/models/*model_name", r.handler.EnableOrDisableModel) - // provider.POST("/:provider_name/instances/:instance_name/models", r.handler.AddModel) - // provider.DELETE("/:provider_name/instances/:instance_name/models", r.handler.DropInstanceModels) + provider.POST("/:provider_name/instances/:instance_name/models", r.handler.AddModels) + provider.DELETE("/:provider_name/instances/:instance_name/models", r.handler.DeleteModels) } protected.GET("/all-models", r.handler.ListModelsOrShowModel) diff --git a/internal/cli/admin_command.go b/internal/cli/admin_command.go index 68cfafd641..eb47001ccd 100644 --- a/internal/cli/admin_command.go +++ b/internal/cli/admin_command.go @@ -150,6 +150,36 @@ func (c *CLI) AdminListRolesCommand(cmd *Command) (ResponseIf, error) { return &result, nil } +// AdminListProvidersCommand to list providers command (admin mode only) +func (c *CLI) AdminListProvidersCommand(cmd *Command) (ResponseIf, error) { + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + apiURL := fmt.Sprintf("/admin/providers") + + resp, err := c.AdminServerClient.Request("GET", apiURL, "admin", nil, nil) + if err != nil { + return nil, fmt.Errorf("failed to list providers: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to list providers: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonResponse + + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("list providers failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil +} + // AdminCreateRoleCommand creates a new role (admin mode only) func (c *CLI) AdminCreateRoleCommand(cmd *Command) (ResponseIf, error) { if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { @@ -3194,3 +3224,273 @@ func (c *CLI) AdminRemoveUserIngestionTasksCommand(cmd *Command) (ResponseIf, er result.Duration = resp.Duration return &result, nil } + +// AdminAddProviderCommand add provider +func (c *CLI) AdminAddProviderCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider_name not provided") + } + + payload := map[string]interface{}{ + "provider_name": providerName, + } + + apiURL := fmt.Sprintf("/admin/providers") + + resp, err := c.AdminServerClient.Request("POST", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to add provider %s: %w", providerName, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to add provider %s: HTTP %d, body: %s", providerName, resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("add provider %s failed: invalid JSON (%w)", providerName, err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// AdminAddModelInstanceCommand add model instance +func (c *CLI) AdminAddModelInstanceCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider_name not provided") + } + + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance_name not provided") + } + + payload := map[string]interface{}{ + "instance_name": instanceName, + } + + apiURL := fmt.Sprintf("/admin/providers/%s/instances", providerName) + + resp, err := c.AdminServerClient.Request("POST", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to add model instance %s: %w", instanceName, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to add model instance %s: HTTP %d, body: %s", instanceName, resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("add model instance %s failed: invalid JSON (%w)", instanceName, err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// AdminAddModelsCommand add models +func (c *CLI) AdminAddModelsCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider_name not provided") + } + + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance_name not provided") + } + + modelNames, ok := cmd.Params["model_names"].([]string) + if !ok { + return nil, fmt.Errorf("model_names not provided") + } + + payload := map[string]interface{}{ + "model_names": modelNames, + } + + apiURL := fmt.Sprintf("/admin/providers/%s/instances/%s/models", providerName, instanceName) + + resp, err := c.AdminServerClient.Request("POST", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to add models %s: %w", modelNames, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to add models %s: HTTP %d, body: %s", modelNames, resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("add models %s failed: invalid JSON (%w)", modelNames, err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// AdminDeleteProvidersCommand delete providers +func (c *CLI) AdminDeleteProvidersCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + providerNames, ok := cmd.Params["provider_names"].([]string) + if !ok { + return nil, fmt.Errorf("provider_names not provided") + } + + payload := map[string]interface{}{ + "provider_names": providerNames, + } + + apiURL := fmt.Sprintf("/admin/providers/") + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to remove providers %s: %w", providerNames, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to remove providers %s: HTTP %d, body: %s", providerNames, resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("remove providers %s failed: invalid JSON (%w)", providerNames, err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// AdminDeleteInstancesCommand delete instances +func (c *CLI) AdminDeleteInstancesCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider_name not provided") + } + + instanceNames, ok := cmd.Params["instance_names"].([]string) + if !ok { + return nil, fmt.Errorf("instance_name not provided") + } + + payload := map[string]interface{}{ + "instance_names": instanceNames, + } + + apiURL := fmt.Sprintf("/admin/providers/%s/instances", providerName) + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to remove instance %s: %w", instanceNames, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to remove instance %s: HTTP %d, body: %s", instanceNames, resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("remove instance %s failed: invalid JSON (%w)", instanceNames, err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// AdminDeleteModelsCommand delete models +func (c *CLI) AdminDeleteModelsCommand(cmd *Command) (ResponseIf, error) { + + if c.Config.CLIMode != AdminMode || c.AdminServerClient.LoginToken == nil { + return nil, fmt.Errorf("this command is only allowed in ADMIN mode or already login") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider_name not provided") + } + + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance_name not provided") + } + + modelNames, ok := cmd.Params["model_names"].([]string) + if !ok { + return nil, fmt.Errorf("model_names not provided") + } + + payload := map[string]interface{}{ + "model_names": modelNames, + } + + apiURL := fmt.Sprintf("/admin/providers/%s/instances/%s/models", providerName, instanceName) + + resp, err := c.AdminServerClient.Request("DELETE", apiURL, "admin", nil, payload) + if err != nil { + return nil, fmt.Errorf("failed to remove model %s: %w", modelNames, err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to remove model %s: HTTP %d, body: %s", modelNames, resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("remove model %s failed: invalid JSON (%w)", modelNames, err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} diff --git a/internal/cli/admin_parser.go b/internal/cli/admin_parser.go index 22982b5093..7151f47706 100644 --- a/internal/cli/admin_parser.go +++ b/internal/cli/admin_parser.go @@ -42,7 +42,8 @@ func (p *Parser) parseAdminLoginUser() (*Command, error) { // Optional: PASSWORD 'password' if p.curToken.Type == TokenPassword { p.nextToken() - password, err := p.parseQuotedString() + var password string + password, err = p.parseQuotedString() if err != nil { return nil, err } @@ -81,7 +82,7 @@ func (p *Parser) parseAdminPingServer() (*Command, error) { // endregion // region LIST commands -func (p *Parser) parseAdminListCommand() (*Command, error) { +func (p *Parser) parseAdminListCommands() (*Command, error) { p.nextToken() // consume LIST switch p.curToken.Type { @@ -103,6 +104,8 @@ func (p *Parser) parseAdminListCommand() (*Command, error) { return p.parseListAvailableProviders() case TokenProvider: return p.parseAdminListProviderModels() + case TokenProviders: + return p.parseAdminListProviders() case TokenModels: return p.parseAdminListModels() case TokenUser: @@ -263,6 +266,16 @@ func (p *Parser) parseAdminListProviderModels() (*Command, error) { return cmd, nil } +// parseAdminListProviders parses LIST PROVIDERS command +func (p *Parser) parseAdminListProviders() (*Command, error) { + p.nextToken() // consume PROVIDERS + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return NewCommand("admin_list_providers"), nil +} + func (p *Parser) parseAdminListModels() (*Command, error) { p.nextToken() // consume MODELS cmd := NewCommand("admin_list_all_models") @@ -1007,29 +1020,6 @@ func (p *Parser) parseAdminDropRole() (*Command, error) { return cmd, nil } -func (p *Parser) parseAdminDropModelProvider() (*Command, error) { - p.nextToken() // consume MODEL - if p.curToken.Type != TokenProvider { - return nil, fmt.Errorf("expected PROVIDER") - } - p.nextToken() - - providerName, err := p.parseQuotedString() - if err != nil { - return nil, err - } - - cmd := NewCommand("drop_model_provider") - cmd.Params["provider_name"] = providerName - - p.nextToken() - // Semicolon is optional - if p.curToken.Type == TokenSemicolon { - p.nextToken() - } - return cmd, nil -} - func (p *Parser) parseAdminDropDataset() (*Command, error) { p.nextToken() // consume DATASET datasetName, err := p.parseQuotedString() @@ -1741,23 +1731,171 @@ func (p *Parser) parseAdminAddCommand() (*Command, error) { return p.parseAddAPIServer() case TokenAdmin: return p.parseAddAdminServer() + case TokenProvider: + return p.parseAdminAddModelProvider() default: return nil, fmt.Errorf("unknown ADD target: %s", p.curToken.Value) } } -func (p *Parser) parseAdminDeleteCommand() (*Command, error) { +// ADD PROVIDER +// ADD PROVIDER INSTANCE +func (p *Parser) parseAdminAddModelProvider() (*Command, error) { + p.nextToken() // consume PROVIDER + + providerName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected provider name: %w", err) + } + p.nextToken() + + if p.curToken.Type == TokenInstance { + return p.parseAdminAddModelInstance(providerName) + } + + cmd := NewCommand("admin_add_provider") + cmd.Params["provider_name"] = providerName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +func (p *Parser) parseAdminAddModelInstance(providerName string) (*Command, error) { + p.nextToken() // consume INSTANCE + + instanceName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model instance name: %w", err) + } + p.nextToken() + + if p.curToken.Type == TokenModel { + return p.parseAdminAddModel(providerName, instanceName) + } + + cmd := NewCommand("admin_add_model_instance") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +func (p *Parser) parseAdminAddModel(providerName, instanceName string) (*Command, error) { + p.nextToken() // consume MODEL + + modelName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model name: %w", err) + } + p.nextToken() + + cmd := NewCommand("admin_add_models") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + cmd.Params["model_names"] = []string{modelName} + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +func (p *Parser) parseAdminDeleteCommands() (*Command, error) { p.nextToken() // consume DELETE switch p.curToken.Type { case TokenAPI: return p.parseDeleteAPIServer() case TokenAdmin: return p.parseDeleteAdminServer() + case TokenProvider: + return p.parseAdminDeleteProvider() default: return nil, fmt.Errorf("unknown ADD target: %s", p.curToken.Value) } } +// DELETE PROVIDER command +// DELETE PROVIDER INSTANCE command +// DELETE PROVIDER INSTANCE MODEL +func (p *Parser) parseAdminDeleteProvider() (*Command, error) { + p.nextToken() // consume PROVIDER + + providerName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected provider name: %w", err) + } + p.nextToken() + + if p.curToken.Type == TokenInstance { + return p.parseAdminDeleteModelInstance(providerName) + } + + cmd := NewCommand("admin_delete_model_providers") + cmd.Params["provider_names"] = []string{providerName} + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +// DELETE PROVIDER INSTANCE command +// DELETE PROVIDER INSTANCE MODEL +func (p *Parser) parseAdminDeleteModelInstance(providerName string) (*Command, error) { + p.nextToken() // consume INSTANCE + + instanceName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model instance name: %w", err) + } + p.nextToken() + + if p.curToken.Type == TokenModel { + return p.parseAdminDeleteModel(providerName, instanceName) + } + + cmd := NewCommand("admin_delete_model_instance") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_names"] = []string{instanceName} + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +// DELETE PROVIDER INSTANCE MODEL +func (p *Parser) parseAdminDeleteModel(providerName, instanceName string) (*Command, error) { + p.nextToken() // consume MODEL + + modelName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model name: %w", err) + } + p.nextToken() + + cmd := NewCommand("admin_delete_model") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + cmd.Params["model_names"] = []string{modelName} + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + func (p *Parser) parseAdminSaveCommand() (*Command, error) { p.nextToken() // consume SAVE switch p.curToken.Type { diff --git a/internal/cli/cli_http.go b/internal/cli/cli_http.go index f8035fdcb5..3473225e42 100644 --- a/internal/cli/cli_http.go +++ b/internal/cli/cli_http.go @@ -125,6 +125,8 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.ListInstanceModels(cmd) case "admin_show_model": return c.CommonShowModel(cmd) + case "admin_list_providers": + return c.AdminListProvidersCommand(cmd) case "admin_list_all_models": return c.ListAllModels(cmd) case "list_admin_tasks": @@ -227,7 +229,19 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.AdminStopUserIngestionTasksCommand(cmd) case "admin_remove_user_ingestion_tasks_command": return c.AdminRemoveUserIngestionTasksCommand(cmd) - // TODO: Implement other commands + case "admin_add_provider": + return c.AdminAddProviderCommand(cmd) + case "admin_add_model_instance": + return c.AdminAddModelInstanceCommand(cmd) + case "admin_add_models": + return c.AdminAddModelsCommand(cmd) + case "admin_delete_model_providers": + return c.AdminDeleteProvidersCommand(cmd) + case "admin_delete_model_instance": + return c.AdminDeleteInstancesCommand(cmd) + case "admin_delete_model": + return c.AdminDeleteModelsCommand(cmd) + // TODO: Implement other commands case "show_admin_server": return c.ShowAdminServer(cmd) case "show_api_server": diff --git a/internal/cli/parser.go b/internal/cli/parser.go index 96de3a8862..c3db5363a5 100644 --- a/internal/cli/parser.go +++ b/internal/cli/parser.go @@ -87,7 +87,7 @@ func (p *Parser) parseAdminCommand() (*Command, error) { case TokenPing: return p.parseAdminPingServer() case TokenList: - return p.parseAdminListCommand() + return p.parseAdminListCommands() case TokenShow: return p.parseAdminShowCommands() case TokenCheck: @@ -123,7 +123,7 @@ func (p *Parser) parseAdminCommand() (*Command, error) { case TokenAdd: return p.parseAdminAddCommand() case TokenDelete: - return p.parseAdminDeleteCommand() + return p.parseAdminDeleteCommands() case TokenSave: return p.parseAdminSaveCommand() case TokenUse: diff --git a/internal/cli/response.go b/internal/cli/response.go index fcbea45844..5fd19a21d9 100644 --- a/internal/cli/response.go +++ b/internal/cli/response.go @@ -88,6 +88,14 @@ func (r *ModelsResponse) PrintOut() { } } +type UserIndexResponse struct { + InternalData CommonDataResponse +} + +func (r *UserIndexResponse) TimeCost() float64 { + return r.InternalData.Duration +} + type CommonDataResponse struct { Code int `json:"code"` Data map[string]interface{} `json:"data"` diff --git a/internal/cli/user_command.go b/internal/cli/user_command.go index 639db12e1e..edcadd95ed 100644 --- a/internal/cli/user_command.go +++ b/internal/cli/user_command.go @@ -1105,7 +1105,6 @@ func (c *CLI) DropMetadataStore(cmd *Command) (ResponseIf, error) { // AddProvider creates a new model provider // ADD PROVIDER -// ADD PROVIDER func (c *CLI) AddProvider(cmd *Command) (ResponseIf, error) { if c.Config.CLIMode != APIMode { return nil, fmt.Errorf("this command is only allowed in USER mode") diff --git a/internal/cli/user_parser.go b/internal/cli/user_parser.go index 2dd262a88f..707da91084 100644 --- a/internal/cli/user_parser.go +++ b/internal/cli/user_parser.go @@ -736,7 +736,6 @@ func (p *Parser) parseCreateModelProvider() (*Command, error) { // parseAddProvider parses ADD PROVIDER commands // ADD PROVIDER -// ADD PROVIDER func (p *Parser) parseAddProvider() (*Command, error) { p.nextToken() // consume PROVIDER @@ -750,16 +749,6 @@ func (p *Parser) parseAddProvider() (*Command, error) { p.nextToken() - // Check if api_key is provided (optional) - if p.curToken.Type == TokenQuotedString { - apiKey, err := p.parseQuotedString() - if err != nil { - return nil, fmt.Errorf("expected api key: %w", err) - } - cmd.Params["api_key"] = apiKey - p.nextToken() - } - // Semicolon is optional if p.curToken.Type == TokenSemicolon { p.nextToken() diff --git a/internal/handler/providers.go b/internal/handler/providers.go index 76a865bca7..7b11a360f0 100644 --- a/internal/handler/providers.go +++ b/internal/handler/providers.go @@ -144,7 +144,7 @@ func (h *ProviderHandler) DeleteProvider(c *gin.Context) { userID := c.GetString("user_id") - errorCode, err := h.modelProviderService.DeleteModelProvider(providerName, userID) + errorCode, err := h.modelProviderService.DeleteModelProvider(userID, providerName) if err != nil { c.JSON(http.StatusOK, gin.H{ "code": errorCode,