diff --git a/.golangci.yml b/.golangci.yml index fd324b4..67f063d 100644 --- a/.golangci.yml +++ b/.golangci.yml @@ -6,7 +6,6 @@ linters: - containedctx - contextcheck - errorlint - - goconst - godot - importas - modernize diff --git a/AGENTS.md b/AGENTS.md index 14316f5..7a5b490 100644 --- a/AGENTS.md +++ b/AGENTS.md @@ -6,7 +6,7 @@ Go CLI for [Qdrant Cloud](https://cloud.qdrant.io), built with Cobra / Viper and - **Module:** `github.com/qdrant/qcloud-cli` - **Binary:** `qcloud` (built to `build/qcloud`) -- **Go version:** 1.26+ +- **Go version:** 1.27+ - **Key dependencies:** `cobra`, `viper`, `google.golang.org/grpc`, `qdrant-cloud-public-api` (generated gRPC stubs) ## Project structure diff --git a/docs/reference/qcloud.md b/docs/reference/qcloud.md index 3bcc920..f63b35e 100644 --- a/docs/reference/qcloud.md +++ b/docs/reference/qcloud.md @@ -36,6 +36,7 @@ Documentation: https://github.com/qdrant/qcloud-cli * [qcloud context](qcloud_context.md) - Manage named configuration contexts * [qcloud hybrid](qcloud_hybrid.md) - Manage hybrid cloud environments * [qcloud iam](qcloud_iam.md) - Manage IAM resources in Qdrant Cloud +* [qcloud inference](qcloud_inference.md) - Manage inference resources * [qcloud package](qcloud_package.md) - Manage packages * [qcloud self-upgrade](qcloud_self-upgrade.md) - Upgrade qcloud to the latest version * [qcloud version](qcloud_version.md) - Print the qcloud CLI version diff --git a/docs/reference/qcloud_inference.md b/docs/reference/qcloud_inference.md new file mode 100644 index 0000000..c0a1a26 --- /dev/null +++ b/docs/reference/qcloud_inference.md @@ -0,0 +1,44 @@ +## qcloud inference + +Manage inference resources + +### Synopsis + +Inspect the inference models offered by Qdrant Cloud. + +Inference models turn text or images into dense, sparse or multi vectors directly +inside Qdrant Cloud, so a cluster can embed documents and queries without a +separate embedding service. Which models are offered depends on the cloud +provider and region a cluster runs in. + +### Examples + +``` +# List the inference models available in a region +qcloud inference models list --cloud-provider aws --cloud-region eu-central-1 +``` + +### Options + +``` + -h, --help help for inference +``` + +### Options inherited from parent commands + +``` + --account-id string Qdrant Cloud Account ID (env: QDRANT_CLOUD_ACCOUNT_ID) + --api-key string Management API Key (env: QDRANT_CLOUD_API_KEY) + -c, --config string Config file path (env: QDRANT_CLOUD_CONFIG, default ~/.config/qcloud/config.yaml) + --console-url string Qdrant Cloud web console base URL (env: QDRANT_CLOUD_CONSOLE_URL, default https://cloud.qdrant.io) + --context string Override the active context (env: QDRANT_CLOUD_CONTEXT) + --debug Enable debug logging to stderr + --endpoint string gRPC API endpoint (env: QDRANT_CLOUD_ENDPOINT, default grpc.cloud.qdrant.io:443) + --json Output as JSON +``` + +### SEE ALSO + +* [qcloud](qcloud.md) - Qdrant Cloud CLI +* [qcloud inference models](qcloud_inference_models.md) - Manage inference models + diff --git a/docs/reference/qcloud_inference_models.md b/docs/reference/qcloud_inference_models.md new file mode 100644 index 0000000..5d7762b --- /dev/null +++ b/docs/reference/qcloud_inference_models.md @@ -0,0 +1,43 @@ +## qcloud inference models + +Manage inference models + +### Synopsis + +Inspect the individual inference models offered by Qdrant Cloud. + +A model is identified by its name (for example "cohere/*"), and describes the +vectors it produces: the vector type, the modality of the input it accepts, and +the dimensionality of its output. + +### Examples + +``` +# List the inference models available in a region +qcloud inference models list --cloud-provider aws --cloud-region eu-central-1 +``` + +### Options + +``` + -h, --help help for models +``` + +### Options inherited from parent commands + +``` + --account-id string Qdrant Cloud Account ID (env: QDRANT_CLOUD_ACCOUNT_ID) + --api-key string Management API Key (env: QDRANT_CLOUD_API_KEY) + -c, --config string Config file path (env: QDRANT_CLOUD_CONFIG, default ~/.config/qcloud/config.yaml) + --console-url string Qdrant Cloud web console base URL (env: QDRANT_CLOUD_CONSOLE_URL, default https://cloud.qdrant.io) + --context string Override the active context (env: QDRANT_CLOUD_CONTEXT) + --debug Enable debug logging to stderr + --endpoint string gRPC API endpoint (env: QDRANT_CLOUD_ENDPOINT, default grpc.cloud.qdrant.io:443) + --json Output as JSON +``` + +### SEE ALSO + +* [qcloud inference](qcloud_inference.md) - Manage inference resources +* [qcloud inference models list](qcloud_inference_models_list.md) - List available inference models + diff --git a/docs/reference/qcloud_inference_models_list.md b/docs/reference/qcloud_inference_models_list.md new file mode 100644 index 0000000..b8694f5 --- /dev/null +++ b/docs/reference/qcloud_inference_models_list.md @@ -0,0 +1,57 @@ +## qcloud inference models list + +List available inference models + +### Synopsis + +List the inference models globally available for a cloud provider and region. + +The listing is global, not account-specific: it shows every model Qdrant Cloud +offers in that region, together with the vector type and modality it produces, +its output dimensionality and per-request token limit, and its price per one +million processed tokens. External models are served by a third-party vendor and +require that vendor's API key to be configured on the cluster. + +Inference is only offered on managed cloud, so unlike "qcloud package +list" this command cannot be used with hybrid cloud. + +``` +qcloud inference models list [flags] +``` + +### Examples + +``` +# List inference models available on AWS in eu-central-1 +qcloud inference models list --cloud-provider aws --cloud-region eu-central-1 + +# List inference models as JSON +qcloud inference models list --cloud-provider gcp --cloud-region us-east4 --json +``` + +### Options + +``` + --cloud-provider string Cloud provider ID (required) + --cloud-region string Cloud provider region ID (required) + -h, --help help for list + --no-headers Do not print column headers +``` + +### Options inherited from parent commands + +``` + --account-id string Qdrant Cloud Account ID (env: QDRANT_CLOUD_ACCOUNT_ID) + --api-key string Management API Key (env: QDRANT_CLOUD_API_KEY) + -c, --config string Config file path (env: QDRANT_CLOUD_CONFIG, default ~/.config/qcloud/config.yaml) + --console-url string Qdrant Cloud web console base URL (env: QDRANT_CLOUD_CONSOLE_URL, default https://cloud.qdrant.io) + --context string Override the active context (env: QDRANT_CLOUD_CONTEXT) + --debug Enable debug logging to stderr + --endpoint string gRPC API endpoint (env: QDRANT_CLOUD_ENDPOINT, default grpc.cloud.qdrant.io:443) + --json Output as JSON +``` + +### SEE ALSO + +* [qcloud inference models](qcloud_inference_models.md) - Manage inference models + diff --git a/go.mod b/go.mod index 5d8f108..4aa8fa4 100644 --- a/go.mod +++ b/go.mod @@ -1,26 +1,26 @@ module github.com/qdrant/qcloud-cli -go 1.26.6 +go 1.27.1 require ( github.com/creativeprojects/go-selfupdate v1.6.0 - github.com/dustin/go-humanize v1.0.1 + github.com/dustin/go-humanize v1.1.0 github.com/google/uuid v1.6.0 github.com/jedib0t/go-pretty/v6 v6.8.3 github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c - github.com/qdrant/qdrant-cloud-public-api v0.188.0 + github.com/qdrant/qdrant-cloud-public-api v0.199.0 github.com/robfig/cron/v3 v3.0.1 github.com/spf13/cobra v1.10.2 github.com/spf13/pflag v1.0.10 github.com/spf13/viper v1.21.0 github.com/stretchr/testify v1.12.1 - google.golang.org/grpc v1.83.2 + google.golang.org/grpc v1.84.0 google.golang.org/protobuf v1.36.12 gopkg.in/yaml.v3 v3.0.1 ) require ( - buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.1 // indirect + buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.2 // indirect code.gitea.io/sdk/gitea v0.23.2 // indirect github.com/42wim/httpsig v1.2.4 // indirect github.com/Masterminds/semver/v3 v3.5.0 // indirect diff --git a/go.sum b/go.sum index 9551281..b1945a9 100644 --- a/go.sum +++ b/go.sum @@ -1,21 +1,19 @@ -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.1 h1:Slv0uGxx219srASyiaI5C9cDlyG8kNDcXpTSYcuAeE4= -buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.1/go.mod h1:TCt1lluMFnctISJXvkIQ4x3ABrPuUKCWKyjKdkJNBpw= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.2 h1:NbnlmV26O7oZ1iM5tsCI+GEx+3ZSdrhvnKQ/eWSrLiY= +buf.build/gen/go/bufbuild/protovalidate/protocolbuffers/go v1.36.12-20260825204119-511051f7f437.2/go.mod h1:TCt1lluMFnctISJXvkIQ4x3ABrPuUKCWKyjKdkJNBpw= code.gitea.io/sdk/gitea v0.23.2 h1:iJB1FDmLegwfwjX8gotBDHdPSbk/ZR8V9VmEJaVsJYg= code.gitea.io/sdk/gitea v0.23.2/go.mod h1:yyF5+GhljqvA30sRDreoyHILruNiy4ASufugzYg0VHM= github.com/42wim/httpsig v1.2.4 h1:mI5bH0nm4xn7K18fo1K3okNDRq8CCJ0KbBYWyA6r8lU= github.com/42wim/httpsig v1.2.4/go.mod h1:yKsYfSyTBEohkPik224QPFylmzEBtda/kjyIAJjh3ps= github.com/Masterminds/semver/v3 v3.5.0 h1:kQceYJfbupGfZOKZQg0kou0DgAKhzDg2NZPAwZ/2OOE= github.com/Masterminds/semver/v3 v3.5.0/go.mod h1:4V+yj/TJE1HU9XfppCwVMZq3I84lprf4nC11bSS5beM= -github.com/cespare/xxhash/v2 v2.3.0 h1:UL815xU9SqsFlibzuggzjXhog7bL6oX9BbNZnL2UFvs= -github.com/cespare/xxhash/v2 v2.3.0/go.mod h1:VGX0DQ3Q6kWi7AoAeZDth3/j3BFtOZR5XLFGgcrjCOs= github.com/cpuguy83/go-md2man/v2 v2.0.6 h1:XJtiaUW6dEEqVuZiMTn1ldk455QWwEIsMIJlo5vtkx0= github.com/cpuguy83/go-md2man/v2 v2.0.6/go.mod h1:oOW0eioCTA6cOiMLiUPZOpcVxMig6NIQQ7OS05n1F4g= github.com/creativeprojects/go-selfupdate v1.6.0 h1:Bu3cIgdyfI1Pg8XsL8nbaT2uMjfZ8HIoxnBmPJbN0sw= github.com/creativeprojects/go-selfupdate v1.6.0/go.mod h1:Ids8O474XGQG0jZ5vpBIhWffcGYjUP6ccOI0mMcvQbI= github.com/davidmz/go-pageant v1.0.2 h1:bPblRCh5jGU+Uptpz6LgMZGD5hJoOt7otgT454WvHn0= github.com/davidmz/go-pageant v1.0.2/go.mod h1:P2EDDnMqIwG5Rrp05dTRITj9z2zpGcD9efWSkTNKLIE= -github.com/dustin/go-humanize v1.0.1 h1:GzkhY7T5VNhEkwH0PVJgjz+fX1rhBrR7pRT3mDkpeCY= -github.com/dustin/go-humanize v1.0.1/go.mod h1:Mu1zIs6XwVuF/gI1OepvI0qD18qycQx+mFykh5fBlto= +github.com/dustin/go-humanize v1.1.0 h1:dbKTrvD0klcbBV/h4AWJdMuZogJACoMlvWIWZ5b2xWg= +github.com/dustin/go-humanize v1.1.0/go.mod h1:hc1CvRkJMsgxqjmjMQF3QNRAZBwY8AXBAzKYoSX9sFI= github.com/fatih/color v1.16.0 h1:zmkK9Ngbjj+K0yRhTVONQh1p/HknKYSlNT+vZCzyokM= github.com/fatih/color v1.16.0/go.mod h1:fL2Sau1YI5c0pdGEVCbKQbLXB6edEj1ZgiY4NijnWvE= github.com/frankban/quicktest v1.14.6 h1:7Xjx+VpznH+oBnejlPUj8oUpdxnVs4f8XU8WnHkI4W8= @@ -24,10 +22,6 @@ github.com/fsnotify/fsnotify v1.9.0 h1:2Ml+OJNzbYCTzsxtv8vKSFD9PbJjmhYF14k/jKC7S github.com/fsnotify/fsnotify v1.9.0/go.mod h1:8jBTzvmWwFyi3Pb8djgCCO5IBqzKJ/Jwo8TRcHyHii0= github.com/go-fed/httpsig v1.1.0 h1:9M+hb0jkEICD8/cAiNqEB66R87tTINszBRTjwjQzWcI= github.com/go-fed/httpsig v1.1.0/go.mod h1:RCMrTZvN1bJYtofsG4rd5NaO5obxQ5xBkdiS7xsT7bM= -github.com/go-logr/logr v1.4.3 h1:CjnDlHq8ikf6E492q6eKboGOC0T8CDaOvkHCIg8idEI= -github.com/go-logr/logr v1.4.3/go.mod h1:9T104GzyrTigFIr8wt5mBrctHMim0Nb2HLGrmQ40KvY= -github.com/go-logr/stdr v1.2.2 h1:hSWxHoqTgW2S2qGc0LTAI563KZ5YKYRhT3MFKZMbjag= -github.com/go-logr/stdr v1.2.2/go.mod h1:mMo/vtBO5dYbehREoey6XUKy/eSumjCCveDpRre4VKE= github.com/go-viper/mapstructure/v2 v2.4.0 h1:EBsztssimR/CONLSZZ04E8qAkxNYq4Qp9LvH92wZUgs= github.com/go-viper/mapstructure/v2 v2.4.0/go.mod h1:oJDH3BJKyqBA2TXFhDsKDGDTlndYOZ6rGS0BRZIxGhM= github.com/golang/protobuf v1.5.4 h1:i7eJL8qZTpSEXOPTxNKhASYpMn+8e5Q6AdndVa1dWek= @@ -69,8 +63,8 @@ github.com/pelletier/go-toml/v2 v2.2.4 h1:mye9XuhQ6gvn5h28+VilKrrPoQVanw5PMw/TB0 github.com/pelletier/go-toml/v2 v2.2.4/go.mod h1:2gIqNv+qfxSVS7cM2xJQKtLSTLUE9V8t9Stt+h56mCY= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c h1:+mdjkGKdHQG3305AYmdv1U2eRNDiU2ErMBj1gwrq8eQ= github.com/pkg/browser v0.0.0-20240102092130-5ac0b6a4141c/go.mod h1:7rwL4CYBLnjLxUqIJNnCWiEdr3bn6IUYi15bNlnbCCU= -github.com/qdrant/qdrant-cloud-public-api v0.188.0 h1:GE+vJHBtTAfnW+WnHpzbj0iMgMDq1uOjc2gn7oI6M4w= -github.com/qdrant/qdrant-cloud-public-api v0.188.0/go.mod h1:7m6a2aCSN/G2JLs2c9mGQKCoaAFm1wUWO/WlVe3B9d0= +github.com/qdrant/qdrant-cloud-public-api v0.199.0 h1:nDOOEu2NdkejsXYYsyDfli0Cq4UBiUGs/tGNGKRrSbw= +github.com/qdrant/qdrant-cloud-public-api v0.199.0/go.mod h1:kp/X69V2f0g/eQtzl5k4R/RE6tgcKcgBTq/zmQxTOB0= github.com/rivo/uniseg v0.2.0/go.mod h1:J6wj4VEh+S6ZtnVlnTBMWIodfgj8LQOQFoIToxlJtxc= github.com/rivo/uniseg v0.4.7 h1:WUdvkW8uEhrYfLC4ZzdpI2ztxP1I582+49Oc5Mq64VQ= github.com/rivo/uniseg v0.4.7/go.mod h1:FN3SvrM+Zdj16jyLfmOkMNblXMcoc8DfTHruCPUcx88= @@ -103,18 +97,6 @@ github.com/ulikunitz/xz v0.5.15 h1:9DNdB5s+SgV3bQ2ApL10xRc35ck0DuIX/isZvIk+ubY= github.com/ulikunitz/xz v0.5.15/go.mod h1:nbz6k7qbPmH4IRqmfOplQw/tblSgqTqBwxkY0oWt/14= gitlab.com/gitlab-org/api/client-go v1.46.0 h1:YxBWFZIFYKcGESCb9fpkwzouo+apyB9pr/XTWzNoL24= gitlab.com/gitlab-org/api/client-go v1.46.0/go.mod h1:FtgyU6g2HS5+fMhw6nLK96GBEEBx5MzntOiJWfIaiN8= -go.opentelemetry.io/auto/sdk v1.2.1 h1:jXsnJ4Lmnqd11kwkBV2LgLoFMZKizbCi5fNZ/ipaZ64= -go.opentelemetry.io/auto/sdk v1.2.1/go.mod h1:KRTj+aOaElaLi+wW1kO/DZRXwkF4C5xPbEe3ZiIhN7Y= -go.opentelemetry.io/otel v1.44.0 h1:JjwHmHpA4iZ3wBxluu2fbbE7j4kqlE8jXyAyPXH7HqU= -go.opentelemetry.io/otel v1.44.0/go.mod h1:BMgjTHL9WPRlRjL2oZCBTL4whCGtXch2H4BhOPIAyYc= -go.opentelemetry.io/otel/metric v1.44.0 h1:1w0gILTcHdr3YI+ixLyjemwrVnsMURbTZFrSYCdDdmc= -go.opentelemetry.io/otel/metric v1.44.0/go.mod h1:8O7hanEPBNgEMmybD3s2VBKcgWOCsA6tzHBPODAiquo= -go.opentelemetry.io/otel/sdk v1.44.0 h1:nHYwb9lK+fJPU/dnT6s7W7Z8itMWyqrnVfbheVYrZ58= -go.opentelemetry.io/otel/sdk v1.44.0/go.mod h1:Osuydd3Se74nqjAKxid74N5eC+jfEqfTegHRnq58oK0= -go.opentelemetry.io/otel/sdk/metric v1.44.0 h1:3LlKgI+VjbVsjNRFZJZAJ30WjXC5VkNRks6si09iEfI= -go.opentelemetry.io/otel/sdk/metric v1.44.0/go.mod h1:5B5pMARnXxKhltooO4xUuCBorl65a4EpnTalObqOigA= -go.opentelemetry.io/otel/trace v1.44.0 h1:jxF5CsGYCe74MCRx2X4g7WsY/VBKRqqpNvXlX/6gtIk= -go.opentelemetry.io/otel/trace v1.44.0/go.mod h1:oLl1jrMQAVo6v3GAggN+1VH9VIz9iUSvW53sW1Q8PIE= go.yaml.in/yaml/v3 v3.0.4/go.mod h1:DhzuOOF2ATzADvBadXxruRBLzYTpT36CKvDb3+aBEFg= go.yaml.in/yaml/v3 v3.0.5 h1:N6y/pJk8buWs9NY5ERU2HSMfm+IuD/OtfdAnq6kESPw= go.yaml.in/yaml/v3 v3.0.5/go.mod h1:HVTZu1O7/Vkt2N+BFy8Zza+lnLsABggaTM2ZpNIGuKg= @@ -151,8 +133,8 @@ google.golang.org/genproto/googleapis/api v0.0.0-20260713224248-f5fc221cf8c4 h1: google.golang.org/genproto/googleapis/api v0.0.0-20260713224248-f5fc221cf8c4/go.mod h1:WRrQ7/7N19PypuT0fxLOL5Lq0waoiRri4FbtHDEKrGE= google.golang.org/genproto/googleapis/rpc v0.0.0-20260713224248-f5fc221cf8c4 h1:7RtFDizMtT9eZzHzKxifoMGfcDBBy+LYZlgfg24ZmOM= google.golang.org/genproto/googleapis/rpc v0.0.0-20260713224248-f5fc221cf8c4/go.mod h1:4Hqkh8ycfw05ld/3BWL7rJOSfebL2Q+DVDeRgYgxUU8= -google.golang.org/grpc v1.83.2 h1:EManeRomTObA0BU7I8vXgg/78uE5MJ9M8B39EX2WscU= -google.golang.org/grpc v1.83.2/go.mod h1:YPI1hK3kDked6iHvgX3tR0y+nX/qpMFKhPgFsokw1S8= +google.golang.org/grpc v1.84.0 h1:soMyaPJ8pAak5PIQ0DGBUir0XRo2fRoMqhNWMLlLxO0= +google.golang.org/grpc v1.84.0/go.mod h1:ljCht0DrxQrXBDRTZp52Qxh3Ffk8CdYm2sj4O2QN2C0= google.golang.org/protobuf v1.36.12 h1:pJOKDDOyeXErUroCihFAd5LQuwXBSpVnKGrj5o/fwxc= google.golang.org/protobuf v1.36.12/go.mod h1:HTf+CrKn2C3g5S8VImy6tdcUvCska2kB7j23XfzDpco= gopkg.in/check.v1 v0.0.0-20161208181325-20d25e280405/go.mod h1:Co6ibVJAznAaIkqp8huTwlJQCZ016jof/cbN4VW5Yz0= diff --git a/internal/cli/root.go b/internal/cli/root.go index 1a3b69a..1234d6f 100644 --- a/internal/cli/root.go +++ b/internal/cli/root.go @@ -13,6 +13,7 @@ import ( contextcmd "github.com/qdrant/qcloud-cli/internal/cmd/context" "github.com/qdrant/qcloud-cli/internal/cmd/hybrid" "github.com/qdrant/qcloud-cli/internal/cmd/iam" + "github.com/qdrant/qcloud-cli/internal/cmd/inference" packagecmd "github.com/qdrant/qcloud-cli/internal/cmd/package" "github.com/qdrant/qcloud-cli/internal/cmd/selfupgrade" "github.com/qdrant/qcloud-cli/internal/cmd/version" @@ -80,6 +81,7 @@ Documentation: https://github.com/qdrant/qcloud-cli`, cmd.AddCommand(backup.NewCommand(s)) cmd.AddCommand(hybrid.NewCommand(s)) cmd.AddCommand(packagecmd.NewCommand(s)) + cmd.AddCommand(inference.NewCommand(s)) cmd.AddCommand(selfupgrade.NewCommand(s)) return cmd diff --git a/internal/cmd/inference/inference.go b/internal/cmd/inference/inference.go new file mode 100644 index 0000000..2c2caf7 --- /dev/null +++ b/internal/cmd/inference/inference.go @@ -0,0 +1,26 @@ +package inference + +import ( + "github.com/spf13/cobra" + + "github.com/qdrant/qcloud-cli/internal/state" +) + +// NewCommand creates the "inference" parent command and registers all subcommands. +func NewCommand(s *state.State) *cobra.Command { + cmd := &cobra.Command{ + Use: "inference", + Short: "Manage inference resources", + Long: `Inspect the inference models offered by Qdrant Cloud. + +Inference models turn text or images into dense, sparse or multi vectors directly +inside Qdrant Cloud, so a cluster can embed documents and queries without a +separate embedding service. Which models are offered depends on the cloud +provider and region a cluster runs in.`, + Example: `# List the inference models available in a region +qcloud inference models list --cloud-provider aws --cloud-region eu-central-1`, + Args: cobra.NoArgs, + } + cmd.AddCommand(newModelsCommand(s)) + return cmd +} diff --git a/internal/cmd/inference/models.go b/internal/cmd/inference/models.go new file mode 100644 index 0000000..9708ba8 --- /dev/null +++ b/internal/cmd/inference/models.go @@ -0,0 +1,24 @@ +package inference + +import ( + "github.com/spf13/cobra" + + "github.com/qdrant/qcloud-cli/internal/state" +) + +func newModelsCommand(s *state.State) *cobra.Command { + cmd := &cobra.Command{ + Use: "models", + Short: "Manage inference models", + Long: `Inspect the individual inference models offered by Qdrant Cloud. + +A model is identified by its name (for example "cohere/*"), and describes the +vectors it produces: the vector type, the modality of the input it accepts, and +the dimensionality of its output.`, + Example: `# List the inference models available in a region +qcloud inference models list --cloud-provider aws --cloud-region eu-central-1`, + Args: cobra.NoArgs, + } + cmd.AddCommand(newModelsListCommand(s)) + return cmd +} diff --git a/internal/cmd/inference/models_list.go b/internal/cmd/inference/models_list.go new file mode 100644 index 0000000..0196a9c --- /dev/null +++ b/internal/cmd/inference/models_list.go @@ -0,0 +1,105 @@ +package inference + +import ( + "cmp" + "fmt" + "io" + "slices" + + "github.com/spf13/cobra" + + bookingv1 "github.com/qdrant/qdrant-cloud-public-api/gen/go/qdrant/cloud/booking/v1" + + "github.com/qdrant/qcloud-cli/internal/cmd/base" + "github.com/qdrant/qcloud-cli/internal/cmd/completion" + "github.com/qdrant/qcloud-cli/internal/cmd/output" + "github.com/qdrant/qcloud-cli/internal/state" +) + +func newModelsListCommand(s *state.State) *cobra.Command { + cmd := base.ListCmd[*bookingv1.ListGlobalInferenceModelsResponse]{ + Use: "list", + Short: "List available inference models", + Long: `List the inference models globally available for a cloud provider and region. + +The listing is global, not account-specific: it shows every model Qdrant Cloud +offers in that region, together with the vector type and modality it produces, +its output dimensionality and per-request token limit, and its price per one +million processed tokens. External models are served by a third-party vendor and +require that vendor's API key to be configured on the cluster. + +Inference is only offered on managed cloud, so unlike "qcloud package +list" this command cannot be used with hybrid cloud.`, + Example: `# List inference models available on AWS in eu-central-1 +qcloud inference models list --cloud-provider aws --cloud-region eu-central-1 + +# List inference models as JSON +qcloud inference models list --cloud-provider gcp --cloud-region us-east4 --json`, + Fetch: func(s *state.State, cmd *cobra.Command) (*bookingv1.ListGlobalInferenceModelsResponse, error) { + ctx := cmd.Context() + client, err := s.UnAuthenticatedClient(ctx) + if err != nil { + return nil, err + } + + cloudProvider, _ := cmd.Flags().GetString("cloud-provider") + cloudRegion, _ := cmd.Flags().GetString("cloud-region") + + resp, err := client.Booking().ListGlobalInferenceModels(ctx, &bookingv1.ListGlobalInferenceModelsRequest{ + CloudProviderId: cloudProvider, + CloudProviderRegionId: cloudRegion, + }) + if err != nil { + return nil, fmt.Errorf("failed to list inference models: %w", err) + } + + // The API returns models in no particular order. + slices.SortFunc(resp.GetItems(), func(a, b *bookingv1.InferenceModel) int { + return cmp.Compare(a.GetName(), b.GetName()) + }) + + return resp, nil + }, + OutputTable: func(_ *cobra.Command, w io.Writer, resp *bookingv1.ListGlobalInferenceModelsResponse) (output.TableRenderer, error) { + t := output.NewTable[*bookingv1.InferenceModel](w) + t.AddField("NAME", func(m *bookingv1.InferenceModel) string { + return m.GetName() + }) + t.AddField("TITLE", func(m *bookingv1.InferenceModel) string { + return m.GetTitle() + }) + t.AddField("VENDOR", func(m *bookingv1.InferenceModel) string { + return m.GetVendor() + }) + t.AddField("VECTOR-TYPE", func(m *bookingv1.InferenceModel) string { + return output.VectorType(m.GetVectorType()) + }) + t.AddField("MODALITY", func(m *bookingv1.InferenceModel) string { + return output.ModelModality(m.GetModality()) + }) + t.AddField("DIMENSIONS", func(m *bookingv1.InferenceModel) string { + return output.OptionalValue(m.Dimensionality, "n/a") + }) + t.AddField("MAX-TOKENS", func(m *bookingv1.InferenceModel) string { + return output.OptionalValue(m.MaxTokensPerRequest, "n/a") + }) + t.AddField("EXTERNAL", func(m *bookingv1.InferenceModel) string { + return output.BoolYesNo(m.GetIsExternal()) + }) + t.AddField("PRICE/1M-TOKENS", func(m *bookingv1.InferenceModel) string { + return output.FormatMillicents(int32(m.GetUnitIntPrice()), "USD") + }) + t.SetItems(resp.GetItems()) + return t, nil + }, + }.CobraCommand(s) + + cmd.Flags().String("cloud-provider", "", "Cloud provider ID (required)") + cmd.Flags().String("cloud-region", "", "Cloud provider region ID (required)") + _ = cmd.MarkFlagRequired("cloud-provider") + _ = cmd.MarkFlagRequired("cloud-region") + + _ = cmd.RegisterFlagCompletionFunc("cloud-provider", completion.CloudProviderCompletion(s)) + _ = cmd.RegisterFlagCompletionFunc("cloud-region", completion.CloudRegionCompletion(s)) + return cmd +} diff --git a/internal/cmd/inference/models_list_test.go b/internal/cmd/inference/models_list_test.go new file mode 100644 index 0000000..e565fec --- /dev/null +++ b/internal/cmd/inference/models_list_test.go @@ -0,0 +1,211 @@ +package inference_test + +import ( + "encoding/json" + "fmt" + "strings" + "testing" + + "github.com/stretchr/testify/assert" + "github.com/stretchr/testify/require" + + bookingv1 "github.com/qdrant/qdrant-cloud-public-api/gen/go/qdrant/cloud/booking/v1" + + "github.com/qdrant/qcloud-cli/internal/testutil" +) + +func models() []*bookingv1.InferenceModel { + return []*bookingv1.InferenceModel{ + { + Id: "3b0f4a4e-6b4a-4f2a-9f5a-1f2f3a4b5c6d", + Name: "mixedbread-ai/mxbai-embed-large-v1", + Title: "mxbai-embed-large-v1", + Description: "General purpose English embedding model", + VectorType: bookingv1.VectorType_VECTOR_TYPE_DENSE, + Modality: bookingv1.ModelModality_MODEL_MODALITY_TEXT, + Vendor: "MixedBread", + UnitIntPrice: 5000, + IsExternal: false, + Dimensionality: new(uint32(1024)), + MaxTokensPerRequest: new(uint32(512)), + }, + { + Id: "9a1c2d3e-4f5a-6b7c-8d9e-0f1a2b3c4d5e", + Name: "cohere/embed-v4.0", + Title: "Cohere Embed v4.0", + Description: "Multimodal embedding model from Cohere", + VectorType: bookingv1.VectorType_VECTOR_TYPE_MULTI, + Modality: bookingv1.ModelModality_MODEL_MODALITY_IMAGE, + Vendor: "Cohere", + UnitIntPrice: 0, + IsExternal: true, + }, + } +} + +func TestListInferenceModels_TableOutput(t *testing.T) { + env := testutil.NewTestEnv(t) + + env.BookingServer.ListGlobalInferenceModelsCalls.Returns(&bookingv1.ListGlobalInferenceModelsResponse{ + Items: models(), + }, nil) + + stdout, _, err := testutil.Exec(t, env, + "inference", "models", "list", + "--cloud-provider", "aws", + "--cloud-region", "eu-central-1", + ) + require.NoError(t, err) + + assert.Contains(t, stdout, "NAME") + assert.Contains(t, stdout, "VECTOR-TYPE") + assert.Contains(t, stdout, "MODALITY") + assert.Contains(t, stdout, "DIMENSIONS") + assert.Contains(t, stdout, "MAX-TOKENS") + assert.Contains(t, stdout, "EXTERNAL") + assert.Contains(t, stdout, "PRICE/1M-TOKENS") + + assert.Contains(t, stdout, "mixedbread-ai/mxbai-embed-large-v1") + assert.Contains(t, stdout, "DENSE") + assert.Contains(t, stdout, "TEXT") + assert.Contains(t, stdout, "1024") + assert.Contains(t, stdout, "512") + assert.Contains(t, stdout, "no") + assert.Contains(t, stdout, "0.0500 USD") + + assert.Contains(t, stdout, "cohere/embed-v4.0") + assert.Contains(t, stdout, "MULTI") + assert.Contains(t, stdout, "IMAGE") + // Unset optional dimensionality / token limit render as n/a, free models as "free". + assert.Contains(t, stdout, "n/a") + assert.Contains(t, stdout, "free") + assert.Contains(t, stdout, "yes") +} + +func TestListInferenceModels_SortedByName(t *testing.T) { + env := testutil.NewTestEnv(t) + + // Returned in non-alphabetical order; the command sorts them by name. + env.BookingServer.ListGlobalInferenceModelsCalls.Returns(&bookingv1.ListGlobalInferenceModelsResponse{ + Items: []*bookingv1.InferenceModel{ + {Name: "qdrant/bm25"}, + {Name: "cohere/embed-v4.0"}, + {Name: "mixedbread-ai/mxbai-embed-large-v1"}, + }, + }, nil) + + stdout, _, err := testutil.Exec(t, env, + "inference", "models", "list", + "--cloud-provider", "aws", + "--cloud-region", "eu-central-1", + "--no-headers", + ) + require.NoError(t, err) + + names := make([]string, 0, 3) + for line := range strings.SplitSeq(strings.TrimSpace(stdout), "\n") { + names = append(names, strings.Fields(line)[0]) + } + + assert.Equal(t, []string{ + "cohere/embed-v4.0", + "mixedbread-ai/mxbai-embed-large-v1", + "qdrant/bm25", + }, names) +} + +func TestListInferenceModels_SendsRequestFields(t *testing.T) { + env := testutil.NewTestEnv(t) + + env.BookingServer.ListGlobalInferenceModelsCalls.Returns(&bookingv1.ListGlobalInferenceModelsResponse{}, nil) + + _, _, err := testutil.Exec(t, env, + "inference", "models", "list", + "--cloud-provider", "gcp", + "--cloud-region", "us-east4", + ) + require.NoError(t, err) + + req, ok := env.BookingServer.ListGlobalInferenceModelsCalls.Last() + require.True(t, ok) + assert.Equal(t, "gcp", req.GetCloudProviderId()) + assert.Equal(t, "us-east4", req.GetCloudProviderRegionId()) +} + +func TestListInferenceModels_JSONOutput(t *testing.T) { + env := testutil.NewTestEnv(t) + + env.BookingServer.ListGlobalInferenceModelsCalls.Returns(&bookingv1.ListGlobalInferenceModelsResponse{ + Items: models(), + }, nil) + + stdout, _, err := testutil.Exec(t, env, + "inference", "models", "list", + "--cloud-provider", "aws", + "--cloud-region", "eu-central-1", + "--json", + ) + require.NoError(t, err) + + var resp struct { + Items []struct { + Name string `json:"name"` + Vendor string `json:"vendor"` + VectorType string `json:"vectorType"` + Dimensionality uint32 `json:"dimensionality"` + } `json:"items"` + } + require.NoError(t, json.Unmarshal([]byte(stdout), &resp)) + require.Len(t, resp.Items, 2) + // JSON output is sorted by name too, so cohere comes before mixedbread-ai. + assert.Equal(t, "cohere/embed-v4.0", resp.Items[0].Name) + assert.Equal(t, "Cohere", resp.Items[0].Vendor) + assert.Equal(t, "VECTOR_TYPE_MULTI", resp.Items[0].VectorType) + assert.Equal(t, "mixedbread-ai/mxbai-embed-large-v1", resp.Items[1].Name) + assert.Equal(t, "VECTOR_TYPE_DENSE", resp.Items[1].VectorType) + assert.Equal(t, uint32(1024), resp.Items[1].Dimensionality) +} + +func TestListInferenceModels_NoHeaders(t *testing.T) { + env := testutil.NewTestEnv(t) + + env.BookingServer.ListGlobalInferenceModelsCalls.Returns(&bookingv1.ListGlobalInferenceModelsResponse{ + Items: models(), + }, nil) + + stdout, _, err := testutil.Exec(t, env, + "inference", "models", "list", + "--cloud-provider", "aws", + "--cloud-region", "eu-central-1", + "--no-headers", + ) + require.NoError(t, err) + assert.NotContains(t, stdout, "VECTOR-TYPE") + assert.Contains(t, stdout, "cohere/embed-v4.0") +} + +func TestListInferenceModels_BackendError(t *testing.T) { + env := testutil.NewTestEnv(t) + + env.BookingServer.ListGlobalInferenceModelsCalls.Returns(nil, fmt.Errorf("boom")) + + _, _, err := testutil.Exec(t, env, + "inference", "models", "list", + "--cloud-provider", "aws", + "--cloud-region", "eu-central-1", + ) + require.Error(t, err) + assert.Contains(t, err.Error(), "failed to list inference models") +} + +func TestListInferenceModels_RequiresCloudProviderAndRegion(t *testing.T) { + env := testutil.NewTestEnv(t) + + _, _, err := testutil.Exec(t, env, "inference", "models", "list") + require.Error(t, err) + assert.Contains(t, err.Error(), "cloud-provider") + + _, _, err = testutil.Exec(t, env, "inference", "models", "list", "--cloud-provider", "aws") + require.Error(t, err) + assert.Contains(t, err.Error(), "cloud-region") +} diff --git a/internal/cmd/output/booking.go b/internal/cmd/output/booking.go index d44ecee..3acb197 100644 --- a/internal/cmd/output/booking.go +++ b/internal/cmd/output/booking.go @@ -22,3 +22,13 @@ func FormatMillicents(mc int32, currency string) string { return fmt.Sprintf("%.4f %s", float64(mc)/100_000.0, currency) } + +// VectorType returns a concise label for a VectorType. +func VectorType(t bookingv1.VectorType) string { + return strings.TrimPrefix(t.String(), "VECTOR_TYPE_") +} + +// ModelModality returns a concise label for a ModelModality. +func ModelModality(m bookingv1.ModelModality) string { + return strings.TrimPrefix(m.String(), "MODEL_MODALITY_") +} diff --git a/internal/cmd/package/package.go b/internal/cmd/package/package.go index 4e0606c..00675ba 100644 --- a/internal/cmd/package/package.go +++ b/internal/cmd/package/package.go @@ -27,7 +27,7 @@ func NewCommand(s *state.State) *cobra.Command { } func newListCommand(s *state.State) *cobra.Command { - cmd := base.ListCmd[*bookingv1.ListPackagesResponse]{ + cmd := base.ListCmd[*bookingv1.ListGlobalPackagesResponse]{ Use: "list", Short: "List available packages for cluster creation", Example: `# List packages for a cloud provider and region @@ -35,14 +35,9 @@ qcloud package list --cloud-provider aws --cloud-region eu-central-1 # List packages for a hybrid cloud provider (no region required) qcloud package list --cloud-provider hybrid`, - Fetch: func(s *state.State, cmd *cobra.Command) (*bookingv1.ListPackagesResponse, error) { + Fetch: func(s *state.State, cmd *cobra.Command) (*bookingv1.ListGlobalPackagesResponse, error) { ctx := cmd.Context() - client, err := s.Client(ctx) - if err != nil { - return nil, err - } - - accountID, err := s.AccountID() + client, err := s.UnAuthenticatedClient(ctx) if err != nil { return nil, err } @@ -61,11 +56,9 @@ qcloud package list --cloud-provider hybrid`, cloudRegionPtr = &cloudRegion } - resp, err := client.Booking().ListPackages(ctx, &bookingv1.ListPackagesRequest{ - AccountId: accountID, + resp, err := client.Booking().ListGlobalPackages(ctx, &bookingv1.ListGlobalPackagesRequest{ CloudProviderId: cloudProvider, CloudProviderRegionId: cloudRegionPtr, - Statuses: []bookingv1.PackageStatus{bookingv1.PackageStatus_PACKAGE_STATUS_ACTIVE}, }) if err != nil { return nil, fmt.Errorf("failed to list packages: %w", err) @@ -73,7 +66,7 @@ qcloud package list --cloud-provider hybrid`, return resp, nil }, - OutputTable: func(_ *cobra.Command, w io.Writer, resp *bookingv1.ListPackagesResponse) (output.TableRenderer, error) { + OutputTable: func(_ *cobra.Command, w io.Writer, resp *bookingv1.ListGlobalPackagesResponse) (output.TableRenderer, error) { t := output.NewTable[*bookingv1.Package](w) t.AddField("NAME", func(p *bookingv1.Package) string { return p.GetName() diff --git a/internal/cmd/package/package_test.go b/internal/cmd/package/package_test.go index 0379531..f342c6f 100644 --- a/internal/cmd/package/package_test.go +++ b/internal/cmd/package/package_test.go @@ -14,7 +14,7 @@ import ( func TestListPackages_TableOutput(t *testing.T) { env := testutil.NewTestEnv(t) - env.BookingServer.ListPackagesCalls.Returns(&bookingv1.ListPackagesResponse{ + env.BookingServer.ListGlobalPackagesCalls.Returns(&bookingv1.ListGlobalPackagesResponse{ Items: []*bookingv1.Package{ { Id: "pkg-123", @@ -55,7 +55,7 @@ func TestListPackages_TableOutput(t *testing.T) { func TestListPackages_FreePackage(t *testing.T) { env := testutil.NewTestEnv(t) - env.BookingServer.ListPackagesCalls.Returns(&bookingv1.ListPackagesResponse{ + env.BookingServer.ListGlobalPackagesCalls.Returns(&bookingv1.ListGlobalPackagesResponse{ Items: []*bookingv1.Package{ { Id: "pkg-free", @@ -77,7 +77,7 @@ func TestListPackages_FreePackage(t *testing.T) { func TestListPackages_HybridNoRegion(t *testing.T) { env := testutil.NewTestEnv(t) - env.BookingServer.ListPackagesCalls.Returns(&bookingv1.ListPackagesResponse{ + env.BookingServer.ListGlobalPackagesCalls.Returns(&bookingv1.ListGlobalPackagesResponse{ Items: []*bookingv1.Package{ { Id: "pkg-hybrid", diff --git a/internal/state/state.go b/internal/state/state.go index cb67ea6..9f213bc 100644 --- a/internal/state/state.go +++ b/internal/state/state.go @@ -29,12 +29,13 @@ type BrowserOpener func(url string) error // State holds shared dependencies for all commands. type State struct { - Version string - Config *config.Config - Logger *slog.Logger - client *qcloudapi.Client - updater Updater - openBrowser BrowserOpener + Version string + Config *config.Config + Logger *slog.Logger + client *qcloudapi.Client + unAuthenticatedClient *qcloudapi.Client + updater Updater + openBrowser BrowserOpener } // New creates a new State with the given version string. @@ -66,11 +67,31 @@ func (s *State) Client(ctx context.Context) (*qcloudapi.Client, error) { return s.client, nil } +// UnAuthenticatedClient returns the gRPC client without authentication, creating it lazily on first call. +func (s *State) UnAuthenticatedClient(ctx context.Context) (*qcloudapi.Client, error) { + if s.unAuthenticatedClient != nil { + return s.unAuthenticatedClient, nil + } + + c, err := qcloudapi.New(ctx, s.Config.Endpoint(), "", s.Version) + if err != nil { + return nil, fmt.Errorf("failed to connect to Qdrant Cloud API: %w", err) + } + + s.unAuthenticatedClient = c + return s.unAuthenticatedClient, nil +} + // SetClient injects a pre-built client, bypassing lazy creation. func (s *State) SetClient(c *qcloudapi.Client) { s.client = c } +// SetUnAuthenticatedClient injects a pre-built unauthenticated client, bypassing lazy creation. +func (s *State) SetUnAuthenticatedClient(c *qcloudapi.Client) { + s.unAuthenticatedClient = c +} + // OpenBrowser opens the given URL in the user's default browser. func (s *State) OpenBrowser(url string) error { if s.openBrowser != nil { diff --git a/internal/testutil/fake_booking.go b/internal/testutil/fake_booking.go index 45c7e68..bc97e3b 100644 --- a/internal/testutil/fake_booking.go +++ b/internal/testutil/fake_booking.go @@ -11,8 +11,10 @@ import ( type FakeBookingService struct { bookingv1.UnimplementedBookingServiceServer - ListPackagesCalls MethodSpy[*bookingv1.ListPackagesRequest, *bookingv1.ListPackagesResponse] - GetPackageCalls MethodSpy[*bookingv1.GetPackageRequest, *bookingv1.GetPackageResponse] + ListPackagesCalls MethodSpy[*bookingv1.ListPackagesRequest, *bookingv1.ListPackagesResponse] + ListGlobalPackagesCalls MethodSpy[*bookingv1.ListGlobalPackagesRequest, *bookingv1.ListGlobalPackagesResponse] + GetPackageCalls MethodSpy[*bookingv1.GetPackageRequest, *bookingv1.GetPackageResponse] + ListGlobalInferenceModelsCalls MethodSpy[*bookingv1.ListGlobalInferenceModelsRequest, *bookingv1.ListGlobalInferenceModelsResponse] } // ListPackages records the call and dispatches via ListPackagesCalls. @@ -26,3 +28,15 @@ func (f *FakeBookingService) GetPackage(ctx context.Context, req *bookingv1.GetP f.GetPackageCalls.record(req) return f.GetPackageCalls.dispatch(ctx, req, f.UnimplementedBookingServiceServer.GetPackage) } + +// ListGlobalPackages records the call and dispatches via ListGlobalPackagesCalls. +func (f *FakeBookingService) ListGlobalPackages(ctx context.Context, req *bookingv1.ListGlobalPackagesRequest) (*bookingv1.ListGlobalPackagesResponse, error) { + f.ListGlobalPackagesCalls.record(req) + return f.ListGlobalPackagesCalls.dispatch(ctx, req, f.UnimplementedBookingServiceServer.ListGlobalPackages) +} + +// ListGlobalInferenceModels records the call and dispatches via ListGlobalInferenceModelsCalls. +func (f *FakeBookingService) ListGlobalInferenceModels(ctx context.Context, req *bookingv1.ListGlobalInferenceModelsRequest) (*bookingv1.ListGlobalInferenceModelsResponse, error) { + f.ListGlobalInferenceModelsCalls.record(req) + return f.ListGlobalInferenceModelsCalls.dispatch(ctx, req, f.UnimplementedBookingServiceServer.ListGlobalInferenceModels) +} diff --git a/internal/testutil/server.go b/internal/testutil/server.go index 4328a6b..ca3dbbd 100644 --- a/internal/testutil/server.go +++ b/internal/testutil/server.go @@ -167,6 +167,7 @@ func newBaseTestEnv(t *testing.T, cfg *envConfig) *TestEnv { s := state.New(cfg.version) s.SetClient(client) + s.SetUnAuthenticatedClient(client) var once sync.Once cleanup := func() { diff --git a/mise.lock b/mise.lock index ed25e02..2490d48 100644 --- a/mise.lock +++ b/mise.lock @@ -1,125 +1,125 @@ # @generated - this file is auto-generated by `mise lock` https://mise.jdx.dev/dev-tools/mise-lock.html [[tools."github:golangci/golangci-lint"]] -version = "2.11.3" +version = "2.14.0" backend = "github:golangci/golangci-lint" [tools."github:golangci/golangci-lint"."platforms.linux-arm64"] -checksum = "sha256:ee3d95f301359e7d578e6d99c8ad5aeadbabc5a13009a30b2b0df11c8058afe9" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.3/golangci-lint-2.11.3-linux-arm64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/370749014" +checksum = "sha256:ee7ec5f3453d15ddf106fae5a4d6c71737712348a979d1fe9cd52ec7ea299bae" +url = "https://github.com/golangci/golangci-lint/releases/download/v2.14.0/golangci-lint-2.14.0-linux-arm64.tar.gz" +url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/585813674" provenance = "github-attestations" [tools."github:golangci/golangci-lint"."platforms.linux-arm64-musl"] -checksum = "sha256:ee3d95f301359e7d578e6d99c8ad5aeadbabc5a13009a30b2b0df11c8058afe9" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.3/golangci-lint-2.11.3-linux-arm64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/370749014" +checksum = "sha256:ee7ec5f3453d15ddf106fae5a4d6c71737712348a979d1fe9cd52ec7ea299bae" +url = "https://github.com/golangci/golangci-lint/releases/download/v2.14.0/golangci-lint-2.14.0-linux-arm64.tar.gz" +url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/585813674" provenance = "github-attestations" [tools."github:golangci/golangci-lint"."platforms.linux-x64"] -checksum = "sha256:87bb8cddbcc825d5778b64e8a91b46c0526b247f4e2f2904dea74ec7450475d1" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.3/golangci-lint-2.11.3-linux-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/370748984" +checksum = "sha256:ab90aeb7b066f92a33415b638a50fe5344bbb75a0d32ad30cc248d88f81032ab" +url = "https://github.com/golangci/golangci-lint/releases/download/v2.14.0/golangci-lint-2.14.0-linux-amd64.tar.gz" +url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/585813815" provenance = "github-attestations" [tools."github:golangci/golangci-lint"."platforms.linux-x64-musl"] -checksum = "sha256:87bb8cddbcc825d5778b64e8a91b46c0526b247f4e2f2904dea74ec7450475d1" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.3/golangci-lint-2.11.3-linux-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/370748984" +checksum = "sha256:ab90aeb7b066f92a33415b638a50fe5344bbb75a0d32ad30cc248d88f81032ab" +url = "https://github.com/golangci/golangci-lint/releases/download/v2.14.0/golangci-lint-2.14.0-linux-amd64.tar.gz" +url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/585813815" provenance = "github-attestations" [tools."github:golangci/golangci-lint"."platforms.macos-arm64"] -checksum = "sha256:30ee39979c516b9d1adca289a3f93429d130c4c0fda5e57d637850894221f6cc" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.3/golangci-lint-2.11.3-darwin-arm64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/370749042" +checksum = "sha256:5ef5f36a7147e91dc58ef9ef4d11bb7bad5ead0c76eb6c01327a73c641d1dcc3" +url = "https://github.com/golangci/golangci-lint/releases/download/v2.14.0/golangci-lint-2.14.0-darwin-arm64.tar.gz" +url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/585813591" provenance = "github-attestations" [tools."github:golangci/golangci-lint"."platforms.macos-x64"] -checksum = "sha256:f93bda1f2cc981fd1326464020494be62f387bbf262706e1b3b644e5afacc440" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.3/golangci-lint-2.11.3-darwin-amd64.tar.gz" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/370749038" +checksum = "sha256:a5667c1c3536be1740133213e1e822bfb8f0d98ea12903174d6d5f635e4ed68d" +url = "https://github.com/golangci/golangci-lint/releases/download/v2.14.0/golangci-lint-2.14.0-darwin-amd64.tar.gz" +url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/585813755" provenance = "github-attestations" [tools."github:golangci/golangci-lint"."platforms.windows-x64"] -checksum = "sha256:cd42e890176bc5cfeb36225a77e66b9410ddd3a59a03551e23f6b210d29e1f67" -url = "https://github.com/golangci/golangci-lint/releases/download/v2.11.3/golangci-lint-2.11.3-windows-amd64.zip" -url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/370749053" +checksum = "sha256:b15f903d1649283a2ab6e8d2f0c317eaf1f9f6b3763950e7e8b7e72a43dd724f" +url = "https://github.com/golangci/golangci-lint/releases/download/v2.14.0/golangci-lint-2.14.0-windows-amd64.zip" +url_api = "https://api.github.com/repos/golangci/golangci-lint/releases/assets/585813793" provenance = "github-attestations" [[tools."github:goreleaser/goreleaser"]] -version = "2.15.3" +version = "2.18.2" backend = "github:goreleaser/goreleaser" [tools."github:goreleaser/goreleaser"."platforms.linux-arm64"] -checksum = "sha256:646b8f36329cf1ec02af18d40e7096973f62524bdef19c3690414e390a9f757d" -url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.3/goreleaser_Linux_arm64.tar.gz" -url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/396601624" +checksum = "sha256:a71681b29194f08f057a68cfcaa5c6b15d907a83a2622c51900c4faff828f322" +url = "https://github.com/goreleaser/goreleaser/releases/download/v2.18.2/goreleaser_Linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/569382435" provenance = "github-attestations" [tools."github:goreleaser/goreleaser"."platforms.linux-arm64-musl"] -checksum = "sha256:646b8f36329cf1ec02af18d40e7096973f62524bdef19c3690414e390a9f757d" -url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.3/goreleaser_Linux_arm64.tar.gz" -url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/396601624" +checksum = "sha256:a71681b29194f08f057a68cfcaa5c6b15d907a83a2622c51900c4faff828f322" +url = "https://github.com/goreleaser/goreleaser/releases/download/v2.18.2/goreleaser_Linux_arm64.tar.gz" +url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/569382435" provenance = "github-attestations" [tools."github:goreleaser/goreleaser"."platforms.linux-x64"] -checksum = "sha256:3b24b3a1629be21a9527d2f46f08b9bbf012c52fe33395714fe2c70acee57e0f" -url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.3/goreleaser_Linux_x86_64.tar.gz" -url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/396601623" +checksum = "sha256:0a96edc9d9bc594e4a41cc4d59467c182062910ab24d9d1f6dd7b667d32606d3" +url = "https://github.com/goreleaser/goreleaser/releases/download/v2.18.2/goreleaser_Linux_x86_64.tar.gz" +url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/569382334" provenance = "github-attestations" [tools."github:goreleaser/goreleaser"."platforms.linux-x64-musl"] -checksum = "sha256:3b24b3a1629be21a9527d2f46f08b9bbf012c52fe33395714fe2c70acee57e0f" -url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.3/goreleaser_Linux_x86_64.tar.gz" -url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/396601623" +checksum = "sha256:0a96edc9d9bc594e4a41cc4d59467c182062910ab24d9d1f6dd7b667d32606d3" +url = "https://github.com/goreleaser/goreleaser/releases/download/v2.18.2/goreleaser_Linux_x86_64.tar.gz" +url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/569382334" provenance = "github-attestations" [tools."github:goreleaser/goreleaser"."platforms.macos-arm64"] -checksum = "sha256:a46a3bf44ef8255b78d9eae9a3f4da60b878011e8eba7cf12bb3787ad5110418" -url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.3/goreleaser_Darwin_arm64.tar.gz" -url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/396601611" +checksum = "sha256:a811ff154fe136a0cfb55d00126c151fc39ec370a663d805a9ca5547445aa70c" +url = "https://github.com/goreleaser/goreleaser/releases/download/v2.18.2/goreleaser_Darwin_arm64.tar.gz" +url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/569382464" provenance = "github-attestations" [tools."github:goreleaser/goreleaser"."platforms.macos-x64"] -checksum = "sha256:723ba2f0ad18ec037c9bdd79d787f8a870a66f432706b3b3962b44b3a90def91" -url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.3/goreleaser_Darwin_x86_64.tar.gz" -url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/396601587" +checksum = "sha256:5e97d6517f73a0b6f71675a2911b15d3136c03c8907650c2b18e114b1c0f5205" +url = "https://github.com/goreleaser/goreleaser/releases/download/v2.18.2/goreleaser_Darwin_x86_64.tar.gz" +url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/569382333" provenance = "github-attestations" [tools."github:goreleaser/goreleaser"."platforms.windows-x64"] -checksum = "sha256:8722fdebfc23e10a36356ddd59af0c1f52c4120ca847afd091bd34e09d8aae8c" -url = "https://github.com/goreleaser/goreleaser/releases/download/v2.15.3/goreleaser_Windows_x86_64.zip" -url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/396601610" +checksum = "sha256:de61a8e7a064abb14210b16c942facd299643d83341c5cd293f19155a7c8b95f" +url = "https://github.com/goreleaser/goreleaser/releases/download/v2.18.2/goreleaser_Windows_x86_64.zip" +url_api = "https://api.github.com/repos/goreleaser/goreleaser/releases/assets/569382470" provenance = "github-attestations" [[tools.go]] -version = "1.26.6" +version = "1.27.1" backend = "core:go" [tools.go."platforms.linux-arm64"] -checksum = "sha256:d0507e9e9d7fe012aae570108cbd76c15de879e17130ab8cb90d4d7445cb1f2e" -url = "https://dl.google.com/go/go1.26.6.linux-arm64.tar.gz" +checksum = "sha256:3450b45a3f9ee8568792736a5c5e70a1f2e9b36c35a8f74958c03e51d7d92bec" +url = "https://dl.google.com/go/go1.27.1.linux-arm64.tar.gz" [tools.go."platforms.linux-arm64-musl"] -checksum = "sha256:d0507e9e9d7fe012aae570108cbd76c15de879e17130ab8cb90d4d7445cb1f2e" -url = "https://dl.google.com/go/go1.26.6.linux-arm64.tar.gz" +checksum = "sha256:3450b45a3f9ee8568792736a5c5e70a1f2e9b36c35a8f74958c03e51d7d92bec" +url = "https://dl.google.com/go/go1.27.1.linux-arm64.tar.gz" [tools.go."platforms.linux-x64"] -checksum = "sha256:708effb774be8237570d0add163225abbdfaf4fca28b2611df167beba4feef89" -url = "https://dl.google.com/go/go1.26.6.linux-amd64.tar.gz" +checksum = "sha256:63d339f0da5ab53635a56f2490a7984dfe12dfcff22ad749f63edaf590168445" +url = "https://dl.google.com/go/go1.27.1.linux-amd64.tar.gz" [tools.go."platforms.linux-x64-musl"] -checksum = "sha256:708effb774be8237570d0add163225abbdfaf4fca28b2611df167beba4feef89" -url = "https://dl.google.com/go/go1.26.6.linux-amd64.tar.gz" +checksum = "sha256:63d339f0da5ab53635a56f2490a7984dfe12dfcff22ad749f63edaf590168445" +url = "https://dl.google.com/go/go1.27.1.linux-amd64.tar.gz" [tools.go."platforms.macos-arm64"] -checksum = "sha256:2dc95ce4675829f2df0e86b28bcef3283635902062a5f0580ca659bf570f3204" -url = "https://dl.google.com/go/go1.26.6.darwin-arm64.tar.gz" +checksum = "sha256:ee215d57e0ec269c60cc9ceca68e6bda321ba9ee5afe24f4b0988703c2d87d12" +url = "https://dl.google.com/go/go1.27.1.darwin-arm64.tar.gz" [tools.go."platforms.macos-x64"] -checksum = "sha256:08b65a63f244115121ced6c3b55ad38d801a7442acad5c949a17aad84ae6d684" -url = "https://dl.google.com/go/go1.26.6.darwin-amd64.tar.gz" +checksum = "sha256:8f8f52c6649542cf027bbc9b9c68d1ec042f9f34808a40413f0b8b3f66f3caa4" +url = "https://dl.google.com/go/go1.27.1.darwin-amd64.tar.gz" [tools.go."platforms.windows-x64"] -checksum = "sha256:5b6c5b556525810463b5c897b50dc7a82d6a3dc0bfaf55d990a7e9f31d6b2318" -url = "https://dl.google.com/go/go1.26.6.windows-amd64.zip" +checksum = "sha256:a3911b5e0e1b1053f25ed0675f4c1c6aad1e2bfcf253df2b9be4caabd2edd95d" +url = "https://dl.google.com/go/go1.27.1.windows-amd64.zip" diff --git a/mise.toml b/mise.toml index b48c7ca..972158b 100644 --- a/mise.toml +++ b/mise.toml @@ -1,7 +1,7 @@ [tools] -go = "1.26.6" -"github:golangci/golangci-lint" = "2.11.3" -"github:goreleaser/goreleaser" = "v2.15.3" +go = "1.27.1" +"github:golangci/golangci-lint" = "2.14.0" +"github:goreleaser/goreleaser" = "v2.18.2" [settings] lockfile = true