Skip to content

feat(search): add gpu-search command to find GPU instance types#280

Open
theFong wants to merge 1 commit intomainfrom
pr/gpu-search
Open

feat(search): add gpu-search command to find GPU instance types#280
theFong wants to merge 1 commit intomainfrom
pr/gpu-search

Conversation

@theFong
Copy link
Member

@theFong theFong commented Feb 2, 2026

Summary

Add brev search command (aliases: gpu-search, gpu, gpus) to search and filter available GPU instance types across cloud providers.

Features

  • Filter by GPU name, provider, VRAM, compute capability, disk size, boot time
  • Sort by price, vram, boot-time, gpu-count, etc.
  • Show instance features (stoppable, rebootable, flex-ports)
  • JSON output support for scripting
  • Pipeable output for chaining with other commands

Examples

# Search all GPUs sorted by price
brev search

# Filter by GPU name
brev search --gpu-name A100

# Filter by specs
brev search --min-vram 40 --max-boot-time 5 --sort price

# JSON output for scripting
brev search --json | jq '.[] | select(.price < 2)'

# Pipe to create
brev search --gpu-name A100 | brev create my-instance

Test plan

  • brev search shows available GPUs
  • Filters work correctly (--gpu-name, --min-vram, etc.)
  • Sort options work (--sort price, --sort vram)
  • JSON output is valid JSON
  • Piped output works with downstream commands

Add `brev search` command (aliases: gpu-search, gpu, gpus) to search and
filter available GPU instance types across cloud providers.

Features:
- Filter by GPU name, provider, VRAM, compute capability, disk, boot time
- Sort by price, vram, boot-time, etc.
- Show instance features (stoppable, rebootable, flex-ports)
- JSON output support for scripting
- Pipeable output for chaining with other commands

Examples:
  brev search --gpu-name A100 --sort price
  brev search --min-vram 40 --max-boot-time 5
  brev search --json | jq '.[] | select(.price < 2)'
@theFong
Copy link
Member Author

theFong commented Feb 3, 2026

Manual QA Results ✅

Build & Static Analysis:

  • ✅ Build passes
  • go vet passes
  • ✅ Unit tests pass (36.7% coverage on gpusearch package)

Test Plan Verification:

  • brev search shows available GPUs - Verified: Returns full table of GPU instances sorted by price
  • ✅ Filters work correctly - Verified: --gpu-name A100, --min-vram 40, --sort price all work as expected
  • ✅ Sort options work - Verified: Sorting by price shows cheapest first
  • ✅ JSON output is valid JSON - Verified: --json outputs valid JSON array
  • ✅ Piped output works with downstream commands - Verified: brev search | brev create successfully creates instance

Notes:

  • Comprehensive unit tests cover all filter and sort functionality
  • Pre-existing test failures on main (Darwin platform tests, Docker daemon) are unrelated to this PR

return true // j unknown goes after i
}
less = instances[i].BootTime < instances[j].BootTime
default:
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

just a note that we silently fail if the sortBy string isn't valid (e.g. typo)

ta.SetOutputMirror(os.Stdout)
ta.Style().Options = getBrevTableOptions()

header := table.Row{"TYPE", "TARGET_DISK", "PROVIDER", "GPU", "COUNT", "VRAM/GPU", "TOTAL_VRAM", "CAPABILITY", "DISK", "$/GB/MO", "BOOT", "FEATURES", "VCPUs", "$/HR"}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

could extract a common

 func formatInstanceRow(inst GPUInstanceInfo, colored bool, t *terminal.Terminal) table.Row {
      // Common formatting logic
  }

or something

your comment about colors doesn't seem to apply? I don't see where colors are being set.

// parseToGB converts size/memory strings like "22GiB360MiB", "16TiB", "2TiB768GiB" to GB
func parseToGB(s string) float64 {
var totalGB float64
re := regexp.MustCompile(`(\d+(?:\.\d+)?)\s*(TiB|TB|GiB|GB|MiB|MB)`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

this only needs to be compiled once not every time it's called

var totalSeconds int

// Match hours
hRe := regexp.MustCompile(`(\d+)h`)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

same with these don't need to compile every time

if s.PricePerGBHr.Amount != "" {
pricePerHr, _ := strconv.ParseFloat(s.PricePerGBHr.Amount, 64)
if pricePerHr > 0 {
pricePerGBMonth = pricePerHr * 730
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

magic number: make 730 a val called "hoursInMonth" ?

)

const (
instanceTypesAPIURL = "https://api.brev.dev"
Copy link
Contributor

@patelspratik patelspratik Feb 4, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

move this to pkg/config/config.go as "PUBLIC REST API", though I think we want to deprecate this in favor of the Connect/GRPC APIs directly. Unless this is an alias I don't know about.

I think there is a different https://brevapi2.us-west-2-prod.control-plane.brev.dev/api/public-instances endpoint that doesn't require Auth that @stephahart put in that the UI uses.


// fetchInstanceTypes fetches instance types from the public Brev API
func fetchInstanceTypes() (*gpusearch.InstanceTypesResponse, error) {
client := resty.New()
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

why not use NewRestyClient?

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants