diff --git a/internal/admin/enterprise_handler.go b/internal/admin/enterprise_handler.go index 282b13530a..3e9572cc07 100644 --- a/internal/admin/enterprise_handler.go +++ b/internal/admin/enterprise_handler.go @@ -442,6 +442,198 @@ func (h *Handler) ShowModel(c *gin.Context) { }) } +func (h *Handler) ListModelInstances(c *gin.Context) { + 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.ListModelInstances(userID, providerName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model instances listed successfully") +} + +func (h *Handler) ShowProviderInstance(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 + } + userID := c.GetString("user_id") + + result, err := h.service.ShowProviderInstance(userID, providerName, instanceName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model instance shown successfully") +} + +func (h *Handler) ShowProviderInstanceBalance(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 + } + userID := c.GetString("user_id") + + result, err := h.service.ShowProviderInstanceBalance(userID, providerName, instanceName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model instance balance shown successfully") +} + +func (h *Handler) CheckInstanceConnection(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 + } + userID := c.GetString("user_id") + + result, err := h.service.CheckInstanceConnection(userID, providerName, instanceName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model instance connection checked successfully") +} + +type CheckConnectionRequest struct { + APIKey string `json:"api_key"` + Region string `json:"region"` + BaseURL string `json:"base_url"` +} + +func (h *Handler) CheckProviderConnection(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 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") + + result, err := h.service.CheckProviderConnection(userID, providerName, req.Region, req.APIKey, req.BaseURL) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model instance connection checked successfully") +} + +type AlterProviderInstanceRequest struct { + ModelName string `json:"model_name"` + APIKey string `json:"api_key"` +} + +func (h *Handler) AlterProviderInstance(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 AlterProviderInstanceRequest + 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") + if userID == "" { + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeUnauthorized, + "message": "Unauthorized", + }) + return + } + + result, err := h.service.AlterProviderInstance(userID, providerName, instanceName, req.ModelName, req.APIKey) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Model instance altered successfully") +} + type AddModelInstanceRequest struct { InstanceName string `json:"instance_name" binding:"required"` } @@ -511,6 +703,92 @@ func (h *Handler) DeleteModelInstance(c *gin.Context) { success(c, result, "Model provider added successfully") } +func (h *Handler) ListInstanceModels(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 + } + + userID := c.GetString("user_id") + + result, err := h.service.ListInstanceModels(userID, providerName, instanceName) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Models listed successfully") +} + +type EnableOrDisableModelRequest struct { + ModelID string `json:"model_id"` + Status string `json:"status"` +} + +func (h *Handler) EnableOrDisableModel(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 EnableOrDisableModelRequest + if err := c.ShouldBindJSON(&req); err != nil { + println("JSON bind error: %v (type: %T)", err, err) + c.JSON(http.StatusOK, gin.H{ + "code": common.CodeBadRequest, + "message": err.Error(), + }) + return + } + + modelID := strings.TrimSpace(req.ModelID) + modelName := strings.TrimPrefix(c.Param("model_name"), "/") + modelName = strings.TrimSpace(modelName) + if modelName == "" && modelID == "" { + c.JSON(http.StatusBadRequest, gin.H{ + "code": common.CodeBadRequest, + "message": "model_name or model_id is required", + }) + return + } + + userID := c.GetString("user_id") + + result, err := h.service.EnableOrDisableModel(userID, providerName, instanceName, modelName, modelID, req.Status) + if err != nil { + errorResponse(c, err.Error(), 500) + return + } + + success(c, result, "Models listed successfully") +} + type AddModelsRequest struct { ModelNames []string `json:"model_names" binding:"required"` } diff --git a/internal/admin/enterprise_service.go b/internal/admin/enterprise_service.go index 7396e5e168..12897cd3e3 100644 --- a/internal/admin/enterprise_service.go +++ b/internal/admin/enterprise_service.go @@ -194,6 +194,78 @@ func (s *Service) DeleteModelProviders(userID string, providerNames []string) (m }, nil } +// ListModelInstances list model instances +func (s *Service) ListModelInstances(userID, providerName string) ([]map[string]interface{}, error) { + + return []map[string]interface{}{ + { + "command": "list_model_instances", + "user_id": userID, + "provider_id": providerName, + "error": "'list model instances' is implemented in enterprise edition", + }, + }, nil +} + +// ShowProviderInstance show provider instance +func (s *Service) ShowProviderInstance(userID, providerName, instanceName string) (map[string]interface{}, error) { + + return map[string]interface{}{ + "command": "show_provider_instance", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "error": "'show provider instance' is implemented in enterprise edition", + }, nil +} + +// ShowProviderInstanceBalance show provider instance balance +func (s *Service) ShowProviderInstanceBalance(userID, providerName, instanceName string) (map[string]interface{}, error) { + return map[string]interface{}{ + "command": "show_provider_instance_balance", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "error": "'show provider instance balance' is implemented in enterprise edition", + }, nil +} + +// CheckInstanceConnection check instance connection +func (s *Service) CheckInstanceConnection(userID, providerName, instanceName string) (map[string]interface{}, error) { + return map[string]interface{}{ + "command": "check_instance_connection", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "error": "'check instance connection' is implemented in enterprise edition", + }, nil +} + +// CheckProviderConnection check provider connection +func (s *Service) CheckProviderConnection(userID, providerName, region, apiKey, baseURL string) (map[string]interface{}, error) { + return map[string]interface{}{ + "command": "check_provider_connection", + "user_id": userID, + "provider_id": providerName, + "region": region, + "api_key": apiKey, + "base_url": baseURL, + }, nil +} + +// AlterProviderInstance alter provider instance +func (s *Service) AlterProviderInstance(userID, providerName, instanceName, newInstanceName, newAPIKey string) (map[string]interface{}, error) { + return map[string]interface{}{ + "command": "alter_provider_instance", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "new_instance_name": newInstanceName, + "new_api_key": newAPIKey, + "error": "'alter provider instance' is implemented in enterprise edition", + }, nil +} + // AddModelInstance Add model instance func (s *Service) AddModelInstance(userID, providerName, instanceName string) (map[string]interface{}, error) { @@ -217,6 +289,35 @@ func (s *Service) DeleteModelInstances(userID, providerName string, instances [] }, nil } +// ListInstanceModels list models for instance +func (s *Service) ListInstanceModels(userID, providerName, instanceName string) ([]map[string]interface{}, error) { + return []map[string]interface{}{ + { + "command": "list_instance_models", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "error": "'list instance models' is implemented in enterprise edition", + }, + }, nil +} + +func (s *Service) EnableOrDisableModel(userID, providerName, instanceName, modelName, modelID, status string) (map[string]interface{}, error) { + + return map[string]interface{}{ + "command": "enable_or_disable_model", + "user_id": userID, + "provider_id": providerName, + "instance_name": instanceName, + "model_name": modelName, + "model_id": modelID, + "status": status, + "error": "'enable or disable model' is implemented in enterprise edition", + }, nil +} + +// AddModel Add model + // AddModels Add models func (s *Service) AddModels(userID, providerName, instanceName string, modelNames []string) (map[string]interface{}, error) { diff --git a/internal/admin/router.go b/internal/admin/router.go index c5e9420cf4..e7de27eaee 100644 --- a/internal/admin/router.go +++ b/internal/admin/router.go @@ -182,15 +182,15 @@ func (r *Router) Setup(engine *gin.Engine) { 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.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.GET("/:provider_name/instances", r.handler.ListModelInstances) + provider.GET("/:provider_name/instances/:instance_name", r.handler.ShowProviderInstance) + provider.GET("/:provider_name/instances/:instance_name/balance", r.handler.ShowProviderInstanceBalance) + 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.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.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.AddModels) provider.DELETE("/:provider_name/instances/:instance_name/models", r.handler.DeleteModels) } diff --git a/internal/cli/admin_parser.go b/internal/cli/admin_parser.go index 7151f47706..0efa0ad9d2 100644 --- a/internal/cli/admin_parser.go +++ b/internal/cli/admin_parser.go @@ -103,7 +103,7 @@ func (p *Parser) parseAdminListCommands() (*Command, error) { case TokenAvailable: return p.parseListAvailableProviders() case TokenProvider: - return p.parseAdminListProviderModels() + return p.parseAdminListProviderCommands() case TokenProviders: return p.parseAdminListProviders() case TokenModels: @@ -244,7 +244,7 @@ func (p *Parser) parseAdminListIngestionTasks() (*Command, error) { } // LIST PROVIDER 'provider_name' MODELS; -func (p *Parser) parseAdminListProviderModels() (*Command, error) { +func (p *Parser) parseAdminListProviderCommands() (*Command, error) { p.nextToken() // consume PROVIDER providerName, err := p.parseQuotedString() @@ -253,10 +253,59 @@ func (p *Parser) parseAdminListProviderModels() (*Command, error) { } p.nextToken() + switch p.curToken.Type { + case TokenInstances: + return p.parseAdminListProviderInstances(providerName) + case TokenInstance: + return p.parseAdminListProviderInstance(providerName) + case TokenModels: + return p.parseAdminListProviderModels(providerName) + default: + return nil, fmt.Errorf("unknown LIST target: %s", p.curToken.Value) + } +} + +// LIST PROVIDER 'provider_name' INSTANCE 'instance_name' MODELS; +func (p *Parser) parseAdminListProviderInstance(providerName string) (*Command, error) { + p.nextToken() // consume INSTANCE + + instanceName, err := p.parseQuotedString() + if err != nil { + return nil, err + } + p.nextToken() + if p.curToken.Type != TokenModels { return nil, fmt.Errorf("expected MODELS") } + p.nextToken() + + cmd := NewCommand("admin_list_provider_instance_models") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return cmd, nil +} + +// LIST PROVIDER 'provider_name' INSTANCES; +func (p *Parser) parseAdminListProviderInstances(providerName string) (*Command, error) { + p.nextToken() // consume INSTANCES + cmd := NewCommand("admin_list_provider_instances") + cmd.Params["provider_name"] = providerName + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + + return cmd, nil +} + +func (p *Parser) parseAdminListProviderModels(providerName string) (*Command, error) { p.nextToken() // consume MODELS + cmd := NewCommand("admin_list_provider_models") cmd.Params["provider_name"] = providerName @@ -266,7 +315,7 @@ func (p *Parser) parseAdminListProviderModels() (*Command, error) { return cmd, nil } -// parseAdminListProviders parses LIST PROVIDERS command +// LIST PROVIDERS func (p *Parser) parseAdminListProviders() (*Command, error) { p.nextToken() // consume PROVIDERS // Semicolon is optional @@ -657,13 +706,12 @@ func (p *Parser) parseAdminShowProvider() (*Command, error) { } p.nextToken() - if p.curToken.Type == TokenModel { - // SHOW PROVIDER 'provider_name' MODEL 'model_name' + switch p.curToken.Type { + case TokenInstance: + return p.parseAdminShowProviderInstance(providerName) + case TokenModel: return p.parseAdminShowProviderModel(providerName) - } - - // Semicolon is optional - if p.curToken.Type == TokenSemicolon { + default: p.nextToken() } @@ -672,6 +720,46 @@ func (p *Parser) parseAdminShowProvider() (*Command, error) { return cmd, nil } +// SHOW PROVIDER 'provider_name' INSTANCE 'instance_name'; +func (p *Parser) parseAdminShowProviderInstance(providerName string) (*Command, error) { + p.nextToken() // consume INSTANCE + + instanceName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected instance name: %w", err) + } + p.nextToken() // consume instance_name + + if p.curToken.Type == TokenBalance { + return p.parseAdminShowProviderInstanceBalance(providerName, instanceName) + } + + cmd := NewCommand("admin_show_provider_instance") + cmd.Params["instance_name"] = instanceName + cmd.Params["provider_name"] = providerName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +// SHOW PROVIDER 'provider_name' INSTANCE 'instance_name' BALANCE; +func (p *Parser) parseAdminShowProviderInstanceBalance(providerName, instanceName string) (*Command, error) { + p.nextToken() // consume BALANCE + + cmd := NewCommand("admin_show_provider_instance_balance") + cmd.Params["instance_name"] = instanceName + cmd.Params["provider_name"] = providerName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + // SHOW PROVIDER 'provider_name' MODEL 'model_name'; func (p *Parser) parseAdminShowProviderModel(providerName string) (*Command, error) { p.nextToken() // consume MODEL @@ -760,17 +848,22 @@ func (p *Parser) parseCommonShowPoolModel() (*Command, error) { // endregion SHOW commands -// parseAdminCheck +// CHECK LICENSE +// CHECK PROVIDER 'provider_name' REGION 'region_name' KEY 'api_key' [URL 'base_url']; +// CHECK PROVIDER 'provider_name' INSTANCE 'instance_name'; func (p *Parser) parseAdminCheck() (*Command, error) { p.nextToken() // consume CHECK switch p.curToken.Type { case TokenLicense: return p.parseAdminCheckLicense() + case TokenProvider: + return p.parseAdminCheckProvider() default: return nil, fmt.Errorf("unknown CHECK target: %s", p.curToken.Value) } } +// CHECK LICENSE; func (p *Parser) parseAdminCheckLicense() (*Command, error) { p.nextToken() // consume LICENSE cmd := NewCommand("admin_check_license") @@ -782,6 +875,90 @@ func (p *Parser) parseAdminCheckLicense() (*Command, error) { return cmd, nil } +// CHECK PROVIDER 'provider_name' REGION 'region_name' KEY 'api_key' [URL 'base_url']; +// CHECK PROVIDER 'provider_name' INSTANCE 'instance_name'; +func (p *Parser) parseAdminCheckProvider() (*Command, error) { + if p.curToken.Type != TokenProvider { + return nil, fmt.Errorf("expected PROVIDER after CHECK") + } + p.nextToken() + + if p.curToken.Type != TokenQuotedString { + return nil, fmt.Errorf("expected provider name after PROVIDER") + } + providerName := p.curToken.Value + p.nextToken() + + if p.curToken.Type == TokenInstance { + return p.parseAdminCheckProviderInstance(providerName) + } + + if p.curToken.Type != TokenRegion { + return nil, fmt.Errorf("expected REGION after provider name") + } + p.nextToken() + + if p.curToken.Type != TokenQuotedString { + return nil, fmt.Errorf("expected region name after REGION") + } + regionName := p.curToken.Value + p.nextToken() + + if p.curToken.Type != TokenKey { + return nil, fmt.Errorf("expected KEY after region name") + } + p.nextToken() + + if p.curToken.Type != TokenQuotedString { + return nil, fmt.Errorf("expected API key after KEY") + } + apiKey := p.curToken.Value + p.nextToken() + + baseURL := "" + if p.curToken.Type == TokenURL { + p.nextToken() + if p.curToken.Type != TokenQuotedString { + return nil, fmt.Errorf("expected base URL after URL") + } + baseURL = p.curToken.Value + p.nextToken() + } + + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + if p.curToken.Type != TokenEOF { + return nil, fmt.Errorf("unexpected token: %s", p.curToken.Value) + } + + cmd := NewCommand("admin_check_provider_with_key") + cmd.Params["provider_name"] = providerName + cmd.Params["region"] = regionName + cmd.Params["api_key"] = apiKey + if baseURL != "" { + cmd.Params["base_url"] = baseURL + } + + return cmd, nil +} + +func (p *Parser) parseAdminCheckProviderInstance(providerName string) (*Command, error) { + if p.curToken.Type != TokenInstance { + return nil, fmt.Errorf("expected PROVIDER after CHECK") + } + p.nextToken() + + instanceName, err := p.parseQuotedString() + if err != nil { + return nil, err + } + cmd := NewCommand("admin_check_provider_instance") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + return cmd, nil +} + // STOP INGESTION TASKS 'task_id1 task_id2'; func (p *Parser) parseAdminStopIngestionTasks() (*Command, error) { p.nextToken() // consume STOP @@ -1067,6 +1244,8 @@ func (p *Parser) parseAdminAlterCommands() (*Command, error) { return p.parseAdminAlterUser() case TokenRole: return p.parseAdminAlterRole() + case TokenProvider: + return p.parseAdminAlterProvider() default: return nil, fmt.Errorf("unknown ALTER target: %s", p.curToken.Value) } @@ -1199,6 +1378,67 @@ func (p *Parser) parseAdminAlterRole() (*Command, error) { return cmd, nil } +func (p *Parser) parseAdminAlterProvider() (*Command, error) { + p.nextToken() // consume PROVIDER + + providerName, err := p.parseQuotedString() + if err != nil { + return nil, err + } + p.nextToken() + + if p.curToken.Type != TokenInstance { + return nil, fmt.Errorf("expected INSTANCE") + } + p.nextToken() + instanceName, err := p.parseQuotedString() + if err != nil { + return nil, err + } + p.nextToken() + + newModelName := "" + newAPIKey := "" +optionsLoop: + for { + switch p.curToken.Type { + case TokenName: + p.nextToken() + newModelName, err = p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model name: %w", err) + } + p.nextToken() + case TokenKey: + p.nextToken() + newAPIKey, err = p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected API key: %w", err) + } + p.nextToken() + default: + break optionsLoop + } + } + + if newModelName == "" && newAPIKey == "" { + return nil, fmt.Errorf("expected NAME or KEY after INSTANCE") + } + + cmd := NewCommand("admin_alter_provider_instance") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + cmd.Params["new_model_name"] = newModelName + cmd.Params["new_api_key"] = newAPIKey + + p.nextToken() + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + // endregion ALTER commands func (p *Parser) parseAdminGrantCommands() (*Command, error) { @@ -1896,6 +2136,112 @@ func (p *Parser) parseAdminDeleteModel(providerName, instanceName string) (*Comm return cmd, nil } +func (p *Parser) parseAdminEnableCommand() (*Command, error) { + p.nextToken() // consume ENABLE + switch p.curToken.Type { + case TokenProvider: + return p.parseAdminEnableModel() + default: + return nil, fmt.Errorf("unknown ENABLE target: %s", p.curToken.Value) + } +} + +func (p *Parser) parseAdminEnableModel() (*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 nil, fmt.Errorf("expected INSTANCE") + } + p.nextToken() + + 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 nil, fmt.Errorf("expected MODEL") + } + p.nextToken() + + modelName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model name: %w", err) + } + p.nextToken() + + cmd := NewCommand("admin_enable_model") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + cmd.Params["model_name"] = modelName + + // Semicolon is optional + if p.curToken.Type == TokenSemicolon { + p.nextToken() + } + return cmd, nil +} + +func (p *Parser) parseAdminDisableCommand() (*Command, error) { + p.nextToken() // consume DISABLE + switch p.curToken.Type { + case TokenProvider: + return p.parseAdminDisableModel() + default: + return nil, fmt.Errorf("unknown DISABLE target: %s", p.curToken.Value) + } +} + +func (p *Parser) parseAdminDisableModel() (*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 nil, fmt.Errorf("expected INSTANCE") + } + p.nextToken() + + 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 nil, fmt.Errorf("expected MODEL") + } + p.nextToken() + + modelName, err := p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model name: %w", err) + } + p.nextToken() + + cmd := NewCommand("admin_disable_model") + cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + cmd.Params["model_name"] = 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 3473225e42..50af6bfa3c 100644 --- a/internal/cli/cli_http.go +++ b/internal/cli/cli_http.go @@ -75,6 +75,8 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.AdminAlterUserPassword(cmd) case "admin_alter_role": return c.AdminAlterRole(cmd) + case "admin_alter_provider_instance": + return c.CommonAlterProviderInstanceCommand(cmd) case "admin_drop_user": return c.AdminDropUserCommand(cmd) case "admin_drop_user_api_key": @@ -115,14 +117,20 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.CommonAvailableProvidersCommand(cmd) case "admin_show_provider": return c.CommonShowProviderCommand(cmd) + case "admin_show_provider_instance": + return c.CommonShowProviderInstanceCommand(cmd) + case "admin_show_provider_instance_balance": + return c.CommonShowProviderInstanceBalanceCommand(cmd) case "admin_show_provider_model": return c.CommonShowProviderModelCommand(cmd) case "admin_list_provider_models": return c.CommonListModelsCommand(cmd) + case "admin_list_provider_instance_models": + return c.CommonListInstanceModels(cmd) + case "admin_list_provider_instances": + return c.CommonListProviderInstances(cmd) case "list_supported_models": return c.ListSupportedModels(cmd) - case "list_instance_models": - return c.ListInstanceModels(cmd) case "admin_show_model": return c.CommonShowModel(cmd) case "admin_list_providers": @@ -151,6 +159,10 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.UserShowMessageQueueCommand(cmd) case "admin_check_license": return c.AdminCheckLicenseCommand(cmd) + case "admin_check_provider_with_key": + return c.CommonCheckProviderWithKey(cmd) + case "admin_check_provider_instance": + return c.CommonCheckProviderConnection(cmd) case "admin_show_fingerprint": return c.AdminShowFingerprintCommand(cmd) case "admin_show_license": @@ -241,6 +253,10 @@ func (c *CLI) ExecuteAdminCommand(cmd *Command) (ResponseIf, error) { return c.AdminDeleteInstancesCommand(cmd) case "admin_delete_model": return c.AdminDeleteModelsCommand(cmd) + case "admin_enable_model": + return c.CommonEnableOrDisableModel(cmd, "enable") + case "admin_disable_model": + return c.CommonEnableOrDisableModel(cmd, "disable") // TODO: Implement other commands case "show_admin_server": return c.ShowAdminServer(cmd) @@ -317,7 +333,7 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) { case "list_supported_models": return c.ListSupportedModels(cmd) case "list_instance_models": - return c.ListInstanceModels(cmd) + return c.CommonListInstanceModels(cmd) case "show_provider_model": return c.CommonShowProviderModelCommand(cmd) case "show_model": @@ -335,21 +351,21 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) { case "create_provider_instance": return c.CreateProviderInstance(cmd) case "list_provider_instances": - return c.ListProviderInstances(cmd) + return c.CommonListProviderInstances(cmd) case "show_provider_instance": - return c.ShowProviderInstance(cmd) + return c.CommonShowProviderInstanceCommand(cmd) case "show_instance_balance": return c.ShowInstanceBalance(cmd) case "alter_provider_instance": - return c.AlterProviderInstance(cmd) + return c.CommonAlterProviderInstanceCommand(cmd) case "drop_provider_instance": return c.DropProviderInstance(cmd) case "drop_instance_model": return c.DropInstanceModel(cmd) case "enable_model": - return c.EnableOrDisableModel(cmd, "enable") + return c.CommonEnableOrDisableModel(cmd, "enable") case "disable_model": - return c.EnableOrDisableModel(cmd, "disable") + return c.CommonEnableOrDisableModel(cmd, "disable") case "add_custom_model": return c.AddCustomModel(cmd) case "chat_to_model": @@ -374,9 +390,9 @@ func (c *CLI) ExecuteUserCommand(cmd *Command) (ResponseIf, error) { case "parse_file_user_command": return c.ParseFileUserCommand(cmd) case "check_provider_connection": - return c.CheckProviderConnection(cmd) + return c.CommonCheckProviderConnection(cmd) case "check_provider_with_key": - return c.CheckProviderWithKey(cmd) + return c.CommonCheckProviderWithKey(cmd) case "use_model": return c.UseModel(cmd) case "use_api_server": diff --git a/internal/cli/common_command.go b/internal/cli/common_command.go index 1fb646b94a..9cffd6cb6c 100644 --- a/internal/cli/common_command.go +++ b/internal/cli/common_command.go @@ -323,6 +323,190 @@ func (c *CLI) CommonShowProviderCommand(cmd *Command) (ResponseIf, error) { return &result, nil } +// CommonShowProviderInstanceCommand shows details of a specific instance +func (c *CLI) CommonShowProviderInstanceCommand(cmd *Command) (ResponseIf, error) { + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance name not provided") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider name not provided") + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/instances/%s", providerName, instanceName) + resp, err = c.AdminServerClient.Request("GET", endPoint, "web", nil, nil) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/instances/%s", providerName, instanceName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", endPoint, "web", nil, nil) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to show instance: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to show instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("failed to show instance: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// CommonShowProviderInstanceBalanceCommand shows balance of a specific instance +func (c *CLI) CommonShowProviderInstanceBalanceCommand(cmd *Command) (ResponseIf, error) { + + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance name not provided") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider name not provided") + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/instances/%s/balance", providerName, instanceName) + resp, err = c.AdminServerClient.Request("GET", endPoint, "web", nil, nil) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/instances/%s/balance", providerName, instanceName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", endPoint, "web", nil, nil) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to show instance balance: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to show instance balance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("failed to show instance balance: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +// CommonListProviderInstances lists all instances of a provider +// LIST INSTANCES FROM PROVIDER +func (c *CLI) CommonListProviderInstances(cmd *Command) (ResponseIf, error) { + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider_name not provided") + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/instances", providerName) + resp, err = c.AdminServerClient.Request("GET", endPoint, "web", nil, nil) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/instances", providerName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", endPoint, "web", nil, nil) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to list instances: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to list instances: 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 instances failed: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + + result.Duration = resp.Duration + return &result, nil +} + +func (c *CLI) CommonListInstanceModels(cmd *Command) (ResponseIf, error) { + + 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") + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/instances/%s/models", providerName, instanceName) + resp, err = c.AdminServerClient.Request("GET", endPoint, "web", nil, nil) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/instances/%s/models", providerName, instanceName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", endPoint, "web", nil, nil) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to list instance models: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to list instance models: 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("failed to list instance models: invalid JSON (%w)", err) + } + + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil +} + func (c *CLI) CommonListModelsCommand(cmd *Command) (ResponseIf, error) { providerName, ok := cmd.Params["provider_name"].(string) @@ -452,6 +636,284 @@ func (c *CLI) CommonShowProviderModelCommand(cmd *Command) (ResponseIf, error) { return &result, nil } +func (c *CLI) CommonCheckProviderWithKey(cmd *Command) (ResponseIf, error) { + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok || providerName == "" { + return nil, fmt.Errorf("provider name not provided") + } + region, ok := cmd.Params["region"].(string) + if !ok || region == "" { + return nil, fmt.Errorf("region not provided") + } + apiKey, ok := cmd.Params["api_key"].(string) + if !ok { + return nil, fmt.Errorf("api_key not provided") + } + baseURL, _ := cmd.Params["base_url"].(string) + + var apiKeyValue interface{} + if apiKey != "" { + apiKeyValue = apiKey + } else { + apiKeyValue = nil + } + + payload := map[string]interface{}{ + "region": region, + "api_key": apiKeyValue, + } + if baseURL != "" { + payload["base_url"] = baseURL + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/connection", providerName) + resp, err = c.AdminServerClient.Request("POST", endPoint, "web", nil, payload) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/connection", providerName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("POST", endPoint, "web", nil, payload) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to check provider connection with key: %w", err) + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to check provider connection: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + switch c.Config.CLIMode { + case AdminMode: + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + case APIMode: + var result SimpleResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + default: + return nil, fmt.Errorf("invalid server type") + } +} + +func (c *CLI) CommonCheckProviderConnection(cmd *Command) (ResponseIf, error) { + + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance name not provided") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider name not provided") + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/instances/%s/connection", providerName, instanceName) + resp, err = c.AdminServerClient.Request("POST", endPoint, "web", nil, nil) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/instances/%s/connection", providerName, instanceName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("POST", endPoint, "web", nil, nil) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to check provider connection: %w", err) + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to check provider connection: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + switch c.Config.CLIMode { + case AdminMode: + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + case APIMode: + var result SimpleResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + default: + return nil, fmt.Errorf("invalid server type") + } +} + +// AlterProviderInstanceCommand alters a provider instance +func (c *CLI) CommonAlterProviderInstanceCommand(cmd *Command) (ResponseIf, error) { + + 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{}{} + + newName, ok := cmd.Params["new_model_name"].(string) + if ok { + payload["model_name"] = newName + } + + newAPIKey, ok := cmd.Params["new_api_key"].(string) + if ok { + payload["api_key"] = newAPIKey + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/instances/%s", providerName, instanceName) + resp, err = c.AdminServerClient.Request("PUT", endPoint, "web", nil, payload) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/instances/%s", providerName, instanceName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("PUT", endPoint, "web", nil, payload) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to alter instance: %w", err) + } + + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to alter instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + switch c.Config.CLIMode { + case AdminMode: + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + case APIMode: + var result SimpleResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + default: + return nil, fmt.Errorf("invalid server type") + } +} + +func (c *CLI) CommonEnableOrDisableModel(cmd *Command, status string) (ResponseIf, error) { + + modelName, ok := cmd.Params["model_name"].(string) + if !ok { + return nil, fmt.Errorf("model name not provided") + } + + instanceName, ok := cmd.Params["instance_name"].(string) + if !ok { + return nil, fmt.Errorf("instance name not provided") + } + + providerName, ok := cmd.Params["provider_name"].(string) + if !ok { + return nil, fmt.Errorf("provider name not provided") + } + + payload := map[string]interface{}{ + "status": status, + } + + var resp *Response + var err error + var endPoint string + switch c.Config.CLIMode { + case AdminMode: + endPoint = fmt.Sprintf("/admin/providers/%s/instances/%s/models/%s", providerName, instanceName, modelName) + resp, err = c.AdminServerClient.Request("PATCH", endPoint, "web", nil, payload) + case APIMode: + endPoint = fmt.Sprintf("/providers/%s/instances/%s/models/%s", providerName, instanceName, modelName) + resp, err = c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("PATCH", endPoint, "web", nil, payload) + default: + return nil, fmt.Errorf("invalid server type") + } + + if err != nil { + return nil, fmt.Errorf("failed to enable/disable model: %w", err) + } + if resp.StatusCode != 200 { + return nil, fmt.Errorf("failed to enable/disable model: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) + } + + switch c.Config.CLIMode { + case AdminMode: + var result CommonDataResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + case APIMode: + var result SimpleResponse + if err = json.Unmarshal(resp.Body, &result); err != nil { + return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) + } + if result.Code != 0 { + return nil, fmt.Errorf("%s", result.Message) + } + result.Duration = resp.Duration + return &result, nil + default: + return nil, fmt.Errorf("invalid server type") + } +} + func (c *CLI) SetDefaultModel(cmd *Command) (ResponseIf, error) { modelType, ok := cmd.Params["model_type"].(string) diff --git a/internal/cli/parser.go b/internal/cli/parser.go index c3db5363a5..41596e0ffa 100644 --- a/internal/cli/parser.go +++ b/internal/cli/parser.go @@ -124,6 +124,10 @@ func (p *Parser) parseAdminCommand() (*Command, error) { return p.parseAdminAddCommand() case TokenDelete: return p.parseAdminDeleteCommands() + case TokenEnable: + return p.parseAdminEnableCommand() + case TokenDisable: + return p.parseAdminDisableCommand() case TokenSave: return p.parseAdminSaveCommand() case TokenUse: diff --git a/internal/cli/user_command.go b/internal/cli/user_command.go index edcadd95ed..9965d32ead 100644 --- a/internal/cli/user_command.go +++ b/internal/cli/user_command.go @@ -1275,83 +1275,6 @@ func (c *CLI) CreateProviderInstance(cmd *Command) (ResponseIf, error) { return &result, nil } -// ListProviderInstances lists all instances of a provider -// LIST INSTANCES FROM PROVIDER -func (c *CLI) ListProviderInstances(cmd *Command) (ResponseIf, error) { - if c.Config.CLIMode != APIMode { - return nil, fmt.Errorf("this command is only allowed in USER mode") - } - - providerName, ok := cmd.Params["provider_name"].(string) - if !ok { - return nil, fmt.Errorf("provider name not provided") - } - - url := fmt.Sprintf("/providers/%s/instances", providerName) - - resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", url, "web", nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to list instances: %w", err) - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to list instances: 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 instances failed: invalid JSON (%w)", err) - } - - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - - result.Duration = resp.Duration - return &result, nil -} - -// ShowProviderInstance shows details of a specific instance -// SHOW INSTANCE FROM PROVIDER -func (c *CLI) ShowProviderInstance(cmd *Command) (ResponseIf, error) { - if c.Config.CLIMode != APIMode { - return nil, fmt.Errorf("this command is only allowed in USER mode") - } - - instanceName, ok := cmd.Params["instance_name"].(string) - if !ok { - return nil, fmt.Errorf("instance name not provided") - } - - providerName, ok := cmd.Params["provider_name"].(string) - if !ok { - return nil, fmt.Errorf("provider name not provided") - } - - url := fmt.Sprintf("/providers/%s/instances/%s", providerName, instanceName) - - resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", url, "web", nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to show instance: %w", err) - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to show instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - - var result CommonDataResponse - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("show instance failed: invalid JSON (%w)", err) - } - - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - - result.Duration = resp.Duration - return &result, nil -} - // ShowInstanceBalance shows balance of a specific instance // SHOW BALANCE FROM PROVIDER func (c *CLI) ShowInstanceBalance(cmd *Command) (ResponseIf, error) { @@ -1393,56 +1316,6 @@ func (c *CLI) ShowInstanceBalance(cmd *Command) (ResponseIf, error) { return &result, nil } -// AlterProviderInstance renames a provider instance -// ALTER INSTANCE NAME FROM PROVIDER -func (c *CLI) AlterProviderInstance(cmd *Command) (ResponseIf, error) { - if c.Config.CLIMode != APIMode { - return nil, fmt.Errorf("this command is only allowed in USER mode") - } - - instanceName, ok := cmd.Params["instance_name"].(string) - if !ok { - return nil, fmt.Errorf("instance name not provided") - } - - newName, ok := cmd.Params["new_name"].(string) - if !ok { - return nil, fmt.Errorf("new name not provided") - } - - providerName, ok := cmd.Params["provider_name"].(string) - if !ok { - return nil, fmt.Errorf("provider name not provided") - } - - url := fmt.Sprintf("/providers/%s/instances/%s", providerName, instanceName) - - payload := map[string]interface{}{ - "llm_name": newName, - } - - resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("PUT", url, "web", nil, payload) - if err != nil { - return nil, fmt.Errorf("failed to alter instance: %w", err) - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to alter instance: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - - var result SimpleResponse - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("alter instance failed: invalid JSON (%w)", err) - } - - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - - result.Duration = resp.Duration - return &result, nil -} - // DropProviderInstance deletes a provider instance // DROP INSTANCE FROM PROVIDER func (c *CLI) DropProviderInstance(cmd *Command) (ResponseIf, error) { @@ -1538,87 +1411,6 @@ func (c *CLI) DropInstanceModel(cmd *Command) (ResponseIf, error) { return &result, nil } -func (c *CLI) ListInstanceModels(cmd *Command) (ResponseIf, error) { - if c.Config.CLIMode != APIMode { - return nil, fmt.Errorf("this command is only allowed in USER mode") - } - 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") - } - - var endPoint string - endPoint = fmt.Sprintf("/providers/%s/instances/%s/models", providerName, instanceName) - - resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", endPoint, "web", nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to list instance models: %w", err) - } - - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to list instance models: 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("failed to list instance models: invalid JSON (%w)", err) - } - - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - result.Duration = resp.Duration - return &result, nil -} - -func (c *CLI) EnableOrDisableModel(cmd *Command, status string) (ResponseIf, error) { - if c.Config.CLIMode != APIMode { - return nil, fmt.Errorf("this command is only allowed in USER mode") - } - - modelName, ok := cmd.Params["model_name"].(string) - if !ok { - return nil, fmt.Errorf("model name not provided") - } - - instanceName, ok := cmd.Params["instance_name"].(string) - if !ok { - return nil, fmt.Errorf("instance name not provided") - } - - providerName, ok := cmd.Params["provider_name"].(string) - if !ok { - return nil, fmt.Errorf("provider name not provided") - } - - url := fmt.Sprintf("/providers/%s/instances/%s/models/%s", providerName, instanceName, modelName) - - payload := map[string]interface{}{ - "status": status, - } - - resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("PATCH", url, "web", nil, payload) - if err != nil { - return nil, fmt.Errorf("failed to enable/disable model: %w", err) - } - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to enable/disable model: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - var result SimpleResponse - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("enable/disable model failed: invalid JSON (%w)", err) - } - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - result.Duration = resp.Duration - return &result, nil -} - func isValidURL(str string) bool { u, err := netUrl.Parse(str) if err != nil { @@ -2668,103 +2460,6 @@ func (c *CLI) ShowTaskUserCommand(cmd *Command) (ResponseIf, error) { return &result, nil } -func (c *CLI) CheckProviderConnection(cmd *Command) (ResponseIf, error) { - if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIToken == nil && c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].LoginToken == nil { - return nil, fmt.Errorf("API token not set. Please login first") - } - - if c.Config.CLIMode != APIMode { - return nil, fmt.Errorf("this command is only allowed in USER mode") - } - - instanceName, ok := cmd.Params["instance_name"].(string) - if !ok { - return nil, fmt.Errorf("instance name not provided") - } - - providerName, ok := cmd.Params["provider_name"].(string) - if !ok { - return nil, fmt.Errorf("provider name not provided") - } - - url := fmt.Sprintf("/providers/%s/instances/%s/connection", providerName, instanceName) - - resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", url, "web", nil, nil) - if err != nil { - return nil, fmt.Errorf("failed to check provider connection: %w", err) - } - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to check provider connection: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - var result SimpleResponse - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) - } - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - result.Duration = resp.Duration - return &result, nil -} - -func (c *CLI) CheckProviderWithKey(cmd *Command) (ResponseIf, error) { - if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIToken == nil && c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].LoginToken == nil { - return nil, fmt.Errorf("API token not set. Please login first") - } - if c.Config.CLIMode != APIMode { - return nil, fmt.Errorf("this command is only allowed in USER mode") - } - - providerName, ok := cmd.Params["provider_name"].(string) - if !ok || providerName == "" { - return nil, fmt.Errorf("provider name not provided") - } - region, ok := cmd.Params["region"].(string) - if !ok || region == "" { - return nil, fmt.Errorf("region not provided") - } - apiKey, ok := cmd.Params["api_key"].(string) - if !ok { - return nil, fmt.Errorf("api_key not provided") - } - baseURL, _ := cmd.Params["base_url"].(string) - - var apiKeyValue interface{} - if apiKey != "" { - apiKeyValue = apiKey - } else { - apiKeyValue = nil - } - - url := fmt.Sprintf("/providers/%s/connection", providerName) - - payload := map[string]interface{}{ - "region": region, - "api_key": apiKeyValue, - } - if baseURL != "" { - payload["base_url"] = baseURL - } - - resp, err := c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].Request("GET", url, "api", nil, payload) - if err != nil { - return nil, fmt.Errorf("failed to check provider connection with key: %w", err) - } - if resp.StatusCode != 200 { - return nil, fmt.Errorf("failed to check provider connection: HTTP %d, body: %s", resp.StatusCode, string(resp.Body)) - } - - var result SimpleResponse - if err = json.Unmarshal(resp.Body, &result); err != nil { - return nil, fmt.Errorf("check provider connection failed: invalid JSON (%w)", err) - } - if result.Code != 0 { - return nil, fmt.Errorf("%s", result.Message) - } - result.Duration = resp.Duration - return &result, nil -} - // UseModel sets the current model for chat func (c *CLI) UseModel(cmd *Command) (ResponseIf, error) { if c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].APIToken == nil && c.APIServerClientMap[c.Config.APIClientConfig.CurrentAPIServer].LoginToken == nil { diff --git a/internal/cli/user_parser.go b/internal/cli/user_parser.go index 707da91084..e2c2cadc55 100644 --- a/internal/cli/user_parser.go +++ b/internal/cli/user_parser.go @@ -1710,19 +1710,36 @@ func (p *Parser) parseAlterInstance() (*Command, error) { if err != nil { return nil, fmt.Errorf("expected instance name: %w", err) } - - p.nextToken() - if p.curToken.Type != TokenName { - return nil, fmt.Errorf("expected NAME") - } p.nextToken() - newName, err := p.parseQuotedString() - if err != nil { - return nil, fmt.Errorf("expected new instance name: %w", err) + newModelName := "" + newAPIKey := "" +optionsLoop: + for { + switch p.curToken.Type { + case TokenName: + p.nextToken() + newModelName, err = p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected model name: %w", err) + } + p.nextToken() + case TokenKey: + p.nextToken() + newAPIKey, err = p.parseQuotedString() + if err != nil { + return nil, fmt.Errorf("expected API key: %w", err) + } + p.nextToken() + default: + break optionsLoop + } + } + + if newModelName == "" && newAPIKey == "" { + return nil, fmt.Errorf("expected NAME or KEY after INSTANCE") } - p.nextToken() if p.curToken.Type != TokenFrom { return nil, fmt.Errorf("expected FROM") } @@ -1739,9 +1756,10 @@ func (p *Parser) parseAlterInstance() (*Command, error) { } cmd := NewCommand("alter_provider_instance") - cmd.Params["instance_name"] = instanceName - cmd.Params["new_name"] = newName cmd.Params["provider_name"] = providerName + cmd.Params["instance_name"] = instanceName + cmd.Params["new_model_name"] = newModelName + cmd.Params["new_api_key"] = newAPIKey p.nextToken() // Semicolon is optional @@ -2543,8 +2561,9 @@ func (p *Parser) parseListModelsOfProvider() (*Command, error) { // If so, format is: LIST MODELS FROM // If not, format is: LIST MODELS FROM if p.curToken.Type == TokenQuotedString { + var instanceName string // Two arguments: instance_name and provider_name - instanceName, err := p.parseQuotedString() + instanceName, err = p.parseQuotedString() if err != nil { return nil, err } diff --git a/internal/handler/providers.go b/internal/handler/providers.go index 7b11a360f0..d50fdc0fe6 100644 --- a/internal/handler/providers.go +++ b/internal/handler/providers.go @@ -558,7 +558,8 @@ func (h *ProviderHandler) ShowTask(c *gin.Context) { } type AlterProviderInstanceRequest struct { - LLMName string `json:"llm_name" binding:"required"` + ModelName string `json:"model_name"` + APIKey string `json:"api_key"` } func (h *ProviderHandler) AlterProviderInstance(c *gin.Context) { @@ -598,8 +599,17 @@ func (h *ProviderHandler) AlterProviderInstance(c *gin.Context) { return } + code, err := h.modelProviderService.AlterProviderInstance(userID, providerName, instanceName, req.ModelName, req.APIKey) + if err != nil { + c.JSON(http.StatusOK, gin.H{ + "code": code, + "message": err.Error(), + }) + return + } + c.JSON(http.StatusOK, gin.H{ - "code": common.CodeNotFound, + "code": 0, "message": "success", }) } diff --git a/internal/router/router.go b/internal/router/router.go index 48e1b6e286..8a4d3cc942 100644 --- a/internal/router/router.go +++ b/internal/router/router.go @@ -433,13 +433,6 @@ func (r *Router) Setup(engine *gin.Engine) { 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.CheckInstanceConnection) - // Python's /providers//connection is POST — see - // api/apps/restful_apis/provider_api.py:359. The web front-end - // posts {api_key, base_url, region, model_info} there - // (web/src/services/llm-service.ts:45-48 method: 'post'). The - // Go handler body is already POST-shaped (ShouldBindJSON - // against CheckConnectionRequest), so the only thing missing - // was the routing method. provider.POST("/: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) diff --git a/internal/service/model_service.go b/internal/service/model_service.go index 0ae2b5f9e6..72bbfb4c54 100644 --- a/internal/service/model_service.go +++ b/internal/service/model_service.go @@ -892,7 +892,7 @@ func (m *ModelProviderService) ListTenantAddedModels(userID, modelTypeFilter str return added, common.CodeSuccess, nil } -func (m *ModelProviderService) AlterProviderInstance(providerName, instanceName, newInstanceName, apiKey, userID string) (common.ErrorCode, error) { +func (m *ModelProviderService) AlterProviderInstance(userID, providerName, instanceName, newInstanceName, apiKey string) (common.ErrorCode, error) { return common.CodeSuccess, nil }