Skip to content

Implement RunPod cloud adapter#748

Merged
ralyodio merged 6 commits into
profullstack:masterfrom
caydyan:codex/implement-runpod-cloud-adapter
Jun 14, 2026
Merged

Implement RunPod cloud adapter#748
ralyodio merged 6 commits into
profullstack:masterfrom
caydyan:codex/implement-runpod-cloud-adapter

Conversation

@caydyan

@caydyan caydyan commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Summary

Replaces the RunPod cloud stub with a GraphQL-backed GPU pod adapter:

  • verifies credentials with myself
  • quotes GPU pods from explicit hourlyPrice config or RunPod gpuTypes pricing
  • provisions on-demand pods through podFindAndDeployOnDemand
  • lists account pods via myself { pods }
  • checks a single pod via pod(input: ...)
  • terminates pods with podTerminate
  • maps RunPod pod state and public runtime port IPs into sh1pt Instance values
  • documents required config, including imageName for real provisioning

RunPod reference used:

Safety

Real provisioning requires config.imageName. GPU pods still honor spec.maxHourlyPrice before any mutation is sent. Dry-run provisioning returns a local instance and does not call RunPod.

Validation

  • corepack pnpm --filter @profullstack/sh1pt-core build
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod typecheck
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod build
  • corepack pnpm vitest run packages/cloud/runpod/src/index.test.ts
  • git diff --check

@greptile-apps

greptile-apps Bot commented Jun 14, 2026

Copy link
Copy Markdown
Contributor

Greptile Summary

This PR replaces the RunPod cloud stub with a complete GraphQL-backed GPU pod adapter. It correctly addresses every concern raised in prior review rounds: null guards on myself and podFindAndDeployOnDemand, dry-run path that avoids network calls, conservative on-demand pricing for maxHourlyPrice guardrails, volumeInGb opt-in via optionalPositiveNumber, and gpuTypes ?? [] null coalescing.

  • Core operations: connect verifies credentials via myself, quote fetches gpuTypes pricing or uses an explicit hourlyPrice, provision calls podFindAndDeployOnDemand with a full null guard, list/status/destroy map cleanly to RunPod's GraphQL API.
  • Price guardrail: priceForGpu uses Math.max(communityPrice, securePrice) for cloudType: ALL, so the guardrail is always checked against the highest possible on-demand rate, not the cheaper community estimate.
  • Test coverage: 15 focused unit tests covering every code path including null API responses, dry-run, maxHourlyPrice enforcement, and spot pricing, plus the contractTestCloud suite.

Confidence Score: 5/5

Safe to merge; every previously identified issue has been addressed with tests to confirm.

Every concern from the previous review rounds has been resolved: null guards on myself and podFindAndDeployOnDemand, dry-run path that skips all network calls when no hourlyPrice is set, conservative pricing with Math.max for the ALL cloud type, volumeInGb now opt-in via optionalPositiveNumber, and data.gpuTypes ?? [] preventing a null dereference. No new defects were found in this pass.

No files require special attention; all changed files look correct.

Important Files Changed

Filename Overview
packages/cloud/runpod/src/index.ts Full GraphQL adapter implementation replacing the stub; all previously identified null-safety, dry-run, pricing, and provisioning issues are addressed. No new defects found.
packages/cloud/runpod/src/index.test.ts Comprehensive unit test suite covering all adapter methods, null RunPod responses, dry-run, maxHourlyPrice guardrail, and spot pricing behavior; all previously identified edge cases have dedicated tests.
packages/cloud/runpod/README.md Updated from stub placeholder to accurate documentation of all config fields, pricing behavior, and provisioning requirements.

Sequence Diagram

sequenceDiagram
    participant C as sh1pt CLI
    participant A as RunPod Adapter
    participant R as RunPod GraphQL API

    C->>A: connect(ctx, config)
    A->>R: "query Myself { myself { id email } }"
    R-->>A: "{ myself: { id, email } }"
    A-->>C: "{ accountId }"

    C->>A: quote(ctx, spec, config)
    alt hourlyPrice in config
        A-->>C: Quote (no network call)
    else
        A->>R: "query GpuTypes(input: { id: gpuTypeId })"
        R-->>A: "{ gpuTypes: [...] }"
        A-->>C: Quote (on-demand price x count)
    end

    C->>A: provision(ctx, spec, config)
    alt dryRun and no hourlyPrice
        A-->>C: synthetic dry-run Instance (no network call)
    else
        A->>A: quote() check maxHourlyPrice
        A->>R: "mutation DeployPod(input: {...})"
        R-->>A: "{ podFindAndDeployOnDemand: pod | null }"
        alt pod is null
            A-->>C: throw pod was not provisioned
        else
            A-->>C: Instance (mapped from pod)
        end
    end

    C->>A: list(ctx, config)
    A->>R: "query Pods { myself { pods { ... } } }"
    R-->>A: "{ myself: { pods: [...] } }"
    A-->>C: Instance[]

    C->>A: status(ctx, instanceId, config)
    A->>R: "query Pod(input: { podId })"
    R-->>A: "{ pod: pod | null }"
    A-->>C: Instance

    C->>A: destroy(ctx, instanceId, config)
    alt dryRun
        A-->>C: log only
    else
        A->>R: "mutation TerminatePod(input: { podId })"
        R-->>A: "{ podTerminate: null }"
        A-->>C: void
    end
