/*
* Hanzo Cloud API
*
* The Hanzo Cloud API as a customer calls it: every operation under /v1/ except the operator's admin product, relay routes, legacy spellings and capabilities still reached by flag. Tagged by product: the first path segment after /v1/.
*
* The version of the OpenAPI document: v1
*
* Generated by: https://openapi-generator.tech
*/
use reqwest;
use serde::{Deserialize, Serialize, de::Error as _};
use crate::{apis::ResponseContent, models};
use super::{Error, configuration, ContentType};
/// struct for typed errors of method [`delete_ml_models_by_name`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum DeleteMlModelsByNameError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ml_health`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetMlHealthError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ml_models`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetMlModelsError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`get_ml_models_by_name`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum GetMlModelsByNameError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`patch_ml_models_by_name`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PatchMlModelsByNameError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_ml_models`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostMlModelsError {
UnknownValue(serde_json::Value),
}
/// struct for typed errors of method [`post_ml_models_by_name_predict`]
#[derive(Debug, Clone, Serialize, Deserialize)]
#[serde(untagged)]
pub enum PostMlModelsByNamePredictError {
UnknownValue(serde_json::Value),
}
/// Deletes a deployed inference model. kserve owns the teardown: the InferenceService goes away and the serving deployment behind it follows, so the model stops answering predict calls. Answers 204, or 404 for a name the caller's org does not own.
pub async fn delete_ml_models_by_name(configuration: &configuration::Configuration, name: &str) -> Result<(), Error<DeleteMlModelsByNameError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_name = name;
let uri_str = format!("{}/v1/ml/models/{name}", configuration.base_path, name=crate::apis::urlencode(p_name));
let mut req_builder = configuration.client.request(reqwest::Method::DELETE, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<DeleteMlModelsByNameError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Reports whether the model-serving plane is genuinely usable: that the Kubernetes API answers, that the InferenceService CRD is actually served by this cluster, and that the cluster holds at least one serving runtime to run a model ON. It is a REAL probe, not status theatre — it makes a live call rather than reporting a flag set at boot. 200 only when everything checks out. Otherwise 503 CARRYING THE REPORT — which component failed, and the real error — and that body is the reason this is not a typed op: a typed op reaches a non-2xx by returning an error, and the envelope that produces would drop exactly the detail the probe exists to deliver. The runtime count is reported as its own field and is a SEPARATE fact from the CRD being served: a cluster with the CRD but no runtime accepts a deploy and then never schedules it, so reporting only the CRD would answer 200 while every model hangs. A runtime list this service cannot read reports the read error instead of a count, because a missing grant is a broken probe and not an empty cluster. It answers about the cluster, not about a tenant, so it takes no org and reveals no tenant data. A cluster with no kserve CRD reports degraded honestly rather than failing later at the first deploy.
pub async fn get_ml_health(configuration: &configuration::Configuration, ) -> Result<(), Error<GetMlHealthError>> {
let uri_str = format!("{}/v1/ml/health", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<GetMlHealthError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Lists the inference models deployed in the caller's org. Each entry carries the model's name, when Kubernetes admitted it, and kserve's live status — the spec is on the single-model read. An org that has deployed nothing gets an empty list.
pub async fn get_ml_models(configuration: &configuration::Configuration, ) -> Result<models::MlResourceList, Error<GetMlModelsError>> {
let uri_str = format!("{}/v1/ml/models", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::MlResourceList`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::MlResourceList`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetMlModelsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Returns one deployed inference model. Its spec comes with it, and kserve's live status, which is where readiness and the serving address appear. A name the caller's org does not own answers 404, exactly as an unknown name does, so a probe learns nothing about another tenant's models.
pub async fn get_ml_models_by_name(configuration: &configuration::Configuration, name: &str) -> Result<models::MlResource, Error<GetMlModelsByNameError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_name = name;
let uri_str = format!("{}/v1/ml/models/{name}", configuration.base_path, name=crate::apis::urlencode(p_name));
let mut req_builder = configuration.client.request(reqwest::Method::GET, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::MlResource`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::MlResource`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<GetMlModelsByNameError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Applies a JSON merge patch to one of the caller org's deployed models and answers the updated resource — the way to change a model's image, replica count or resource requests without tearing the deployment down. The body is relayed to Kubernetes VERBATIM. That is deliberate and it is why this route is not a typed op: re-encoding a merge patch changes what it means, because an integer that round-trips through a generic decoder comes back a float. Merge-patch semantics apply as written — a null removes a field, and a list is replaced whole rather than merged. Scoped to the caller's own tenant namespace, resolved from the validated org and project; a name the caller's tenant does not hold is a 404, never another tenant's resource. An empty body is refused, and a patch Kubernetes rejects comes back 422 with its reason rather than being silently dropped.
pub async fn patch_ml_models_by_name(configuration: &configuration::Configuration, name: &str) -> Result<(), Error<PatchMlModelsByNameError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_name = name;
let uri_str = format!("{}/v1/ml/models/{name}", configuration.base_path, name=crate::apis::urlencode(p_name));
let mut req_builder = configuration.client.request(reqwest::Method::PATCH, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<PatchMlModelsByNameError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Deploys one inference model for the caller's org, and answers 201 with the model as Kubernetes admitted it. The `spec` is a kserve InferenceService spec, passed through unchanged — this plane owns the tenancy, the billing and the namespace, and kserve owns what a model IS. An unfunded org is refused BEFORE anything is created, so nobody runs free GPU compute and nobody is charged for a resource that was never made.
pub async fn post_ml_models(configuration: &configuration::Configuration, ml_create: models::MlCreate) -> Result<models::MlResource, Error<PostMlModelsError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_ml_create = ml_create;
let uri_str = format!("{}/v1/ml/models", configuration.base_path);
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
req_builder = req_builder.json(&p_ml_create);
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
let content_type = resp
.headers()
.get("content-type")
.and_then(|v| v.to_str().ok())
.unwrap_or("application/octet-stream");
let content_type = super::ContentType::from(content_type);
if !status.is_client_error() && !status.is_server_error() {
let content = resp.text().await?;
match content_type {
ContentType::Json => serde_json::from_str(&content).map_err(Error::from),
ContentType::Text => return Err(Error::from(serde_json::Error::custom("Received `text/plain` content type response that cannot be converted to `models::MlResource`"))),
ContentType::Unsupported(unknown_type) => return Err(Error::from(serde_json::Error::custom(format!("Received `{unknown_type}` content type response that cannot be converted to `models::MlResource`")))),
}
} else {
let content = resp.text().await?;
let entity: Option<PostMlModelsError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}
/// Sends the request body to the named model's predictor and answers the predictor's reply — its status code, its body bytes and its Content-Type, all unchanged. This is the inference call itself, not a description of one. VERBATIM IS THE CONTRACT, and it is why this route is not a typed op: a model-side error has to surface as the model's own error, not as this layer's paraphrase of it. The body shape is the kserve v2 inference protocol's, which means the runtime decides it, not this API. The v2 model name defaults to the resource name — kserve's single-model convention — and a multi-model runtime selects one with the `model` query parameter. A model that exists but has no serving address yet answers 503 'not ready' rather than a confusing connection error: deployed is not the same as serving. Scoped to the caller's own tenant namespace from the validated org and project, so a name another tenant owns is simply a 404. The predictor's response body is read up to a fixed ceiling.
pub async fn post_ml_models_by_name_predict(configuration: &configuration::Configuration, name: &str) -> Result<(), Error<PostMlModelsByNamePredictError>> {
// add a prefix to parameters to efficiently prevent name collisions
let p_name = name;
let uri_str = format!("{}/v1/ml/models/{name}/predict", configuration.base_path, name=crate::apis::urlencode(p_name));
let mut req_builder = configuration.client.request(reqwest::Method::POST, &uri_str);
if let Some(ref user_agent) = configuration.user_agent {
req_builder = req_builder.header(reqwest::header::USER_AGENT, user_agent.clone());
}
if let Some(ref token) = configuration.bearer_access_token {
req_builder = req_builder.bearer_auth(token.to_owned());
};
let req = req_builder.build()?;
let resp = configuration.client.execute(req).await?;
let status = resp.status();
if !status.is_client_error() && !status.is_server_error() {
Ok(())
} else {
let content = resp.text().await?;
let entity: Option<PostMlModelsByNamePredictError> = serde_json::from_str(&content).ok();
Err(Error::ResponseError(ResponseContent { status, content, entity }))
}
}