Skip to content

Add get-capacity: GPU capacity matrix across host CUDA versions - #71

Merged
lukepiette merged 3 commits into
mainfrom
add-get-capacity-tool
Jul 31, 2026
Merged

Add get-capacity: GPU capacity matrix across host CUDA versions#71
lukepiette merged 3 commits into
mainfrom
add-get-capacity-tool

Conversation

@lukepiette

Copy link
Copy Markdown
Contributor

What

New get-capacity tool: GPU capacity across host CUDA versions, as one matrix call.

Agents picking an endpoint's allowedCudaVersions/minCudaVersion (or diagnosing a capacity-starved endpoint) previously had to reverse-engineer per-version stock by calling the GraphQL gpuTypes.lowestPrice query once per CUDA version — an undocumented idiom nobody discovers organically. This came directly out of a real session (runpod-workers/worker-comfyui#231): choosing that Hub listing's CUDA allowlist took a hand-rolled curl fan-out that this tool replaces.

Matrix mode (default): one credential-free query via lowestPrice.gpuTypeCudaVersions — per GPU type, overall stock + AVAILABLE/UNAVAILABLE per host-reported CUDA version.

Probe mode (cudaVersions: ["12.8", "13.0"], max 12): one stock lookup per version (parallel), returning graded stock (High/Medium/Low/Out) and the lowest on-demand price per version.

Example probe output (live data — this is the worker-comfyui allowlist question as one call):

NVIDIA GeForce RTX 5090   {12.8: Low, 12.9: Low, 13.0: High}
NVIDIA GeForce RTX 4090   {12.8: Low, 12.9: Out, 13.0: Low}

Design notes

  • Public GraphQL on both API versions — the v2 REST catalog has no CUDA dimension, so this never branches on backendFor (spec-parity allowlisted under ALLOWLIST_PENDING_REST; revisit if the REST catalog gains one).
  • Nothing hidden by default (matches the list-gpu-types philosophy): no-stock versions are explicit Out cells, includeUnavailable: false to hide — for a capacity-diagnosis tool, the empty cells are the point.
  • Best-effort fan-out (matches get-job-status precedent): Promise.allSettled; a transiently failing probe reports a per-version probeErrors entry instead of failing the call.
  • Injection-safe inlining: the public GraphQL path takes no variables, so every inlined value is runtime-revalidated (regex-gated versions, int-clamped gpuCount, literal secureCloud) — direct handler calls that bypass zod cannot reach the query string.

Validation

  • Full suite: 512 pass / 0 fail (goldens for both modes' wire queries, injection guard with zero-outbound assertion, gpuCount coercion, version dedupe, probe-failure survival, includeUnavailable, pagination).
  • Two independent adversarial review passes (static + live behavioral): live cross-checks match raw GraphQL cell-for-cell; default response ~1.9k tokens; 12-version fan-out ~330 ms median, no rate limiting at 36 rapid requests; built dist/stdio.mjs serves the tool over a real stdio MCP session on v1 and v2.
  • Known follow-up (pre-existing, not introduced here): the shared graphqlRequest helper never checks response.ok, so an HTTP-level 429/5xx surfaces as an opaque JSON-parse error; with allSettled that lands in probeErrors rather than failing the call, but the helper deserves the same rateLimitHint treatment the REST client got.

🤖 Generated with Claude Code

lukepiette and others added 2 commits July 30, 2026 18:48
One credential-free catalog call answers 'which GPUs have stock on which
host CUDA versions' — previously an undocumented per-version fan-out over
gpuTypes.lowestPrice that agents had to reverse-engineer. Default mode
returns the full AVAILABLE/UNAVAILABLE matrix via gpuTypeCudaVersions;
passing cudaVersions deep-probes those versions for graded stock
(High/Medium/Low) and per-version pricing. Values inlined into the
GraphQL query (the public path takes no variables) are runtime-validated
against injection since direct handler calls bypass zod.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
- Probe mode: Promise.allSettled — a transient failure on one version
  reports a per-version probeErrors entry instead of discarding the
  other results (matches get-job-status's best-effort fan-out stance)
- Nothing hidden by default: probe mode now emits explicit Out cells
  and keeps no-stock GPUs (includeUnavailable:false to hide), matching
  the list-gpu-types catalog philosophy
- Blank/non-string gpuTypeIds entries ignored instead of matching all
- memoryGb tertiary sort tie-break (matches list-gpu-types); .min(1)
  on cudaVersions; goldens for gpuCount coercion, dedupe, probe
  failure, includeUnavailable, matrix secureCloudOnly
- Doc drift: RUNPOD_PUBLIC_GRAPHQL_URL consumer list and architecture
  overview now include get-capacity (and the other GraphQL consumers)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@vercel

vercel Bot commented Jul 30, 2026

Copy link
Copy Markdown

The latest updates on your projects. Learn more about Vercel for GitHub.

Project Deployment Actions Updated (UTC)
runpod-mcp Ready Ready Preview Jul 30, 2026 11:23pm

Request Review

lukepiette added a commit that referenced this pull request Jul 30, 2026
- Array.isArray guard on the parsed errors field: proxies/WAFs emit
  {"errors":"..."} where a non-empty string passes a .length check and
  .map() then throws a TypeError worse than the parse error this change
  eliminates; item messages are stringified defensively too
- 401/429 with a GraphQL-shaped body now fall through to HttpError so
  the re-auth and RateLimit hints are never suppressed
- HttpError caps embedded bodies at 2KB in the message (.body keeps the
  full text) so a large WAF/proxy error page can't flood agent context
- Test harness steps can carry response headers; goldens added for the
  RateLimit-header hint content, non-array errors, 401 precedence, and
  body truncation
- Changeset no longer names get-capacity (lives on unmerged #71)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
The vendored v2 spec (and the live API) support minCudaVersion on
/catalog/gpus availability — 'no CUDA dimension' was wrong. What remains
GraphQL-only, and why this tool doesn't branch on backendFor: the exact
per-version breakdown (gpuTypeCudaVersions), per-version graded stock
(allowedCudaVersions probes), and per-version pricing.

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
@lukepiette

Copy link
Copy Markdown
Contributor Author

Correction from external review: the claim "the v2 REST catalog has no CUDA dimension" was wrong — the v2 spec supports a minCudaVersion floor filter on GET /catalog/gpus?include=AVAILABILITY (it's even in this repo's vendored spec, tests/fixtures/v2-openapi.yaml:282). The code comment, spec-parity allowlist entry, and changeset now state the accurate boundary: what stays GraphQL-only is the exact per-version breakdown (gpuTypeCudaVersions), per-version graded stock (allowedCudaVersions probes), and per-version pricing — a floor filter can't produce a version matrix, so the tool's backend choice is unchanged. No functional changes in this commit.

🤖 Generated with Claude Code

@lukepiette
lukepiette merged commit 04c90da into main Jul 31, 2026
5 checks passed
lukepiette added a commit that referenced this pull request Jul 31, 2026
- Array.isArray guard on the parsed errors field: proxies/WAFs emit
  {"errors":"..."} where a non-empty string passes a .length check and
  .map() then throws a TypeError worse than the parse error this change
  eliminates; item messages are stringified defensively too
- 401/429 with a GraphQL-shaped body now fall through to HttpError so
  the re-auth and RateLimit hints are never suppressed
- HttpError caps embedded bodies at 2KB in the message (.body keeps the
  full text) so a large WAF/proxy error page can't flood agent context
- Test harness steps can carry response headers; goldens added for the
  RateLimit-header hint content, non-array errors, 401 precedence, and
  body truncation
- Changeset no longer names get-capacity (lives on unmerged #71)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
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