From a7c875ef175756e23395918f473926f32262bb9c Mon Sep 17 00:00:00 2001 From: "bao.xu" Date: Wed, 8 Jul 2026 19:36:47 +0800 Subject: [PATCH] feat(umodelverse): add UModelVerse CLI commands --- cmd/products.gen.go | 2 + .../umodelverse/internal/umodelverse/api.go | 152 ++++++++++++++ .../internal/umodelverse/apikey.go | 21 ++ .../internal/umodelverse/apikey_create.go | 75 +++++++ .../internal/umodelverse/apikey_delete.go | 51 +++++ .../internal/umodelverse/apikey_list.go | 41 ++++ .../internal/umodelverse/apikey_update.go | 80 ++++++++ .../umodelverse/internal/umodelverse/cmd.go | 23 +++ .../internal/umodelverse/filter_options.go | 33 +++ .../umodelverse/internal/umodelverse/flags.go | 119 +++++++++++ .../umodelverse/internal/umodelverse/log.go | 20 ++ .../internal/umodelverse/log_describe.go | 38 ++++ .../internal/umodelverse/log_export.go | 53 +++++ .../internal/umodelverse/log_list.go | 67 ++++++ .../umodelverse/internal/umodelverse/model.go | 18 ++ .../internal/umodelverse/model_list.go | 51 +++++ .../umodelverse/internal/umodelverse/order.go | 21 ++ .../internal/umodelverse/order_amount.go | 33 +++ .../internal/umodelverse/order_paid.go | 20 ++ .../internal/umodelverse/order_paid_export.go | 36 ++++ .../internal/umodelverse/order_paid_list.go | 34 ++++ .../umodelverse/order_paid_summary.go | 34 ++++ .../internal/umodelverse/order_summary.go | 18 ++ .../umodelverse/order_summary_export.go | 37 ++++ .../internal/umodelverse/order_unpaid.go | 20 ++ .../umodelverse/order_unpaid_export.go | 36 ++++ .../internal/umodelverse/order_unpaid_list.go | 34 ++++ .../umodelverse/order_unpaid_summary.go | 34 ++++ .../umodelverse/internal/umodelverse/rows.go | 6 + products/umodelverse/product.go | 21 ++ products/umodelverse/product.yaml | 7 + products/umodelverse/testdata/cmdtree.golden | 192 ++++++++++++++++++ .../umodelverse/testdata/completion.golden | 9 + 33 files changed, 1436 insertions(+) create mode 100644 products/umodelverse/internal/umodelverse/api.go create mode 100644 products/umodelverse/internal/umodelverse/apikey.go create mode 100644 products/umodelverse/internal/umodelverse/apikey_create.go create mode 100644 products/umodelverse/internal/umodelverse/apikey_delete.go create mode 100644 products/umodelverse/internal/umodelverse/apikey_list.go create mode 100644 products/umodelverse/internal/umodelverse/apikey_update.go create mode 100644 products/umodelverse/internal/umodelverse/cmd.go create mode 100644 products/umodelverse/internal/umodelverse/filter_options.go create mode 100644 products/umodelverse/internal/umodelverse/flags.go create mode 100644 products/umodelverse/internal/umodelverse/log.go create mode 100644 products/umodelverse/internal/umodelverse/log_describe.go create mode 100644 products/umodelverse/internal/umodelverse/log_export.go create mode 100644 products/umodelverse/internal/umodelverse/log_list.go create mode 100644 products/umodelverse/internal/umodelverse/model.go create mode 100644 products/umodelverse/internal/umodelverse/model_list.go create mode 100644 products/umodelverse/internal/umodelverse/order.go create mode 100644 products/umodelverse/internal/umodelverse/order_amount.go create mode 100644 products/umodelverse/internal/umodelverse/order_paid.go create mode 100644 products/umodelverse/internal/umodelverse/order_paid_export.go create mode 100644 products/umodelverse/internal/umodelverse/order_paid_list.go create mode 100644 products/umodelverse/internal/umodelverse/order_paid_summary.go create mode 100644 products/umodelverse/internal/umodelverse/order_summary.go create mode 100644 products/umodelverse/internal/umodelverse/order_summary_export.go create mode 100644 products/umodelverse/internal/umodelverse/order_unpaid.go create mode 100644 products/umodelverse/internal/umodelverse/order_unpaid_export.go create mode 100644 products/umodelverse/internal/umodelverse/order_unpaid_list.go create mode 100644 products/umodelverse/internal/umodelverse/order_unpaid_summary.go create mode 100644 products/umodelverse/internal/umodelverse/rows.go create mode 100644 products/umodelverse/product.go create mode 100644 products/umodelverse/product.yaml create mode 100644 products/umodelverse/testdata/cmdtree.golden create mode 100644 products/umodelverse/testdata/completion.golden diff --git a/cmd/products.gen.go b/cmd/products.gen.go index 40a32aa6a5..811c4c1fc0 100644 --- a/cmd/products.gen.go +++ b/cmd/products.gen.go @@ -6,6 +6,7 @@ import ( "github.com/ucloud/ucloud-cli/products/udb" "github.com/ucloud/ucloud-cli/products/udisk" "github.com/ucloud/ucloud-cli/products/uhost" + "github.com/ucloud/ucloud-cli/products/umodelverse" "github.com/ucloud/ucloud-cli/products/unet" "github.com/ucloud/ucloud-cli/products/uphost" ) @@ -16,6 +17,7 @@ func registeredProducts() []cli.Product { udb.New(), udisk.New(), uhost.New(), + umodelverse.New(), unet.New(), uphost.New(), } diff --git a/products/umodelverse/internal/umodelverse/api.go b/products/umodelverse/internal/umodelverse/api.go new file mode 100644 index 0000000000..cb249c45fc --- /dev/null +++ b/products/umodelverse/internal/umodelverse/api.go @@ -0,0 +1,152 @@ +package umodelverse + +import ( + "encoding/json" + "fmt" + "sort" + + uai "github.com/ucloud/ucloud-sdk-go/services/uai_modelverse" + "github.com/ucloud/ucloud-sdk-go/ucloud/request" + "github.com/ucloud/ucloud-sdk-go/ucloud/response" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +const productName = "umodelverse" + +type apiResponse struct { + response.BaseGenericResponse +} + +func newClient(ctx *cli.Context) *uai.UAI_ModelverseClient { + return cli.NewServiceClient(ctx, uai.NewClient) +} + +func newRequest(client *uai.UAI_ModelverseClient, req request.Common, retryable bool) { + client.Client.SetupRequest(req) + req.SetRetryable(retryable) +} + +func invokeUMAction(client *uai.UAI_ModelverseClient, action string, req request.Common) (*apiResponse, error) { + var resp apiResponse + err := client.Client.InvokeAction(action, req, &resp) + return &resp, err +} + +func printResponse(ctx *cli.Context, resp *apiResponse) { + payload := resp.GetPayload() + if ctx.Format() != cli.OutputTable { + ctx.PrintList(payload) + return + } + + keys := make([]string, 0, len(payload)) + for key := range payload { + keys = append(keys, key) + } + sort.Strings(keys) + + rows := make([]fieldRow, 0, len(keys)) + for _, key := range keys { + rows = append(rows, fieldRow{Field: key, Value: renderValue(payload[key])}) + } + ctx.PrintList(rows) +} + +func renderValue(v interface{}) string { + switch val := v.(type) { + case nil: + return "" + case string: + return val + case float64, bool: + return fmt.Sprintf("%v", val) + default: + b, err := json.Marshal(val) + if err != nil { + return fmt.Sprintf("%v", val) + } + return string(b) + } +} + +type apiKeyRequest struct { + request.CommonBase + + KeyId *string `required:"false"` + Name *string `required:"false"` + Status *int `required:"false"` + ModelverseDisabled *int `required:"false"` + SandBoxDisabled *int `required:"false"` + DailyLimitAmount *string `required:"false"` + MonthlyLimitAmount *string `required:"false"` + ExpireTime *int64 `required:"false"` + GrantAllModels *bool `required:"false"` + GrantedModels *string `required:"false"` + IPWhitelist *string `required:"false"` + DailyQuotaAlertThreshold *int `required:"false"` + MonthlyQuotaAlertThreshold *int `required:"false"` + QuotaAlertChannels *string `required:"false"` + QuotaAlertEmail *string `required:"false"` + QuotaAlertPhone *string `required:"false"` + QuotaAlertEmailVerificationToken *string `required:"false"` + QuotaAlertPhoneVerificationToken *string `required:"false"` + Offset *int `required:"false"` + Limit *int `required:"false"` +} + +type squareModelRequest struct { + request.CommonBase + + ModelType *string `required:"false"` + KeyWord *string `required:"false"` + Offset *int `required:"false"` + Limit *int `required:"false"` + OrderBy *string `required:"false"` + Order *string `required:"false"` + MaxModelLen *string `required:"false"` + Language *string `required:"false"` +} + +type requestLogRequest struct { + request.CommonBase + + StartTime *int64 `required:"false"` + EndTime *int64 `required:"false"` + Email *string `required:"false"` + RequestId *string `required:"false"` + ModelNames *string `required:"false"` + ApiKeyIds *string `required:"false"` + Offset *int `required:"false"` + Limit *int `required:"false"` +} + +type logDetailRequest struct { + request.CommonBase + + RequestId *string `required:"true"` +} + +type orderRequest struct { + request.CommonBase + + StartTime *int64 `required:"false"` + EndTime *int64 `required:"false"` + Page *int `required:"false"` + PageSize *int `required:"false"` + ResourceIds []string `required:"false"` + ModelIds []string `required:"false"` + PricingUnits []int `required:"false"` + PricingSkus []string `required:"false"` + OrderTypes []int `required:"false"` + ChargeTypes []int `required:"false"` + OrganizationIds []int `required:"false"` + Regions []string `required:"false"` + ProductCodes []string `required:"false"` +} + +type filterOptionsRequest struct { + request.CommonBase + + ProductCode *string `required:"false"` +} diff --git a/products/umodelverse/internal/umodelverse/apikey.go b/products/umodelverse/internal/umodelverse/apikey.go new file mode 100644 index 0000000000..e4fcc0b4d9 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/apikey.go @@ -0,0 +1,21 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newAPIKey(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "apikey", + Short: "Manage uModelVerse API keys", + Long: "Manage uModelVerse API keys.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newAPIKeyCreate(ctx)) + cmd.AddCommand(newAPIKeyDelete(ctx)) + cmd.AddCommand(newAPIKeyUpdate(ctx)) + cmd.AddCommand(newAPIKeyList(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/apikey_create.go b/products/umodelverse/internal/umodelverse/apikey_create.go new file mode 100644 index 0000000000..4ca83beb5b --- /dev/null +++ b/products/umodelverse/internal/umodelverse/apikey_create.go @@ -0,0 +1,75 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + sdk "github.com/ucloud/ucloud-sdk-go/ucloud" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newAPIKeyCreate(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &apiKeyRequest{} + newRequest(client, req, false) + var grantedModels []string + var quotaAlertChannels []string + + cmd := &cobra.Command{ + Use: "create", + Short: "Create a uModelVerse API key", + Long: "Create a uModelVerse API key.", + Run: func(c *cobra.Command, args []string) { + flags := c.Flags() + clearIntIfUnchanged(flags, "modelverse-disabled", &req.ModelverseDisabled) + clearIntIfUnchanged(flags, "sandbox-disabled", &req.SandBoxDisabled) + clearStringIfUnchanged(flags, "daily-limit-amount", &req.DailyLimitAmount) + clearStringIfUnchanged(flags, "monthly-limit-amount", &req.MonthlyLimitAmount) + clearInt64IfUnchanged(flags, "expire-time", &req.ExpireTime) + clearBoolIfUnchanged(flags, "grant-all-models", &req.GrantAllModels) + req.GrantedModels = stringSliceJSONRef(grantedModels) + clearStringIfUnchanged(flags, "ip-whitelist", &req.IPWhitelist) + clearIntIfUnchanged(flags, "daily-quota-alert-threshold", &req.DailyQuotaAlertThreshold) + clearIntIfUnchanged(flags, "monthly-quota-alert-threshold", &req.MonthlyQuotaAlertThreshold) + req.QuotaAlertChannels = stringSliceJSONRef(quotaAlertChannels) + clearStringIfUnchanged(flags, "quota-alert-email", &req.QuotaAlertEmail) + clearStringIfUnchanged(flags, "quota-alert-phone", &req.QuotaAlertPhone) + clearStringIfUnchanged(flags, "quota-alert-email-verification-token", &req.QuotaAlertEmailVerificationToken) + clearStringIfUnchanged(flags, "quota-alert-phone-verification-token", &req.QuotaAlertPhoneVerificationToken) + if req.IPWhitelist != nil { + req.IPWhitelist = sdk.String(cleanMultilineFlag(*req.IPWhitelist)) + } + resp, err := invokeUMAction(client, "CreateUMInferAPIKey", req) + if err != nil { + ctx.HandleError(err) + return + } + fmt.Fprintln(ctx.ProgressWriter(), "umodelverse apikey created") + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + req.Name = flags.String("name", "", "Required. API key name.") + req.ModelverseDisabled = flags.Int("modelverse-disabled", 0, "Optional. Whether ModelVerse is disabled: 0 enabled, 1 disabled.") + req.SandBoxDisabled = flags.Int("sandbox-disabled", 0, "Optional. Whether sandbox is disabled: 0 enabled, 1 disabled.") + req.DailyLimitAmount = flags.String("daily-limit-amount", "", "Optional. Daily limit amount.") + req.MonthlyLimitAmount = flags.String("monthly-limit-amount", "", "Optional. Monthly limit amount.") + req.ExpireTime = flags.Int64("expire-time", 0, "Optional. API key expire time, Unix timestamp. Use -1 for never expire.") + req.GrantAllModels = flags.Bool("grant-all-models", true, "Optional. Grant access to all models.") + flags.StringSliceVar(&grantedModels, "granted-models", nil, "Optional. Granted model IDs when --grant-all-models=false. Can be repeated, comma-separated, or a JSON array string.") + req.IPWhitelist = flags.String("ip-whitelist", "", "Optional. IP whitelist, newline-separated; literal \\n is also accepted.") + req.DailyQuotaAlertThreshold = flags.Int("daily-quota-alert-threshold", 0, "Optional. Daily quota alert threshold.") + req.MonthlyQuotaAlertThreshold = flags.Int("monthly-quota-alert-threshold", 0, "Optional. Monthly quota alert threshold.") + flags.StringSliceVar("aAlertChannels, "quota-alert-channel", nil, "Optional. Quota alert channel, e.g. email or sms. Can be repeated or comma-separated.") + req.QuotaAlertEmail = flags.String("quota-alert-email", "", "Optional. Email address for quota alerts.") + req.QuotaAlertPhone = flags.String("quota-alert-phone", "", "Optional. Phone number for quota alerts.") + req.QuotaAlertEmailVerificationToken = flags.String("quota-alert-email-verification-token", "", "Optional. Email verification token for quota alerts.") + req.QuotaAlertPhoneVerificationToken = flags.String("quota-alert-phone-verification-token", "", "Optional. Phone verification token for quota alerts.") + bindProject(cmd, req, ctx.DefaultProjectID()) + + cmd.MarkFlagRequired("name") + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/apikey_delete.go b/products/umodelverse/internal/umodelverse/apikey_delete.go new file mode 100644 index 0000000000..51ecbd8c7c --- /dev/null +++ b/products/umodelverse/internal/umodelverse/apikey_delete.go @@ -0,0 +1,51 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + sdk "github.com/ucloud/ucloud-sdk-go/ucloud" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newAPIKeyDelete(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &apiKeyRequest{} + newRequest(client, req, true) + + var yes bool + + cmd := &cobra.Command{ + Use: "delete", + Short: "Delete a uModelVerse API key", + Long: "Delete a uModelVerse API key by key ID.", + Run: func(c *cobra.Command, args []string) { + id := ctx.PickResourceID(*req.KeyId) + ok, err := ctx.Confirm(yes, fmt.Sprintf("Are you sure you want to delete API key %s?", id)) + if err != nil { + ctx.HandleError(err) + return + } + if !ok { + return + } + req.KeyId = sdk.String(id) + if _, err := invokeUMAction(client, "DeleteUMInferAPIKey", req); err != nil { + ctx.HandleError(err) + return + } + fmt.Fprintf(ctx.ProgressWriter(), "umodelverse apikey[%s] deleted\n", id) + ctx.EmitResult(cli.OpResultRow{ResourceID: id, Action: "delete", Status: "Deleted"}) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + req.KeyId = flags.String("key-id", "", "Required. API key ID to delete.") + flags.BoolVarP(&yes, "yes", "y", false, "Optional. Skip the confirmation prompt.") + bindProject(cmd, req, ctx.DefaultProjectID()) + + cmd.MarkFlagRequired("key-id") + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/apikey_list.go b/products/umodelverse/internal/umodelverse/apikey_list.go new file mode 100644 index 0000000000..502ae02118 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/apikey_list.go @@ -0,0 +1,41 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newAPIKeyList(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &apiKeyRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "list", + Short: "List uModelVerse API keys", + Long: "List uModelVerse API keys.", + Run: func(c *cobra.Command, args []string) { + flags := c.Flags() + clearStringIfUnchanged(flags, "key-id", &req.KeyId) + clearIntIfUnchanged(flags, "modelverse-disabled", &req.ModelverseDisabled) + clearIntIfUnchanged(flags, "sandbox-disabled", &req.SandBoxDisabled) + resp, err := invokeUMAction(client, "ListUMInferAPIKey", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + req.KeyId = flags.String("key-id", "", "Optional. API key ID filter.") + req.Offset = flags.Int("offset", 0, "Optional. The index of API key which start to list.") + req.Limit = flags.Int("limit", 20, "Optional. The maximum number of API keys per page.") + req.ModelverseDisabled = flags.Int("modelverse-disabled", 0, "Optional. Whether ModelVerse is disabled: 0 enabled, 1 disabled.") + req.SandBoxDisabled = flags.Int("sandbox-disabled", 0, "Optional. Whether sandbox is disabled: 0 enabled, 1 disabled.") + bindProject(cmd, req, ctx.DefaultProjectID()) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/apikey_update.go b/products/umodelverse/internal/umodelverse/apikey_update.go new file mode 100644 index 0000000000..e91a7fe45d --- /dev/null +++ b/products/umodelverse/internal/umodelverse/apikey_update.go @@ -0,0 +1,80 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + sdk "github.com/ucloud/ucloud-sdk-go/ucloud" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newAPIKeyUpdate(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &apiKeyRequest{} + newRequest(client, req, false) + var grantedModels []string + var quotaAlertChannels []string + + cmd := &cobra.Command{ + Use: "update", + Short: "Update a uModelVerse API key", + Long: "Update a uModelVerse API key.", + Run: func(c *cobra.Command, args []string) { + flags := c.Flags() + req.KeyId = sdk.String(ctx.PickResourceID(*req.KeyId)) + clearStringIfUnchanged(flags, "name", &req.Name) + clearIntIfUnchanged(flags, "status", &req.Status) + clearIntIfUnchanged(flags, "modelverse-disabled", &req.ModelverseDisabled) + clearIntIfUnchanged(flags, "sandbox-disabled", &req.SandBoxDisabled) + clearStringIfUnchanged(flags, "daily-limit-amount", &req.DailyLimitAmount) + clearStringIfUnchanged(flags, "monthly-limit-amount", &req.MonthlyLimitAmount) + clearInt64IfUnchanged(flags, "expire-time", &req.ExpireTime) + clearBoolIfUnchanged(flags, "grant-all-models", &req.GrantAllModels) + req.GrantedModels = stringSliceJSONRef(grantedModels) + clearStringIfUnchanged(flags, "ip-whitelist", &req.IPWhitelist) + clearIntIfUnchanged(flags, "daily-quota-alert-threshold", &req.DailyQuotaAlertThreshold) + clearIntIfUnchanged(flags, "monthly-quota-alert-threshold", &req.MonthlyQuotaAlertThreshold) + req.QuotaAlertChannels = stringSliceJSONRef(quotaAlertChannels) + clearStringIfUnchanged(flags, "quota-alert-email", &req.QuotaAlertEmail) + clearStringIfUnchanged(flags, "quota-alert-phone", &req.QuotaAlertPhone) + clearStringIfUnchanged(flags, "quota-alert-email-verification-token", &req.QuotaAlertEmailVerificationToken) + clearStringIfUnchanged(flags, "quota-alert-phone-verification-token", &req.QuotaAlertPhoneVerificationToken) + if req.IPWhitelist != nil { + req.IPWhitelist = sdk.String(cleanMultilineFlag(*req.IPWhitelist)) + } + resp, err := invokeUMAction(client, "UpdateUMInferAPIKey", req) + if err != nil { + ctx.HandleError(err) + return + } + fmt.Fprintf(ctx.ProgressWriter(), "umodelverse apikey[%s] updated\n", *req.KeyId) + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + req.KeyId = flags.String("key-id", "", "Required. API key ID to update.") + req.Name = flags.String("name", "", "Optional. Updated API key name.") + req.Status = flags.Int("status", 0, "Optional. API key status: 1 enabled, 2 disabled.") + req.ModelverseDisabled = flags.Int("modelverse-disabled", 0, "Optional. Whether ModelVerse is disabled: 0 enabled, 1 disabled.") + req.SandBoxDisabled = flags.Int("sandbox-disabled", 0, "Optional. Whether sandbox is disabled: 0 enabled, 1 disabled.") + req.DailyLimitAmount = flags.String("daily-limit-amount", "", "Optional. Daily limit amount.") + req.MonthlyLimitAmount = flags.String("monthly-limit-amount", "", "Optional. Monthly limit amount.") + req.ExpireTime = flags.Int64("expire-time", 0, "Optional. API key expire time, Unix timestamp. Use -1 for never expire.") + req.GrantAllModels = flags.Bool("grant-all-models", true, "Optional. Grant access to all models.") + flags.StringSliceVar(&grantedModels, "granted-models", nil, "Optional. Granted model IDs when --grant-all-models=false. Can be repeated, comma-separated, or a JSON array string.") + req.IPWhitelist = flags.String("ip-whitelist", "", "Optional. IP whitelist, newline-separated; literal \\n is also accepted.") + req.DailyQuotaAlertThreshold = flags.Int("daily-quota-alert-threshold", 0, "Optional. Daily quota alert threshold.") + req.MonthlyQuotaAlertThreshold = flags.Int("monthly-quota-alert-threshold", 0, "Optional. Monthly quota alert threshold.") + flags.StringSliceVar("aAlertChannels, "quota-alert-channel", nil, "Optional. Quota alert channel, e.g. email or sms. Can be repeated or comma-separated.") + req.QuotaAlertEmail = flags.String("quota-alert-email", "", "Optional. Email address for quota alerts.") + req.QuotaAlertPhone = flags.String("quota-alert-phone", "", "Optional. Phone number for quota alerts.") + req.QuotaAlertEmailVerificationToken = flags.String("quota-alert-email-verification-token", "", "Optional. Email verification token for quota alerts.") + req.QuotaAlertPhoneVerificationToken = flags.String("quota-alert-phone-verification-token", "", "Optional. Phone verification token for quota alerts.") + bindProject(cmd, req, ctx.DefaultProjectID()) + + cmd.MarkFlagRequired("key-id") + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/cmd.go b/products/umodelverse/internal/umodelverse/cmd.go new file mode 100644 index 0000000000..64c3ff9821 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/cmd.go @@ -0,0 +1,23 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +// NewCommand builds the `umodelverse` root command. +func NewCommand(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: productName, + Short: "Manipulate uModelVerse resources", + Long: "Manipulate uModelVerse resources, API keys, model catalog, inference logs, and billing data.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newAPIKey(ctx)) + cmd.AddCommand(newModel(ctx)) + cmd.AddCommand(newLog(ctx)) + cmd.AddCommand(newOrder(ctx)) + cmd.AddCommand(newFilterOptions(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/filter_options.go b/products/umodelverse/internal/umodelverse/filter_options.go new file mode 100644 index 0000000000..1c6eff7a7f --- /dev/null +++ b/products/umodelverse/internal/umodelverse/filter_options.go @@ -0,0 +1,33 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newFilterOptions(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &filterOptionsRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "filter-options", + Short: "Get uModelVerse order filter options", + Long: "Get uModelVerse order filter options.", + Run: func(c *cobra.Command, args []string) { + clearStringIfUnchanged(c.Flags(), "product-code", &req.ProductCode) + resp, err := invokeUMAction(client, "GetFilterOptions", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + req.ProductCode = flags.String("product-code", "", "Optional. Product code, e.g. modelverse or sandbox.") + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/flags.go b/products/umodelverse/internal/umodelverse/flags.go new file mode 100644 index 0000000000..a948b5e1d3 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/flags.go @@ -0,0 +1,119 @@ +package umodelverse + +import ( + "encoding/json" + "strings" + + "github.com/spf13/cobra" + "github.com/spf13/pflag" +) + +func bindProject(cmd *cobra.Command, req interface{ SetProjectIdRef(*string) error }, defaultProject string) { + projectID := defaultProject + cmd.Flags().StringVar(&projectID, "project-id", defaultProject, "Optional. Override default project-id for this command invocation, see 'ucloud project list'") + _ = req.SetProjectIdRef(&projectID) +} + +func bindTimeRange(cmd *cobra.Command, req *orderRequest) { + req.StartTime = cmd.Flags().Int64("start-time", 0, "Required. Query start time, Unix timestamp in seconds.") + req.EndTime = cmd.Flags().Int64("end-time", 0, "Required. Query end time, Unix timestamp in seconds.") + cmd.MarkFlagRequired("start-time") + cmd.MarkFlagRequired("end-time") +} + +func bindOrderFilters(cmd *cobra.Command, req *orderRequest) { + flags := cmd.Flags() + flags.StringSliceVar(&req.ResourceIds, "resource-id", nil, "Optional. Resource ID filter. Can be repeated or comma-separated.") + flags.StringSliceVar(&req.ModelIds, "model-id", nil, "Optional. Model ID filter. Can be repeated or comma-separated.") + flags.IntSliceVar(&req.PricingUnits, "pricing-unit", nil, "Optional. Pricing unit filter. Can be repeated or comma-separated.") + flags.StringSliceVar(&req.PricingSkus, "pricing-sku", nil, "Optional. Pricing SKU filter. Can be repeated or comma-separated.") + flags.IntSliceVar(&req.OrderTypes, "order-type", nil, "Optional. Order type filter. Can be repeated or comma-separated.") + flags.IntSliceVar(&req.OrganizationIds, "organization-id", nil, "Optional. Organization ID filter. Can be repeated or comma-separated.") + flags.StringSliceVar(&req.Regions, "order-region", nil, "Optional. Order region filter. Can be repeated or comma-separated.") + flags.StringSliceVar(&req.ProductCodes, "product-code", nil, "Optional. Product code filter, e.g. modelverse or sandbox.") +} + +func bindPage(cmd *cobra.Command, req *orderRequest) { + req.Page = cmd.Flags().Int("page", 1, "Required. Page number, starting from 1.") + req.PageSize = cmd.Flags().Int("page-size", 20, "Required. Page size.") + cmd.MarkFlagRequired("page") + cmd.MarkFlagRequired("page-size") +} + +func cleanMultilineFlag(s string) string { + return strings.ReplaceAll(s, "\\n", "\n") +} + +func stringSliceJSONRef(values []string) *string { + values = normalizeStringSliceValues(values) + if len(values) == 0 { + return nil + } + b, _ := json.Marshal(values) + s := string(b) + return &s +} + +func intSliceJSONRef(values []int) *string { + if len(values) == 0 { + return nil + } + b, _ := json.Marshal(values) + s := string(b) + return &s +} + +func bindOrderChargeTypes(cmd *cobra.Command, req *orderRequest) { + cmd.Flags().IntSliceVar(&req.ChargeTypes, "charge-type-code", nil, "Optional. Charge type code filter. Can be repeated or comma-separated.") +} + +func normalizeStringSliceValues(values []string) []string { + if len(values) != 1 { + return values + } + raw := strings.TrimSpace(values[0]) + if len(raw) < 2 || raw[0] != '[' || raw[len(raw)-1] != ']' { + return values + } + var parsed []string + if err := json.Unmarshal([]byte(raw), &parsed); err == nil { + return parsed + } + raw = strings.TrimSpace(raw[1 : len(raw)-1]) + if raw == "" { + return nil + } + parts := strings.Split(raw, ",") + parsed = make([]string, 0, len(parts)) + for _, part := range parts { + item := strings.Trim(strings.TrimSpace(part), `"'`) + if item != "" { + parsed = append(parsed, item) + } + } + return parsed +} + +func clearStringIfUnchanged(flags *pflag.FlagSet, name string, target **string) { + if !flags.Changed(name) { + *target = nil + } +} + +func clearIntIfUnchanged(flags *pflag.FlagSet, name string, target **int) { + if !flags.Changed(name) { + *target = nil + } +} + +func clearInt64IfUnchanged(flags *pflag.FlagSet, name string, target **int64) { + if !flags.Changed(name) { + *target = nil + } +} + +func clearBoolIfUnchanged(flags *pflag.FlagSet, name string, target **bool) { + if !flags.Changed(name) { + *target = nil + } +} diff --git a/products/umodelverse/internal/umodelverse/log.go b/products/umodelverse/internal/umodelverse/log.go new file mode 100644 index 0000000000..8969741f47 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/log.go @@ -0,0 +1,20 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newLog(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "log", + Short: "Query and export uModelVerse inference logs", + Long: "Query and export uModelVerse inference logs.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newLogList(ctx)) + cmd.AddCommand(newLogDescribe(ctx)) + cmd.AddCommand(newLogExport(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/log_describe.go b/products/umodelverse/internal/umodelverse/log_describe.go new file mode 100644 index 0000000000..fcf44e4e85 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/log_describe.go @@ -0,0 +1,38 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newLogDescribe(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &logDetailRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "describe", + Short: "Describe a uModelVerse inference request log", + Long: "Describe a uModelVerse inference request log by request ID.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "GetUMInferRequestLogDetail", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + bindProject(cmd, req, ctx.DefaultProjectID()) + req.RequestId = flags.String("request-id", "", "Required. Request ID.") + cmd.MarkFlagRequired("region") + cmd.MarkFlagRequired("zone") + cmd.MarkFlagRequired("request-id") + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/log_export.go b/products/umodelverse/internal/umodelverse/log_export.go new file mode 100644 index 0000000000..cdbecabf23 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/log_export.go @@ -0,0 +1,53 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newLogExport(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &requestLogRequest{} + newRequest(client, req, false) + var modelNames []string + var apiKeyIds []string + + cmd := &cobra.Command{ + Use: "export", + Short: "Export uModelVerse inference request logs", + Long: "Export uModelVerse inference request logs. Time flags use Unix milliseconds.", + Run: func(c *cobra.Command, args []string) { + clearStringIfUnchanged(c.Flags(), "request-id", &req.RequestId) + req.ModelNames = stringSliceJSONRef(modelNames) + req.ApiKeyIds = stringSliceJSONRef(apiKeyIds) + resp, err := invokeUMAction(client, "DownloadUMInferRequestLog", req) + if err != nil { + ctx.HandleError(err) + return + } + fmt.Fprintln(ctx.ProgressWriter(), "umodelverse log export task created") + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + bindProject(cmd, req, ctx.DefaultProjectID()) + req.StartTime = flags.Int64("start-time-ms", 0, "Required. Export start time, Unix timestamp in milliseconds.") + req.EndTime = flags.Int64("end-time-ms", 0, "Required. Export end time, Unix timestamp in milliseconds.") + req.Email = flags.String("email", "", "Required. Email address to receive export result.") + flags.StringSliceVar(&modelNames, "model-name", nil, "Optional. Model name filter. Can be repeated or comma-separated.") + flags.StringSliceVar(&apiKeyIds, "key-id", nil, "Optional. API key ID filter. Can be repeated or comma-separated.") + req.RequestId = flags.String("request-id", "", "Optional. Request ID filter.") + cmd.MarkFlagRequired("region") + cmd.MarkFlagRequired("zone") + cmd.MarkFlagRequired("start-time-ms") + cmd.MarkFlagRequired("end-time-ms") + cmd.MarkFlagRequired("email") + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/log_list.go b/products/umodelverse/internal/umodelverse/log_list.go new file mode 100644 index 0000000000..c4ee339f41 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/log_list.go @@ -0,0 +1,67 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newLogList(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &requestLogRequest{} + newRequest(client, req, true) + var modelNames []string + var apiKeyIds []string + + cmd := &cobra.Command{ + Use: "list", + Short: "List uModelVerse inference request logs", + Long: "List uModelVerse inference request logs.", + PreRunE: func(c *cobra.Command, args []string) error { + if req.Limit != nil && !isAllowedLogLimit(*req.Limit) { + return fmt.Errorf("limit must be one of [10, 20, 50, 100]") + } + return nil + }, + Run: func(c *cobra.Command, args []string) { + clearStringIfUnchanged(c.Flags(), "request-id", &req.RequestId) + req.ModelNames = stringSliceJSONRef(modelNames) + req.ApiKeyIds = stringSliceJSONRef(apiKeyIds) + resp, err := invokeUMAction(client, "ListUMInferRequestLogs", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + bindProject(cmd, req, ctx.DefaultProjectID()) + req.StartTime = flags.Int64("start-time-ms", 0, "Required. Query start time, Unix timestamp in milliseconds.") + req.EndTime = flags.Int64("end-time-ms", 0, "Required. Query end time, Unix timestamp in milliseconds.") + flags.StringSliceVar(&modelNames, "model-name", nil, "Optional. Model name filter. Can be repeated or comma-separated.") + flags.StringSliceVar(&apiKeyIds, "key-id", nil, "Optional. API key ID filter. Can be repeated or comma-separated.") + req.RequestId = flags.String("request-id", "", "Optional. Request ID filter.") + req.Offset = flags.Int("offset", 0, "Optional. The index of log which start to list.") + req.Limit = flags.Int("limit", 20, "Optional. The maximum number of logs per page. Allowed values: 10, 20, 50, 100.") + cmd.MarkFlagRequired("region") + cmd.MarkFlagRequired("zone") + cmd.MarkFlagRequired("start-time-ms") + cmd.MarkFlagRequired("end-time-ms") + return cmd +} + +func isAllowedLogLimit(limit int) bool { + switch limit { + case 10, 20, 50, 100: + return true + default: + return false + } +} diff --git a/products/umodelverse/internal/umodelverse/model.go b/products/umodelverse/internal/umodelverse/model.go new file mode 100644 index 0000000000..dde559bc3e --- /dev/null +++ b/products/umodelverse/internal/umodelverse/model.go @@ -0,0 +1,18 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newModel(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "model", + Short: "Query uModelVerse models", + Long: "Query uModelVerse models.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newModelList(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/model_list.go b/products/umodelverse/internal/umodelverse/model_list.go new file mode 100644 index 0000000000..55aded952e --- /dev/null +++ b/products/umodelverse/internal/umodelverse/model_list.go @@ -0,0 +1,51 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newModelList(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &squareModelRequest{} + newRequest(client, req, true) + var maxModelLen []int + var language []string + + cmd := &cobra.Command{ + Use: "list", + Short: "List uModelVerse square models", + Long: "List uModelVerse square models.", + Run: func(c *cobra.Command, args []string) { + flags := c.Flags() + clearStringIfUnchanged(flags, "model-type", &req.ModelType) + clearStringIfUnchanged(flags, "keyword", &req.KeyWord) + clearStringIfUnchanged(flags, "order-by", &req.OrderBy) + clearStringIfUnchanged(flags, "order", &req.Order) + req.MaxModelLen = intSliceJSONRef(maxModelLen) + req.Language = stringSliceJSONRef(language) + resp, err := invokeUMAction(client, "ListUFSquareModel", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + ctx.BindRegion(cmd, req) + ctx.BindZone(cmd, req) + ctx.BindProjectID(cmd, req) + req.ModelType = flags.String("model-type", "", "Optional. Model type.") + req.KeyWord = flags.String("keyword", "", "Optional. Keyword filter.") + req.Offset = flags.Int("offset", 0, "Optional. The index of model which start to list.") + req.Limit = flags.Int("limit", 20, "Optional. The maximum number of models per page.") + req.OrderBy = flags.String("order-by", "", "Optional. Sort field.") + req.Order = flags.String("order", "", "Optional. Sort order.") + flags.IntSliceVar(&maxModelLen, "max-model-len", nil, "Optional. Context length filter. Can be repeated or comma-separated.") + flags.StringSliceVar(&language, "language", nil, "Optional. Language filter, e.g. chinese or english. Can be repeated or comma-separated.") + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order.go b/products/umodelverse/internal/umodelverse/order.go new file mode 100644 index 0000000000..62b64ec350 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order.go @@ -0,0 +1,21 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrder(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "order", + Short: "Query and export uModelVerse orders", + Long: "Query and export uModelVerse orders.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newOrderAmount(ctx)) + cmd.AddCommand(newOrderPaid(ctx)) + cmd.AddCommand(newOrderUnpaid(ctx)) + cmd.AddCommand(newOrderSummary(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_amount.go b/products/umodelverse/internal/umodelverse/order_amount.go new file mode 100644 index 0000000000..34d9f15359 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_amount.go @@ -0,0 +1,33 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderAmount(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "amount", + Short: "Get uModelVerse order amount statistics", + Long: "Get uModelVerse order amount statistics.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "GetOrderAmount", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindOrderFilters(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_paid.go b/products/umodelverse/internal/umodelverse/order_paid.go new file mode 100644 index 0000000000..65f314afc0 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_paid.go @@ -0,0 +1,20 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderPaid(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "paid", + Short: "Query and export paid uModelVerse orders", + Long: "Query and export paid uModelVerse orders.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newOrderPaidList(ctx)) + cmd.AddCommand(newOrderPaidSummary(ctx)) + cmd.AddCommand(newOrderPaidExport(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_paid_export.go b/products/umodelverse/internal/umodelverse/order_paid_export.go new file mode 100644 index 0000000000..1a00360810 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_paid_export.go @@ -0,0 +1,36 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderPaidExport(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, false) + + cmd := &cobra.Command{ + Use: "export", + Short: "Export paid uModelVerse order details", + Long: "Export paid uModelVerse order details as an Excel file download link.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "DownloadListPaidOrders", req) + if err != nil { + ctx.HandleError(err) + return + } + fmt.Fprintln(ctx.ProgressWriter(), "umodelverse paid order export created") + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindOrderFilters(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_paid_list.go b/products/umodelverse/internal/umodelverse/order_paid_list.go new file mode 100644 index 0000000000..7a32553380 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_paid_list.go @@ -0,0 +1,34 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderPaidList(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "list", + Short: "List paid uModelVerse orders", + Long: "List paid uModelVerse orders. Time range is [start-time, end-time), in Unix seconds.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "ListPaidOrders", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindPage(cmd, req) + bindOrderFilters(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_paid_summary.go b/products/umodelverse/internal/umodelverse/order_paid_summary.go new file mode 100644 index 0000000000..52c5aa7509 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_paid_summary.go @@ -0,0 +1,34 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderPaidSummary(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "summary", + Short: "Summarize paid uModelVerse orders", + Long: "Summarize paid uModelVerse orders.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "ListPaidOrderSummary", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindOrderFilters(cmd, req) + bindOrderChargeTypes(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_summary.go b/products/umodelverse/internal/umodelverse/order_summary.go new file mode 100644 index 0000000000..eeebe5bc01 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_summary.go @@ -0,0 +1,18 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderSummary(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "summary", + Short: "Export uModelVerse order summaries", + Long: "Export uModelVerse order summaries.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newOrderSummaryExport(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_summary_export.go b/products/umodelverse/internal/umodelverse/order_summary_export.go new file mode 100644 index 0000000000..ad8bdfe1fb --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_summary_export.go @@ -0,0 +1,37 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderSummaryExport(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, false) + + cmd := &cobra.Command{ + Use: "export", + Short: "Export uModelVerse order summary", + Long: "Export uModelVerse order summary as an Excel file download link.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "DownloadOrderSummary", req) + if err != nil { + ctx.HandleError(err) + return + } + fmt.Fprintln(ctx.ProgressWriter(), "umodelverse order summary export created") + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindOrderFilters(cmd, req) + bindOrderChargeTypes(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_unpaid.go b/products/umodelverse/internal/umodelverse/order_unpaid.go new file mode 100644 index 0000000000..9b3207f550 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_unpaid.go @@ -0,0 +1,20 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderUnpaid(ctx *cli.Context) *cobra.Command { + cmd := &cobra.Command{ + Use: "unpaid", + Short: "Query and export unpaid uModelVerse orders", + Long: "Query and export unpaid uModelVerse orders.", + Args: cobra.NoArgs, + } + cmd.AddCommand(newOrderUnpaidList(ctx)) + cmd.AddCommand(newOrderUnpaidSummary(ctx)) + cmd.AddCommand(newOrderUnpaidExport(ctx)) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_unpaid_export.go b/products/umodelverse/internal/umodelverse/order_unpaid_export.go new file mode 100644 index 0000000000..9f2e2f024d --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_unpaid_export.go @@ -0,0 +1,36 @@ +package umodelverse + +import ( + "fmt" + + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderUnpaidExport(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, false) + + cmd := &cobra.Command{ + Use: "export", + Short: "Export unpaid uModelVerse order details", + Long: "Export unpaid uModelVerse order details as an Excel file download link.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "DownloadListUnpaidOrders", req) + if err != nil { + ctx.HandleError(err) + return + } + fmt.Fprintln(ctx.ProgressWriter(), "umodelverse unpaid order export created") + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindOrderFilters(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_unpaid_list.go b/products/umodelverse/internal/umodelverse/order_unpaid_list.go new file mode 100644 index 0000000000..dcd18e6ab0 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_unpaid_list.go @@ -0,0 +1,34 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderUnpaidList(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "list", + Short: "List unpaid uModelVerse orders", + Long: "List unpaid uModelVerse orders.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "ListUnpaidOrders", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindPage(cmd, req) + bindOrderFilters(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/order_unpaid_summary.go b/products/umodelverse/internal/umodelverse/order_unpaid_summary.go new file mode 100644 index 0000000000..917c2b8cc9 --- /dev/null +++ b/products/umodelverse/internal/umodelverse/order_unpaid_summary.go @@ -0,0 +1,34 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" +) + +func newOrderUnpaidSummary(ctx *cli.Context) *cobra.Command { + client := newClient(ctx) + req := &orderRequest{} + newRequest(client, req, true) + + cmd := &cobra.Command{ + Use: "summary", + Short: "Summarize unpaid uModelVerse orders", + Long: "Summarize unpaid uModelVerse orders.", + Run: func(c *cobra.Command, args []string) { + resp, err := invokeUMAction(client, "ListUnpaidOrderSummary", req) + if err != nil { + ctx.HandleError(err) + return + } + printResponse(ctx, resp) + }, + } + + flags := cmd.Flags() + flags.SortFlags = false + bindTimeRange(cmd, req) + bindOrderFilters(cmd, req) + bindOrderChargeTypes(cmd, req) + return cmd +} diff --git a/products/umodelverse/internal/umodelverse/rows.go b/products/umodelverse/internal/umodelverse/rows.go new file mode 100644 index 0000000000..764b0e9bde --- /dev/null +++ b/products/umodelverse/internal/umodelverse/rows.go @@ -0,0 +1,6 @@ +package umodelverse + +type fieldRow struct { + Field string + Value string +} diff --git a/products/umodelverse/product.go b/products/umodelverse/product.go new file mode 100644 index 0000000000..7c1c64afe6 --- /dev/null +++ b/products/umodelverse/product.go @@ -0,0 +1,21 @@ +package umodelverse + +import ( + "github.com/spf13/cobra" + + "github.com/ucloud/ucloud-cli/pkg/cli" + internalumodelverse "github.com/ucloud/ucloud-cli/products/umodelverse/internal/umodelverse" +) + +type product struct{} + +// New returns the umodelverse product (registered via hack/gen-products). +func New() cli.Product { return product{} } + +func (product) Metadata() cli.Metadata { + return cli.Metadata{Name: "umodelverse", Commands: []string{"umodelverse"}} +} + +func (product) NewCommand(ctx *cli.Context) []*cobra.Command { + return []*cobra.Command{internalumodelverse.NewCommand(ctx)} +} diff --git a/products/umodelverse/product.yaml b/products/umodelverse/product.yaml new file mode 100644 index 0000000000..93e6551663 --- /dev/null +++ b/products/umodelverse/product.yaml @@ -0,0 +1,7 @@ +# products/umodelverse/product.yaml — umodelverse 产品元数据(归属真源,owner 自治维护) +name: umodelverse +owners: + - Episkey-G +commands: + - umodelverse +enabled: true diff --git a/products/umodelverse/testdata/cmdtree.golden b/products/umodelverse/testdata/cmdtree.golden new file mode 100644 index 0000000000..d3209208c9 --- /dev/null +++ b/products/umodelverse/testdata/cmdtree.golden @@ -0,0 +1,192 @@ +ucloud umodelverse use=umodelverse short=Manipulate uModelVerse resources +ucloud umodelverse apikey use=apikey short=Manage uModelVerse API keys +ucloud umodelverse apikey create use=create short=Create a uModelVerse API key + flag=daily-limit-amount short= default= required= + flag=daily-quota-alert-threshold short= default=0 required= + flag=expire-time short= default=0 required= + flag=grant-all-models short= default=true required= + flag=granted-models short= default=[] required= + flag=ip-whitelist short= default= required= + flag=modelverse-disabled short= default=0 required= + flag=monthly-limit-amount short= default= required= + flag=monthly-quota-alert-threshold short= default=0 required= + flag=name short= default= required=true + flag=project-id short= default= required= + flag=quota-alert-channel short= default=[] required= + flag=quota-alert-email short= default= required= + flag=quota-alert-email-verification-token short= default= required= + flag=quota-alert-phone short= default= required= + flag=quota-alert-phone-verification-token short= default= required= + flag=sandbox-disabled short= default=0 required= +ucloud umodelverse apikey delete use=delete short=Delete a uModelVerse API key + flag=key-id short= default= required=true + flag=project-id short= default= required= + flag=yes short=y default=false required= +ucloud umodelverse apikey list use=list short=List uModelVerse API keys + flag=key-id short= default= required= + flag=limit short= default=20 required= + flag=modelverse-disabled short= default=0 required= + flag=offset short= default=0 required= + flag=project-id short= default= required= + flag=sandbox-disabled short= default=0 required= +ucloud umodelverse apikey update use=update short=Update a uModelVerse API key + flag=daily-limit-amount short= default= required= + flag=daily-quota-alert-threshold short= default=0 required= + flag=expire-time short= default=0 required= + flag=grant-all-models short= default=true required= + flag=granted-models short= default=[] required= + flag=ip-whitelist short= default= required= + flag=key-id short= default= required=true + flag=modelverse-disabled short= default=0 required= + flag=monthly-limit-amount short= default= required= + flag=monthly-quota-alert-threshold short= default=0 required= + flag=name short= default= required= + flag=project-id short= default= required= + flag=quota-alert-channel short= default=[] required= + flag=quota-alert-email short= default= required= + flag=quota-alert-email-verification-token short= default= required= + flag=quota-alert-phone short= default= required= + flag=quota-alert-phone-verification-token short= default= required= + flag=sandbox-disabled short= default=0 required= + flag=status short= default=0 required= +ucloud umodelverse filter-options use=filter-options short=Get uModelVerse order filter options + flag=product-code short= default= required= +ucloud umodelverse log use=log short=Query and export uModelVerse inference logs +ucloud umodelverse log describe use=describe short=Describe a uModelVerse inference request log + flag=project-id short= default= required= + flag=region short= default= required=true + flag=request-id short= default= required=true + flag=zone short= default= required=true +ucloud umodelverse log export use=export short=Export uModelVerse inference request logs + flag=email short= default= required=true + flag=end-time-ms short= default=0 required=true + flag=key-id short= default=[] required= + flag=model-name short= default=[] required= + flag=project-id short= default= required= + flag=region short= default= required=true + flag=request-id short= default= required= + flag=start-time-ms short= default=0 required=true + flag=zone short= default= required=true +ucloud umodelverse log list use=list short=List uModelVerse inference request logs + flag=end-time-ms short= default=0 required=true + flag=key-id short= default=[] required= + flag=limit short= default=20 required= + flag=model-name short= default=[] required= + flag=offset short= default=0 required= + flag=project-id short= default= required= + flag=region short= default= required=true + flag=request-id short= default= required= + flag=start-time-ms short= default=0 required=true + flag=zone short= default= required=true +ucloud umodelverse model use=model short=Query uModelVerse models +ucloud umodelverse model list use=list short=List uModelVerse square models + flag=keyword short= default= required= + flag=language short= default=[] required= + flag=limit short= default=20 required= + flag=max-model-len short= default=[] required= + flag=model-type short= default= required= + flag=offset short= default=0 required= + flag=order short= default= required= + flag=order-by short= default= required= + flag=project-id short= default= required= + flag=region short= default= required= + flag=zone short= default= required= +ucloud umodelverse order use=order short=Query and export uModelVerse orders +ucloud umodelverse order amount use=amount short=Get uModelVerse order amount statistics + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true +ucloud umodelverse order paid use=paid short=Query and export paid uModelVerse orders +ucloud umodelverse order paid export use=export short=Export paid uModelVerse order details + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true +ucloud umodelverse order paid list use=list short=List paid uModelVerse orders + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=page short= default=1 required=true + flag=page-size short= default=20 required=true + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true +ucloud umodelverse order paid summary use=summary short=Summarize paid uModelVerse orders + flag=charge-type-code short= default=[] required= + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true +ucloud umodelverse order summary use=summary short=Export uModelVerse order summaries +ucloud umodelverse order summary export use=export short=Export uModelVerse order summary + flag=charge-type-code short= default=[] required= + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true +ucloud umodelverse order unpaid use=unpaid short=Query and export unpaid uModelVerse orders +ucloud umodelverse order unpaid export use=export short=Export unpaid uModelVerse order details + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true +ucloud umodelverse order unpaid list use=list short=List unpaid uModelVerse orders + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=page short= default=1 required=true + flag=page-size short= default=20 required=true + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true +ucloud umodelverse order unpaid summary use=summary short=Summarize unpaid uModelVerse orders + flag=charge-type-code short= default=[] required= + flag=end-time short= default=0 required=true + flag=model-id short= default=[] required= + flag=order-region short= default=[] required= + flag=order-type short= default=[] required= + flag=organization-id short= default=[] required= + flag=pricing-sku short= default=[] required= + flag=pricing-unit short= default=[] required= + flag=product-code short= default=[] required= + flag=resource-id short= default=[] required= + flag=start-time short= default=0 required=true diff --git a/products/umodelverse/testdata/completion.golden b/products/umodelverse/testdata/completion.golden new file mode 100644 index 0000000000..b1bae7f71b --- /dev/null +++ b/products/umodelverse/testdata/completion.golden @@ -0,0 +1,9 @@ +ucloud umodelverse log describe region dynamic +ucloud umodelverse log describe zone dynamic +ucloud umodelverse log export region dynamic +ucloud umodelverse log export zone dynamic +ucloud umodelverse log list region dynamic +ucloud umodelverse log list zone dynamic +ucloud umodelverse model list project-id dynamic +ucloud umodelverse model list region dynamic +ucloud umodelverse model list zone dynamic