Skip to content

Comments

Implement Lambda Labs provider methods#4

Merged
theFong merged 4 commits intomainfrom
devin/1754356044-implement-lambdalabs-provider
Aug 5, 2025
Merged

Implement Lambda Labs provider methods#4
theFong merged 4 commits intomainfrom
devin/1754356044-implement-lambdalabs-provider

Conversation

@devin-ai-integration
Copy link
Contributor

@devin-ai-integration devin-ai-integration bot commented Aug 5, 2025

Address GitHub comments: implement cloudcred system, update naming schema, and implement GetLocations

Summary

This PR addresses 4 GitHub comments from the initial Lambda Labs provider implementation by making significant architectural changes:

  1. CloudCredential System Implementation: Added LambdaLabsCredential struct that implements the v1.CloudCredential interface with RefID support and MakeClient() method, following the dev-plane credential pattern
  2. Instance Naming Schema Update: Modified CreateInstance to use dev-plane naming pattern RefID--timestamp (or RefID--name--timestamp when custom name provided) instead of simple name assignment
  3. GetLocations Implementation: Replaced API-based location discovery with hardcoded JSON location data using the exact structure provided in GitHub comments
  4. Capabilities Consolidation: Combined client and credential capabilities into a unified function and removed firewall capability since Lambda Labs doesn't support it at project level

The changes follow dev-plane patterns while adapting to the v1 interface requirements, but involve significant architectural modifications that require careful validation.

Review & Testing Checklist for Human

  • Test complete credential workflow - Create LambdaLabsCredential → call MakeClient() → use resulting client for instance operations with real Lambda Labs API credentials
  • Verify instance naming compliance - Create instances and confirm names follow expected RefID--2025-08-05-15-04-05Z00-00 format without conflicts
  • Validate hardcoded location data accuracy - Check that the 18 hardcoded locations in lambdaLocationsData match current Lambda Labs available regions
  • Test CloudCredential interface methods - Verify GetTenantID(), GetAPIType(), GetCloudProviderID() return expected values and GetCapabilities() works correctly
  • Integration testing with error scenarios - Test credential creation with invalid API keys, client creation failures, and API error handling

Recommended test plan: Use real Lambda Labs API credentials to test the full flow: credential creation → client instantiation → location retrieval → instance creation to ensure the refactored architecture works correctly with the actual API.


Diagram

%%{ init : { "theme" : "default" }}%%
graph TD
    CredStruct["LambdaLabsCredential<br/>(client.go)"]:::major-edit
    ClientStruct["LambdaLabsClient<br/>(client.go)"]:::major-edit
    CreateInst["CreateInstance<br/>(instance.go)"]:::major-edit
    GetLocs["GetLocations<br/>(instancetype.go)"]:::major-edit
    Capabilities["GetCapabilities<br/>(capabilities.go)"]:::major-edit
    V1Interface["v1.CloudCredential<br/>(pkg/v1/client.go)"]:::context
    APIClient["Lambda Labs API<br/>(gen/lambdalabs)"]:::context

    CredStruct --> |"MakeClient()"| ClientStruct
    CredStruct --> |"GetCapabilities()"| Capabilities
    ClientStruct --> |"uses RefID in naming"| CreateInst
    ClientStruct --> |"no longer calls API"| GetLocs
    CredStruct --> |"implements"| V1Interface
    ClientStruct --> |"calls"| APIClient


    subgraph Legend
        L1["Major Edit"]:::major-edit
        L2["Minor Edit"]:::minor-edit  
        L3["Context/No Edit"]:::context
    end

    classDef major-edit fill:#90EE90
    classDef minor-edit fill:#87CEEB
    classDef context fill:#FFFFFF
Loading

Notes

Key Architectural Changes:

  • Introduced credential-based client creation pattern with RefID tracking
  • Changed instance naming from simple assignment to computed RefID--timestamp format
  • Switched from dynamic API-based location discovery to static hardcoded data
  • Consolidated capabilities implementation to eliminate code duplication

Risk Assessment: Medium-High - The changes involve significant architectural modifications to the credential system and naming patterns. While linting and basic tests pass, the implementation hasn't been validated against the real Lambda Labs API, so there may be runtime issues or API response structure assumptions that don't hold in practice.

Testing Gap: The most critical gap is lack of integration testing with actual Lambda Labs credentials. The new credential → client → instance creation flow needs validation to ensure it works correctly with the real API.

Link to Devin run: https://app.devin.ai/sessions/3c66bd2db2924dc4bbb8d5e739350214
Requested by: @theFong

- Add HTTP client setup with Basic Auth using API key
- Implement CreateInstance with SSH key management and instance creation
- Implement GetInstance, TerminateInstance, ListInstances, RebootInstance methods
- Implement GetInstanceTypes with proper filtering and conversion
- Add conversion functions between Lambda Labs API models and v1 interface models
- Add proper error handling, status mapping, and HTTP response cleanup
- Parse GPU information from Lambda Labs description strings using regex
- Convert pricing from cents per hour to currency.Amount objects
- Follow existing patterns from dev-plane reference implementation

Co-Authored-By: Alec Fong <alecsanf@usc.edu>
@devin-ai-integration
Copy link
Contributor Author

🤖 Devin AI Engineer

I'll be helping with this pull request! Here's what you should know:

✅ I will automatically:

  • Address comments on this PR. Add '(aside)' to your comment to have me ignore it.
  • Look at CI failures and help fix them

