feat: Add spot pod start and improved error handling
- Added functionality to start spot pods using the RunPod API. - Improved error handling and clarity in the RunPod client. - Added more detailed comments to the code for better readability. - Refactored the HTTP client and utils to improve modularity. - Updated example to demonstrate spot pod creation and starting. Co-authored-by: mariobassem12 <mariobassem12@gmail.com>
This commit is contained in:
@@ -33,7 +33,7 @@ on_demand_pod_response := rp.create_on_demand_pod(
|
||||
println('Created pod with ID: ${on_demand_pod_response.id}')
|
||||
|
||||
// create a spot pod
|
||||
spot_pod_resp := rp.create_spot_pod(
|
||||
spot_pod_response := rp.create_spot_pod(
|
||||
port: 1826
|
||||
bid_per_gpu: 0.2
|
||||
cloud_type: .secure
|
||||
@@ -55,8 +55,16 @@ spot_pod_resp := rp.create_spot_pod(
|
||||
},
|
||||
]
|
||||
)!
|
||||
println('Created spot pod with ID: ${spot_pod_resp.id}')
|
||||
println('Created spot pod with ID: ${spot_pod_response.id}')
|
||||
|
||||
// start on-demand pod
|
||||
start_on_demand_pod := rp.start_on_demand_pod(pod_id: '${on_demand_pod_response.id}', gpu_count: 1)!
|
||||
println('Started pod with ID: ${start_on_demand_pod.id}')
|
||||
println('Started on demand pod with ID: ${start_on_demand_pod.id}')
|
||||
|
||||
// start spot pod
|
||||
start_spot_pod := rp.start_spot_pod(
|
||||
pod_id: '${spot_pod_response.id}'
|
||||
gpu_count: 1
|
||||
bid_per_gpu: 0.2
|
||||
)!
|
||||
println('Started spot pod with ID: ${start_on_demand_pod.id}')
|
||||
|
||||
@@ -84,11 +84,17 @@ pub fn (mut rp RunPod) create_spot_pod(input PodRentInterruptableInput) !PodResu
|
||||
@[params]
|
||||
pub struct PodResume {
|
||||
pub mut:
|
||||
pod_id string @[json: 'podId']
|
||||
gpu_count int @[json: 'gpuCount']
|
||||
pod_id string @[json: 'podId']
|
||||
gpu_count int @[json: 'gpuCount']
|
||||
bid_per_gpu f32 @[json: 'bidPerGpu']
|
||||
}
|
||||
|
||||
// Start On-Demand Pod
|
||||
pub fn (mut rp RunPod) start_on_demand_pod(input PodResume) !PodResult {
|
||||
return rp.start_on_demand_pod_request(input)!
|
||||
}
|
||||
|
||||
// Start Spot Pod
|
||||
pub fn (mut rp RunPod) start_spot_pod(input PodResume) !PodResult {
|
||||
return rp.start_spot_pod_request(input)!
|
||||
}
|
||||
|
||||
@@ -1,21 +1,10 @@
|
||||
module runpod
|
||||
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import json
|
||||
|
||||
fn (mut rp RunPod) httpclient() !&httpconnection.HTTPConnection {
|
||||
mut http_conn := httpconnection.new(
|
||||
name: 'runpod_${rp.name}'
|
||||
url: 'https://api.runpod.io'
|
||||
cache: true
|
||||
retry: 3
|
||||
)!
|
||||
|
||||
// Add authorization header
|
||||
http_conn.default_header.add(.authorization, 'Bearer ${rp.api_key}')
|
||||
return http_conn
|
||||
}
|
||||
|
||||
// #### Internally method doing a network call to create a new on-demand pod.
|
||||
// - Build the required query based pn the input sent by the user and send the request.
|
||||
// - Decode the response received from the API into two objects `Data` and `Error`.
|
||||
// - The data field should contains the pod details same as `PodResult` struct.
|
||||
// - The error field should contain the error message.
|
||||
fn (mut rp RunPod) create_pod_find_and_deploy_on_demand_request(request PodFindAndDeployOnDemandRequest) !PodResult {
|
||||
gql := build_query(
|
||||
query_type: .mutation
|
||||
@@ -29,6 +18,11 @@ fn (mut rp RunPod) create_pod_find_and_deploy_on_demand_request(request PodFindA
|
||||
}
|
||||
}
|
||||
|
||||
// #### Internally method doing a network call to create a new spot pod.
|
||||
// - Build the required query based pn the input sent by the user and send the request.
|
||||
// - Decode the response received from the API into two objects `Data` and `Error`.
|
||||
// - The data field should contains the pod details same as `PodResult` struct.
|
||||
// - The error field should contain the error message.
|
||||
fn (mut rp RunPod) create_create_spot_pod_request(input PodRentInterruptableInput) !PodResult {
|
||||
gql := build_query(
|
||||
query_type: .mutation
|
||||
@@ -42,7 +36,11 @@ fn (mut rp RunPod) create_create_spot_pod_request(input PodRentInterruptableInpu
|
||||
}
|
||||
}
|
||||
|
||||
// Start On-Demand Pod
|
||||
// #### Internally method doing a network call to start on demand pod.
|
||||
// - Build the required query based pn the input sent by the user and send the request.
|
||||
// - Decode the response received from the API into two objects `Data` and `Error`.
|
||||
// - The data field should contains the pod details same as `PodResult` struct.
|
||||
// - The error field should contain the error message.
|
||||
fn (mut rp RunPod) start_on_demand_pod_request(input PodResume) !PodResult {
|
||||
gql := build_query(
|
||||
query_type: .mutation
|
||||
@@ -55,3 +53,21 @@ fn (mut rp RunPod) start_on_demand_pod_request(input PodResume) !PodResult {
|
||||
return error('Could not find podRentInterruptable in response data: ${response_.data}')
|
||||
}
|
||||
}
|
||||
|
||||
// #### Internally method doing a network call to start spot pod.
|
||||
// - Build the required query based pn the input sent by the user and send the request.
|
||||
// - Decode the response received from the API into two objects `Data` and `Error`.
|
||||
// - The data field should contains the pod details same as `PodResult` struct.
|
||||
// - The error field should contain the error message.
|
||||
fn (mut rp RunPod) start_spot_pod_request(input PodResume) !PodResult {
|
||||
gql := build_query(
|
||||
query_type: .mutation
|
||||
method_name: 'podBidResume'
|
||||
request_model: input
|
||||
response_model: PodResult{}
|
||||
)
|
||||
response_ := rp.make_request[GqlResponse[PodResult]](.post, '/graphql', gql)!
|
||||
return response_.data['podBidResume'] or {
|
||||
return error('Could not find podRentInterruptable in response data: ${response_.data}')
|
||||
}
|
||||
}
|
||||
|
||||
@@ -3,9 +3,21 @@ module runpod
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import json
|
||||
|
||||
// Represents the main structure for interacting with the RunPod API.
|
||||
// Provides utilities to manage HTTP connections and perform GraphQL queries.
|
||||
fn (mut rp RunPod) httpclient() !&httpconnection.HTTPConnection {
|
||||
mut http_conn := httpconnection.new(
|
||||
name: 'runpod_vclient_${rp.name}'
|
||||
url: rp.base_url
|
||||
cache: true
|
||||
retry: 3
|
||||
)!
|
||||
return http_conn
|
||||
}
|
||||
|
||||
// Retrieves the field name from the `FieldData` struct, either from its attributes or its name.
|
||||
fn get_field_name(field FieldData) string {
|
||||
mut field_name := ''
|
||||
// Process attributes to fetch the JSON field name or fallback to field name
|
||||
if field.attrs.len > 0 {
|
||||
for attr in field.attrs {
|
||||
attrs := attr.trim_space().split(':')
|
||||
@@ -20,8 +32,8 @@ fn get_field_name(field FieldData) string {
|
||||
return field_name
|
||||
}
|
||||
|
||||
// Constructs JSON-like request fields from a struct.
|
||||
fn get_request_fields[T](struct_ T) string {
|
||||
// Start the current level
|
||||
mut body_ := '{ '
|
||||
mut fields := []string{}
|
||||
|
||||
@@ -62,8 +74,8 @@ fn get_request_fields[T](struct_ T) string {
|
||||
return body_
|
||||
}
|
||||
|
||||
// Constructs JSON-like response fields for a given struct.
|
||||
fn get_response_fields[R](struct_ R) string {
|
||||
// Start the current level
|
||||
mut body_ := '{ '
|
||||
|
||||
$for field in R.fields {
|
||||
@@ -80,11 +92,13 @@ fn get_response_fields[R](struct_ R) string {
|
||||
return body_
|
||||
}
|
||||
|
||||
// Enum representing the type of GraphQL operation.
|
||||
pub enum QueryType {
|
||||
query
|
||||
mutation
|
||||
}
|
||||
|
||||
// Struct for building GraphQL queries with request and response models.
|
||||
@[params]
|
||||
pub struct BuildQueryArgs[T, R] {
|
||||
pub:
|
||||
@@ -94,11 +108,8 @@ pub:
|
||||
response_model R @[required]
|
||||
}
|
||||
|
||||
// Builds a GraphQL query or mutation string from provided arguments.
|
||||
fn build_query[T, R](args BuildQueryArgs[T, R]) string {
|
||||
// Convert input to JSON
|
||||
// input_json := json.encode(request)
|
||||
|
||||
// Build the GraphQL mutation string
|
||||
mut request_fields := get_request_fields(args.request_model)
|
||||
mut response_fields := get_response_fields(args.response_model)
|
||||
|
||||
@@ -114,6 +125,7 @@ fn build_query[T, R](args BuildQueryArgs[T, R]) string {
|
||||
return json.encode(gql)
|
||||
}
|
||||
|
||||
// Converts the `QueryType` enum to its string representation.
|
||||
fn (q QueryType) to_string() string {
|
||||
return match q {
|
||||
.query {
|
||||
@@ -125,6 +137,7 @@ fn (q QueryType) to_string() string {
|
||||
}
|
||||
}
|
||||
|
||||
// Enum representing HTTP methods.
|
||||
enum HTTPMethod {
|
||||
get
|
||||
post
|
||||
@@ -132,6 +145,7 @@ enum HTTPMethod {
|
||||
delete
|
||||
}
|
||||
|
||||
// Sends an HTTP request to the RunPod API with the specified method, path, and data.
|
||||
fn (mut rp RunPod) make_request[T](method HTTPMethod, path string, data string) !T {
|
||||
mut request := httpconnection.Request{
|
||||
prefix: path
|
||||
|
||||
Reference in New Issue
Block a user