Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ authors = ["Tracel team"]
homepage = "https://burn.dev"
repository = "https://github.com/tracel-ai/burn-central-client"
license = "MIT OR Apache-2.0"
rust-version = "1.97.0"

[workspace.dependencies]
reqwest = { version = "0.13.4", features = ["blocking", "form"] }
Expand Down
1 change: 1 addition & 0 deletions tracel-client/Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@
name = "tracel-client"
version.workspace = true
edition.workspace = true
rust-version.workspace = true
authors.workspace = true
description = "Collection of HTTP calls required to communicate with the Tracel backend"
homepage.workspace = true
Expand Down
94 changes: 85 additions & 9 deletions tracel-client/src/console/job/mod.rs
Original file line number Diff line number Diff line change
@@ -1,28 +1,104 @@
pub mod response;

mod body;

use crate::{
ClientError,
console::{Client, job::body::ComputeProviderQueueJobRequest},
console::{
Client,
job::{
body::ComputeProviderQueueJobRequest,
response::{JobLogResponse, JobResponse, ProjectJobsResponse, QueuedJobResponse},
},
},
};

impl Client {
pub fn start_remote_job(
/// Queue a job that runs the code version `digest` on a compute provider group.
///
/// `command` holds the program's arguments, which the compute provider splits into
/// words with shell quoting rules.
///
/// The client must be logged in before calling this method.
pub fn queue_job(
&self,
compute_provider_group_name: &str,
owner_name: &str,
namespace: &str,
project_name: &str,
compute_provider_group_name: &str,
digest: &str,
command: &str,
) -> Result<(), ClientError> {
let path: &str = &format!("projects/{owner_name}/{project_name}/jobs/queue");
let url = self.transport.join(path);

) -> Result<QueuedJobResponse, ClientError> {
let body = ComputeProviderQueueJobRequest {
compute_provider_group_name: compute_provider_group_name.to_string(),
digest: digest.to_string(),
command: command.to_string(),
};

self.transport.post(url, Some(body))
self.transport.post_json(
format!("projects/{namespace}/{project_name}/jobs/queue"),
Some(body),
)
}

/// List the jobs of a project.
///
/// The client must be logged in before calling this method.
pub fn list_jobs(
&self,
namespace: &str,
project_name: &str,
) -> Result<ProjectJobsResponse, ClientError> {
self.transport
.get_json(format!("projects/{namespace}/{project_name}/jobs"))
}

/// Get a job by its project-scoped number.
///
/// The client must be logged in before calling this method.
pub fn get_job(
&self,
namespace: &str,
project_name: &str,
job_num: i32,
) -> Result<JobResponse, ClientError> {
self.transport.get_json(format!(
"projects/{namespace}/{project_name}/jobs/{job_num}"
))
}

/// Read a page of a job's log file from byte offset `start`, or from the beginning.
///
/// The client must be logged in before calling this method.
pub fn get_job_logs(
&self,
namespace: &str,
project_name: &str,
job_num: i32,
start: Option<u64>,
) -> Result<JobLogResponse, ClientError> {
let mut url = self.transport.join(&format!(
"projects/{namespace}/{project_name}/jobs/{job_num}/logs"
));
if let Some(start) = start {
url.query_pairs_mut()
.append_pair("start", &start.to_string());
}

self.transport.get_json(url)
}

/// Cancel a new, queued or running job.
///
/// The client must be logged in before calling this method.
pub fn cancel_job(
&self,
namespace: &str,
project_name: &str,
job_num: i32,
) -> Result<(), ClientError> {
self.transport.put(
format!("projects/{namespace}/{project_name}/jobs/{job_num}/cancel"),
None::<()>,
)
}
}
87 changes: 87 additions & 0 deletions tracel-client/src/console/job/response.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
use serde::{Deserialize, Serialize};

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct QueuedJobResponse {
pub job_id: String,
pub compute_provider_group_id: String,
/// The project-scoped job number, or `None` from a server that does not send it.
#[serde(default)]
pub job_num: Option<i32>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ProjectJobsResponse {
pub jobs: Vec<ProjectJobResponse>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct ProjectJobResponse {
pub job_num: i32,
pub job_project_version: String,
pub job_command: String,
/// `new`, `queued`, `running`, `pending_cancellation`, `completed`, `failed` or `cancelled`.
pub status: String,
pub execution_context: Option<ExecutionContextResponse>,
pub created_at: String,
pub started_at: Option<String>,
pub completed_at: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JobResponse {
pub job_num: i32,
pub compute_provider_id: Option<i32>,
pub job_project: JobProjectResponse,
pub job_project_version: String,
pub job_command: String,
/// `new`, `queued`, `running`, `pending_cancellation`, `completed`, `failed` or `cancelled`.
pub status: String,
pub status_message: String,
pub execution_context: Option<ExecutionContextResponse>,
pub created_at: String,
pub started_at: Option<String>,
pub completed_at: Option<String>,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JobProjectResponse {
pub project_name: String,
pub namespace_name: String,
}

#[derive(Serialize, Deserialize, Clone, Debug)]
#[serde(tag = "type")]
pub enum ExecutionContextResponse {
Managed {
compute_provider_name: String,
memory_gb: i32,
cpu_cores: i32,
num_gpus: Option<i32>,
lock_price_per_hour: MoneyResponse,
final_cost: Option<MoneyResponse>,
estimated_cost: Option<MoneyResponse>,
},
SelfManaged {
compute_provider_name: String,
},
}

#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct MoneyResponse {
/// A decimal amount.
pub amount: String,
pub currency: String,
}

/// One page of a job's log file.
#[derive(Serialize, Deserialize, Clone, Debug)]
pub struct JobLogResponse {
pub logs: String,
/// Byte offset of the first byte in `logs`.
pub start: u64,
/// Byte offset after the last byte in `logs`; pass it as the next `start`.
pub end: u64,
/// Size of the log file in bytes.
pub total_size: u64,
pub has_more: bool,
}
2 changes: 2 additions & 0 deletions tracel-client/src/error.rs
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,8 @@ pub enum ApiErrorCode {
ModelVersionUploadIncomplete,
MultipartUploadIncomplete,
ModelVersionConflict,
ComputeProviderJob,
JobLog,
Unauthenticated,
CredentialNotAllowed,
// ...
Expand Down
7 changes: 7 additions & 0 deletions tracel-client/src/transport.rs
Original file line number Diff line number Diff line change
Expand Up @@ -236,6 +236,13 @@ impl ApiTransport {
Ok(serde_json::from_slice::<R>(&bytes)?)
}

pub fn put<T>(&self, path: impl AsRef<str>, body: Option<T>) -> Result<(), ClientError>
where
T: serde::Serialize,
{
self.req(reqwest::Method::PUT, path, body).map(|_| ())
}

pub fn delete(&self, path: impl AsRef<str>) -> Result<(), ClientError> {
self.req(reqwest::Method::DELETE, path, None::<serde_json::Value>)
.map(|_| ())
Expand Down
Loading