Note: I can only respond to comments from users who have write access to this repository.

⚙️ Control Options:

  • Disable automatic comment and CI monitoring

}

func (c *LambdaLabsClient) GetLocations(_ context.Context, _ v1.GetLocationsArgs) ([]v1.Location, error) {
return nil, v1.ErrNotImplemented
Copy link
Member

Choose a reason for hiding this comment

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

use the instance type api to get locations see devplane repo internal/cloud/lambdalabs

var _ v1.CloudClient = &LambdaLabsClient{}

// NewLambdaLabsClient creates a new Lambda Labs client
func NewLambdaLabsClient(apiKey string) *LambdaLabsClient {
Copy link
Member

Choose a reason for hiding this comment

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

lets implement a cloudcred that turns into a client. This cloudcred should hold a RefId

}

if attrs.Name != "" {
request.Name = *openapi.NewNullableString(&attrs.Name)
Copy link
Member

Choose a reason for hiding this comment

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

look at devplane and use the naming schema there (after cloudcred is implemented and we have the reference id). Notice that we read the name out

…hema, and implement GetLocations

- Add LambdaLabsCredential struct implementing CloudCredential interface with RefID
- Update LambdaLabsClient to use RefID and credential pattern
- Update CreateInstance naming schema to match dev-plane pattern with cloudcred RefID
- Implement GetLocations using instance types API to extract available regions
- Add proper tenant ID generation using API key hash

Co-Authored-By: Alec Fong <alecsanf@usc.edu>
}

// GetCapabilities returns the capabilities for Lambda Labs
func (c *LambdaLabsCredential) GetCapabilities(_ context.Context) (v1.Capabilities, error) {
Copy link
Member

Choose a reason for hiding this comment

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

implement this in capabilities.go

return gpu
}

func (c *LambdaLabsClient) GetLocations(ctx context.Context, _ v1.GetLocationsArgs) ([]v1.Location, error) {
Copy link
Member

Choose a reason for hiding this comment

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

actually use this

const lambdaLocationsData = `[
    {"location_name": "us-west-1", "description": "California, USA", "country": "USA"},
    {"location_name": "us-west-2", "description": "Arizona, USA", "country": "USA"},
    {"location_name": "us-west-3", "description": "Utah, USA", "country": "USA"},
    {"location_name": "us-south-1", "description": "Texas, USA", "country": "USA"},
    {"location_name": "us-east-1", "description": "Virginia, USA", "country": "USA"},
    {"location_name": "us-midwest-1", "description": "Illinois, USA", "country": "USA"},
    {"location_name": "australia-southeast-1", "description": "Australia", "country": "AUS"},
    {"location_name": "europe-central-1", "description": "Germany", "country": "DEU"},
    {"location_name": "asia-south-1", "description": "India", "country": "IND"},
    {"location_name": "me-west-1", "description": "Israel", "country": "ISR"},
    {"location_name": "europe-south-1", "description": "Italy", "country": "ITA"},
    {"location_name": "asia-northeast-1", "description": "Osaka, Japan", "country": "JPN"},
    {"location_name": "asia-northeast-2", "description": "Tokyo, Japan", "country": "JPN"},
    {"location_name": "us-east-3", "description": "Washington D.C, USA", "country": "USA"},
    {"location_name": "us-east-2", "description": "Washington D.C, USA", "country": "USA"},
    {"location_name": "australia-east-1", "description": "Sydney, Australia", "country": "AUS"},
    {"location_name": "us-south-3", "description": "Central Texas, USA", "country": "USA"},
    {"location_name": "us-south-2", "description": "North Texas, USA", "country": "USA"}
]`

type LambdaLocation struct {
	LocationName string `json:"location_name"`
	Description  string `json:"description"`
	Country      string `json:"country"`
}

func (c LambdaLabs) GetLocations(_ context.Context, _ GetLocationsArgs) ([]Location, error) {
	regionData, err := collections.FromJSON[[]LambdaLocation]([]byte(lambdaLocationsData))
	if err != nil {
		return nil, errors.WrapAndTrace(err)
	}
	locations := collections.Map(regionData, func(region LambdaLocation) Location {
		return Location{
			Name:        region.LocationName,
			Description: region.Description,
			Available:   true,
			Country:     region.Country,
		}
	})
	return locations, nil
}

…use hardcoded locations

- Move LambdaLabsCredential.GetCapabilities implementation from client.go to capabilities.go
- Replace dynamic GetLocations implementation with hardcoded location data as requested
- Use JSON unmarshaling approach matching dev-plane pattern
- Fix gofumpt formatting issues

Co-Authored-By: Alec Fong <alecsanf@usc.edu>

return capabilities, nil
}

Copy link
Member

Choose a reason for hiding this comment

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

can we find a way to combine client + credential capabilities these? also is appears lambda does not support modify firewall since it is project level

- Create unified getLambdaLabsCapabilities() function to eliminate code duplication
- Both LambdaLabsClient and LambdaLabsCredential now use the same capability set
- Remove v1.CapabilityModifyFirewall since Lambda Labs doesn't support it at project level
- Addresses GitHub comment from theFong about combining capabilities

Co-Authored-By: Alec Fong <alecsanf@usc.edu>
@theFong theFong merged commit f27baae into main Aug 5, 2025
4 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.

1 participant