Loading

Reviews (5): Last reviewed commit: "Guard empty RunPod provision response" | Re-trigger Greptile

Comment thread packages/cloud/runpod/src/index.ts
Comment thread packages/cloud/runpod/src/index.ts Outdated
Comment thread packages/cloud/runpod/src/index.ts
Comment thread packages/cloud/runpod/src/index.ts Outdated
@caydyan

caydyan commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the Greptile feedback in 061285b0:

  • Guard myself in connect()/list() and return a clear RunPod account-permission error instead of a TypeError.
  • Keep dry-run provisioning offline when hourlyPrice is omitted; it now returns a local zero-cost preview without calling RunPod.
  • Quote cloudType: ALL with the highest available community/secure price so maxHourlyPrice is not checked against a lower community-only estimate.

Validation after the fix:

  • corepack pnpm --filter @profullstack/sh1pt-core build
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod typecheck
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod build
  • corepack pnpm vitest run packages/cloud/runpod/src/index.test.ts (19 tests)
  • git diff --check

Comment thread packages/cloud/runpod/src/index.ts Outdated
@caydyan

caydyan commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Follow-up pushed in 01d61381 to cover the remaining storage-cost concern from the Greptile inline review:

  • volumeInGb is now omitted unless explicitly requested through config or spec.storage, so RunPod will not attach default network storage on ordinary pod launches.
  • Added coverage that real provisioning without storage does not send volumeInGb.
  • README now calls out that volumeInGb is opt-in network volume storage.

Validation after this follow-up:

  • corepack pnpm --filter @profullstack/sh1pt-core build
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod typecheck
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod build
  • corepack pnpm vitest run packages/cloud/runpod/src/index.test.ts (20 tests)
  • git diff --check

@caydyan

caydyan commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the latest Greptile follow-up in 2013f9a7:

  • quoteFromApi() now treats missing/null gpuTypes as an empty result and surfaces the existing clear RunPod GPU type not found error.
  • selectGpuType() no longer silently falls back to the first returned GPU when the requested model does not match.
  • Added tests for gpuTypes: null and non-matching GPU result sets.
  • README notes that unknown GPU types fail clearly instead of selecting another GPU.

Validation after this fix:

  • corepack pnpm --filter @profullstack/sh1pt-core build
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod typecheck
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod build
  • corepack pnpm vitest run packages/cloud/runpod/src/index.test.ts (22 tests)
  • git diff --check

Comment thread packages/cloud/runpod/src/index.ts Outdated
@caydyan

caydyan commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the spot-pricing guardrail issue in 0477848e:

  • RunPod quotes now use on-demand GPU prices for guardrails because this adapter does not submit spot bidPerGpu values.
  • Quote.spot is now false for RunPod quotes unless/until the adapter actually provisions spot bids.
  • Added coverage that maxHourlyPrice blocks against on-demand price even when spec.spotOk is true.
  • README documents that spot bids are not sent by this adapter.

Validation after this fix:

  • corepack pnpm --filter @profullstack/sh1pt-core build
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod typecheck
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod build
  • corepack pnpm vitest run packages/cloud/runpod/src/index.test.ts (23 tests)
  • git diff --check

Comment thread packages/cloud/runpod/src/index.ts
@caydyan

caydyan commented Jun 14, 2026

Copy link
Copy Markdown
Contributor Author

Addressed the latest Greptile finding in 5465626d:

  • provision() now guards a null/empty podFindAndDeployOnDemand mutation result and returns a clear capacity/provisioning error instead of letting podInstance() throw a TypeError.
  • Added regression coverage for a null RunPod provision response.

Validation after this fix:

  • corepack pnpm --filter @profullstack/sh1pt-core build
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod typecheck
  • corepack pnpm --filter @profullstack/sh1pt-cloud-runpod build
  • corepack pnpm vitest run packages/cloud/runpod/src/index.test.ts (24 tests)
  • git diff --check

@ralyodio ralyodio merged commit 5138990 into profullstack:master Jun 14, 2026
5 checks passed
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