refactor: improve RunPod client
- Refactor RunPod client to use generics for requests and responses. - This improves code readability and maintainability. - Remove redundant code for building GraphQL queries and handling HTTP requests. - Add support for environment variables in pod creation. - Update example with new API key and environment variables. Co-authored-by: supermario <mariobassem12@gmail.com>
This commit is contained in:
@@ -27,22 +27,6 @@ struct GqlQuery {
|
||||
query string
|
||||
}
|
||||
|
||||
struct GqlInput {
|
||||
cloud_type string @[json: 'cloudType']
|
||||
gpu_count int @[json: 'gpuCount']
|
||||
volume_in_gb int @[json: 'volumeInGb']
|
||||
container_disk_in_gb int @[json: 'containerDiskInGb']
|
||||
min_vcpu_count int @[json: 'minVcpuCount']
|
||||
min_memory_in_gb int @[json: 'minMemoryInGb']
|
||||
gpu_type_id string @[json: 'gpuTypeId']
|
||||
name string
|
||||
image_name string @[json: 'imageName']
|
||||
docker_args string @[json: 'dockerArgs']
|
||||
ports string
|
||||
volume_mount_path string @[json: 'volumeMountPath']
|
||||
env []map[string]string
|
||||
}
|
||||
|
||||
// GraphQL response wrapper
|
||||
struct GqlResponse {
|
||||
data GqlResponseData
|
||||
@@ -52,105 +36,11 @@ struct GqlResponseData {
|
||||
pod_find_and_deploy_on_demand PodFindAndDeployOnDemandResponse @[json: 'podFindAndDeployOnDemand']
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) get_response_fields[T](response_fields_str_ string, struct_ T) string {
|
||||
mut response_fields_str := response_fields_str_
|
||||
|
||||
// Start the current level
|
||||
response_fields_str += '{'
|
||||
|
||||
$for field in struct_.fields {
|
||||
$if field.is_struct {
|
||||
// Recursively process nested structs
|
||||
response_fields_str += '${field.name}'
|
||||
response_fields_str += ' '
|
||||
response_fields_str += rp.get_response_fields('', struct_.$(field.name))
|
||||
} $else {
|
||||
// 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(':')
|
||||
if attrs.len == 2 && attrs[0] == 'json' {
|
||||
response_fields_str += '${attrs[1]}'
|
||||
break
|
||||
}
|
||||
}
|
||||
} else {
|
||||
response_fields_str += '${field.name}'
|
||||
}
|
||||
}
|
||||
response_fields_str += ' '
|
||||
}
|
||||
// End the current level
|
||||
response_fields_str = response_fields_str.trim_space()
|
||||
response_fields_str += '}'
|
||||
return response_fields_str
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) build_query(request PodFindAndDeployOnDemandRequest, response PodFindAndDeployOnDemandResponse) string {
|
||||
// Convert input to JSON
|
||||
input_json := json.encode(request)
|
||||
|
||||
// Build the GraphQL mutation string
|
||||
response_fields_str := ''
|
||||
mut response_fields := rp.get_response_fields(response_fields_str, response)
|
||||
|
||||
// Wrap the query correctly
|
||||
query := 'mutation { podFindAndDeployOnDemand(input: ${input_json}) ${response_fields} }'
|
||||
|
||||
// Wrap in the final structure
|
||||
gql := GqlQuery{
|
||||
query: query
|
||||
}
|
||||
|
||||
// Return the final GraphQL query as a JSON string
|
||||
return json.encode(gql)
|
||||
}
|
||||
|
||||
enum HTTPMethod {
|
||||
get
|
||||
post
|
||||
put
|
||||
delete
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) make_request[T](method HTTPMethod, path string, data string) !T {
|
||||
mut request := httpconnection.Request{
|
||||
prefix: path
|
||||
data: data
|
||||
debug: true
|
||||
dataformat: .json
|
||||
}
|
||||
|
||||
mut http := rp.httpclient()!
|
||||
mut response := T{}
|
||||
|
||||
match method {
|
||||
.get {
|
||||
request.method = .get
|
||||
response = http.get_json_generic[T](request)!
|
||||
}
|
||||
.post {
|
||||
request.method = .post
|
||||
response = http.post_json_generic[T](request)!
|
||||
}
|
||||
.put {
|
||||
request.method = .put
|
||||
response = http.put_json_generic[T](request)!
|
||||
}
|
||||
.delete {
|
||||
request.method = .delete
|
||||
response = http.delete_json_generic[T](request)!
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) create_pod_request(request PodFindAndDeployOnDemandRequest) !PodFindAndDeployOnDemandResponse {
|
||||
response_type := PodFindAndDeployOnDemandResponse{}
|
||||
gql := rp.build_query(request, response_type)
|
||||
fn (mut rp RunPod) create_pod_request[T, R](request T, response R) !R {
|
||||
gql := rp.build_query[T, R](request, response)
|
||||
println('gql: ${gql}')
|
||||
response := rp.make_request[GqlResponse](.post, '/graphql', gql)!
|
||||
println('response: ${json.encode(response)}')
|
||||
return response_type
|
||||
response_ := rp.make_request[GqlResponse](.post, '/graphql', gql)!
|
||||
println('response: ${json.encode(response_)}')
|
||||
return response
|
||||
// return response.data.pod_find_and_deploy_on_demand
|
||||
}
|
||||
|
||||
@@ -23,11 +23,31 @@ pub mut:
|
||||
base_url string = 'https://api.runpod.io/v1'
|
||||
}
|
||||
|
||||
enum CloudType {
|
||||
all
|
||||
secure
|
||||
community
|
||||
}
|
||||
|
||||
fn (ct CloudType) to_string() string {
|
||||
return match ct {
|
||||
.all {
|
||||
'ALL'
|
||||
}
|
||||
.secure {
|
||||
'SECURE'
|
||||
}
|
||||
.community {
|
||||
'COMMUNITY'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Input structure for the mutation
|
||||
@[params]
|
||||
pub struct PodFindAndDeployOnDemandRequest {
|
||||
pub mut:
|
||||
cloud_type string = 'ALL' @[json: 'cloudType']
|
||||
cloud_type CloudType = .all @[json: 'cloudType']
|
||||
gpu_count int = 1 @[json: 'gpuCount']
|
||||
volume_in_gb int = 40 @[json: 'volumeInGb']
|
||||
container_disk_in_gb int = 40 @[json: 'containerDiskInGb']
|
||||
@@ -69,7 +89,10 @@ pub fn new(api_key string) !&RunPod {
|
||||
|
||||
// create_endpoint creates a new endpoint
|
||||
pub fn (mut rp RunPod) create_pod(pod PodFindAndDeployOnDemandRequest) !PodFindAndDeployOnDemandResponse {
|
||||
response := rp.create_pod_request(pod)!
|
||||
response_type := PodFindAndDeployOnDemandResponse{}
|
||||
request_type := pod
|
||||
response := rp.create_pod_request[PodFindAndDeployOnDemandRequest, PodFindAndDeployOnDemandResponse](request_type,
|
||||
response_type)!
|
||||
return response
|
||||
}
|
||||
|
||||
|
||||
144
lib/clients/runpod/utils.v
Normal file
144
lib/clients/runpod/utils.v
Normal file
@@ -0,0 +1,144 @@
|
||||
module runpod
|
||||
|
||||
import freeflowuniverse.herolib.core.httpconnection
|
||||
import json
|
||||
|
||||
fn (mut rp RunPod) 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(':')
|
||||
if attrs.len == 2 && attrs[0] == 'json' {
|
||||
field_name = attrs[1].trim_space()
|
||||
continue
|
||||
}
|
||||
}
|
||||
} else {
|
||||
field_name = field.name
|
||||
}
|
||||
return field_name
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) get_request_fields[T](struct_ T) string {
|
||||
// Start the current level
|
||||
mut body_ := '{ '
|
||||
mut fields := []string{}
|
||||
|
||||
$for field in T.fields {
|
||||
mut string_ := ''
|
||||
string_ += rp.get_field_name(field)
|
||||
string_ += ': '
|
||||
|
||||
$if field.is_enum {
|
||||
string_ += struct_.$(field.name).to_string()
|
||||
}
|
||||
|
||||
$if field.typ is string {
|
||||
item := struct_.$(field.name)
|
||||
string_ += "\"${item}\""
|
||||
}
|
||||
|
||||
$if field.typ is int {
|
||||
item := struct_.$(field.name)
|
||||
string_ += '${item}'
|
||||
}
|
||||
|
||||
// TODO: Loop only on the env map
|
||||
$if field.is_array {
|
||||
for i in struct_.$(field.name) {
|
||||
for k, v in i {
|
||||
string_ += '[{ '
|
||||
string_ += "key: \"${k}\", value: \"${v}\""
|
||||
string_ += ' }]'
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$if field.is_struct {
|
||||
rp.get_request_fields(struct_.$(field.name))
|
||||
}
|
||||
|
||||
fields << string_
|
||||
}
|
||||
body_ += fields.join(', ')
|
||||
body_ += ' }'
|
||||
return body_
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) get_response_fields[R](struct_ R) string {
|
||||
// Start the current level
|
||||
mut body_ := '{ '
|
||||
|
||||
$for field in R.fields {
|
||||
$if field.is_struct {
|
||||
// Recursively process nested structs
|
||||
body_ += '${field.name} '
|
||||
body_ += rp.get_response_fields(struct_.$(field.name))
|
||||
} $else {
|
||||
body_ += rp.get_field_name(field)
|
||||
body_ += ' '
|
||||
}
|
||||
}
|
||||
body_ += ' }'
|
||||
return body_
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) build_query[T, R](request T, response R) string {
|
||||
// Convert input to JSON
|
||||
// input_json := json.encode(request)
|
||||
|
||||
// Build the GraphQL mutation string
|
||||
mut request_fields := rp.get_request_fields(request)
|
||||
mut response_fields := rp.get_response_fields(response)
|
||||
|
||||
// Wrap the query correctly
|
||||
query := 'mutation { podFindAndDeployOnDemand(input: ${request_fields}) ${response_fields} }'
|
||||
|
||||
// Wrap in the final structure
|
||||
gql := GqlQuery{
|
||||
query: query
|
||||
}
|
||||
|
||||
// Return the final GraphQL query as a JSON string
|
||||
return json.encode(gql)
|
||||
}
|
||||
|
||||
enum HTTPMethod {
|
||||
get
|
||||
post
|
||||
put
|
||||
delete
|
||||
}
|
||||
|
||||
fn (mut rp RunPod) make_request[T](method HTTPMethod, path string, data string) !T {
|
||||
mut request := httpconnection.Request{
|
||||
prefix: path
|
||||
data: data
|
||||
debug: true
|
||||
dataformat: .json
|
||||
}
|
||||
|
||||
mut http := rp.httpclient()!
|
||||
mut response := T{}
|
||||
|
||||
match method {
|
||||
.get {
|
||||
request.method = .get
|
||||
response = http.get_json_generic[T](request)!
|
||||
}
|
||||
.post {
|
||||
request.method = .post
|
||||
response = http.post_json_generic[T](request)!
|
||||
}
|
||||
.put {
|
||||
request.method = .put
|
||||
response = http.put_json_generic[T](request)!
|
||||
}
|
||||
.delete {
|
||||
request.method = .delete
|
||||
response = http.delete_json_generic[T](request)!
|
||||
}
|
||||
}
|
||||
return response
|
||||
}
|
||||
@@ -9,6 +9,7 @@ pub fn (mut h HTTPConnection) get_json_generic[T](req Request) !T {
|
||||
|
||||
pub fn (mut h HTTPConnection) post_json_generic[T](req Request) !T {
|
||||
data := h.post_json_str(req)!
|
||||
println('data: ${data}')
|
||||
return json.decode(T, data) or { return error("couldn't decode json for ${req} for ${data}") }
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user