diff --git a/Cargo.toml b/Cargo.toml index 3ea64f0..98cdff7 100644 --- a/Cargo.toml +++ b/Cargo.toml @@ -1,6 +1,6 @@ [package] name = "openai-api-rs" -version = "0.1.15" +version = "1.0.0" edition = "2021" authors = ["Dongri Jin "] license = "MIT" @@ -9,8 +9,16 @@ repository = "https://github.com/dongri/openai-api-rs" # See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html -[dependencies] -reqwest = { version = "0.11", features = ["json"] } -tokio = { version = "1", features = ["full"] } -serde = { version = "1", features = ["derive"] } -serde_json = "1.0.97" +[dependencies.serde] +version = "1" +features = ["derive"] +default-features = false + +[dependencies.serde_json] +version = "1" +default-features = false + +[dependencies.minreq] +version = "2" +default-features = false +features = ["https-rustls", "json-using-serde"] \ No newline at end of file diff --git a/README.md b/README.md index a8da280..0078d9a 100644 --- a/README.md +++ b/README.md @@ -7,7 +7,7 @@ Check out the [docs.rs](https://docs.rs/openai-api-rs/). Cargo.toml ```toml [dependencies] -openai-api-rs = "0.1" +openai-api-rs = "1.0" ``` ## Usage @@ -56,7 +56,7 @@ let req = ChatCompletionRequest { ### Send request ```rust -let result = client.completion(req).await?; +let result = client.completion(req)?; println!("{:?}", result.choices[0].text); ``` @@ -66,8 +66,7 @@ use openai_api_rs::v1::api::Client; use openai_api_rs::v1::chat_completion::{self, ChatCompletionRequest}; use std::env; -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); let req = ChatCompletionRequest { model: chat_completion::GPT4.to_string(), @@ -90,7 +89,7 @@ async fn main() -> Result<(), Box> { logit_bias: None, user: None, }; - let result = client.chat_completion(req).await?; + let result = client.chat_completion(req)?; println!("{:?}", result.choices[0].message.content); Ok(()) } diff --git a/examples/chat_completion.rs b/examples/chat_completion.rs index afa4997..ae64146 100644 --- a/examples/chat_completion.rs +++ b/examples/chat_completion.rs @@ -2,8 +2,7 @@ use openai_api_rs::v1::api::Client; use openai_api_rs::v1::chat_completion::{self, ChatCompletionRequest}; use std::env; -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); let req = ChatCompletionRequest { model: chat_completion::GPT4.to_string(), @@ -26,7 +25,7 @@ async fn main() -> Result<(), Box> { logit_bias: None, user: None, }; - let result = client.chat_completion(req).await?; + let result = client.chat_completion(req)?; println!("{:?}", result.choices[0].message.content); Ok(()) } diff --git a/examples/completion.rs b/examples/completion.rs index 653c326..0a62a3a 100644 --- a/examples/completion.rs +++ b/examples/completion.rs @@ -2,8 +2,7 @@ use openai_api_rs::v1::api::Client; use openai_api_rs::v1::completion::{self, CompletionRequest}; use std::env; -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); let req = CompletionRequest { model: completion::GPT3_TEXT_DAVINCI_003.to_string(), @@ -23,7 +22,7 @@ async fn main() -> Result<(), Box> { logit_bias: None, user: None, }; - let result = client.completion(req).await?; + let result = client.completion(req)?; println!("{:}", result.choices[0].text); Ok(()) diff --git a/examples/embedding.rs b/examples/embedding.rs index d5c53e4..7b72fc0 100644 --- a/examples/embedding.rs +++ b/examples/embedding.rs @@ -2,15 +2,14 @@ use openai_api_rs::v1::api::Client; use openai_api_rs::v1::embedding::EmbeddingRequest; use std::env; -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); let req = EmbeddingRequest { model: "text-embedding-ada-002".to_string(), input: "story time".to_string(), user: Option::None, }; - let result = client.embedding(req).await?; + let result = client.embedding(req)?; println!("{:?}", result.data); Ok(()) diff --git a/examples/function_call.rs b/examples/function_call.rs index 9882f89..a656de3 100644 --- a/examples/function_call.rs +++ b/examples/function_call.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::{env, vec}; -async fn get_coin_price(coin: &str) -> f64 { +fn get_coin_price(coin: &str) -> f64 { let coin = coin.to_lowercase(); match coin.as_str() { "btc" | "bitcoin" => 10000.0, @@ -13,8 +13,7 @@ async fn get_coin_price(coin: &str) -> f64 { } } -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); let mut properties = HashMap::new(); @@ -60,7 +59,7 @@ async fn main() -> Result<(), Box> { user: None, }; - let result = client.chat_completion(req).await?; + let result = client.chat_completion(req)?; match result.choices[0].finish_reason { chat_completion::FinishReason::stop => { @@ -82,7 +81,7 @@ async fn main() -> Result<(), Box> { let c: Currency = serde_json::from_str(&arguments)?; let coin = c.coin; if name == "get_coin_price" { - let price = get_coin_price(&coin).await; + let price = get_coin_price(&coin); println!("{} price: {}", coin, price); } } diff --git a/examples/function_call_role.rs b/examples/function_call_role.rs index 2df42ce..c011668 100644 --- a/examples/function_call_role.rs +++ b/examples/function_call_role.rs @@ -4,7 +4,7 @@ use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::{env, vec}; -async fn get_coin_price(coin: &str) -> f64 { +fn get_coin_price(coin: &str) -> f64 { let coin = coin.to_lowercase(); match coin.as_str() { "btc" | "bitcoin" => 10000.0, @@ -13,8 +13,7 @@ async fn get_coin_price(coin: &str) -> f64 { } } -#[tokio::main] -async fn main() -> Result<(), Box> { +fn main() -> Result<(), Box> { let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); let mut properties = HashMap::new(); @@ -60,7 +59,7 @@ async fn main() -> Result<(), Box> { user: None, }; - let result = client.chat_completion(req).await?; + let result = client.chat_completion(req)?; match result.choices[0].finish_reason { chat_completion::FinishReason::stop => { @@ -93,7 +92,7 @@ async fn main() -> Result<(), Box> { chat_completion::ChatCompletionMessage { role: chat_completion::MessageRole::function, content: { - let price = get_coin_price(&coin).await; + let price = get_coin_price(&coin); format!("{{\"price\": {}}}", price) }, name: Some(String::from("get_coin_price")), @@ -113,7 +112,7 @@ async fn main() -> Result<(), Box> { logit_bias: None, user: None, }; - let result = client.chat_completion(req).await?; + let result = client.chat_completion(req)?; println!("{:?}", result.choices[0].message.content); } chat_completion::FinishReason::content_filter => { diff --git a/src/v1/api.rs b/src/v1/api.rs index 6fa3643..44fc2f0 100644 --- a/src/v1/api.rs +++ b/src/v1/api.rs @@ -24,7 +24,7 @@ use crate::v1::image::{ }; use crate::v1::moderation::{CreateModerationRequest, CreateModerationResponse}; -use reqwest::Response; +use minreq::Response; const API_URL_V1: &str = "https://api.openai.com/v1"; @@ -57,31 +57,7 @@ impl Client { } } - pub async fn client_builder(&self) -> Result { - let mut headers = reqwest::header::HeaderMap::new(); - headers.insert( - reqwest::header::CONTENT_TYPE, - reqwest::header::HeaderValue::from_static("application/json"), - ); - headers.insert( - reqwest::header::AUTHORIZATION, - reqwest::header::HeaderValue::from_str(&("Bearer ".to_owned() + &self.api_key)) - .unwrap(), - ); - match &self.organization { - Some(organization) => headers.insert( - reqwest::header::HeaderName::from_static("openai-organization"), - reqwest::header::HeaderValue::from_str(organization).unwrap(), - ), - None => None, - }; - let client = reqwest::Client::builder() - .default_headers(headers) - .build()?; - Ok(client) - } - - pub async fn post( + pub fn post( &self, path: &str, params: &T, @@ -91,311 +67,322 @@ impl Client { api_endpoint = self.api_endpoint, path = path ); - let client = match self.client_builder().await { - Ok(c) => c, - Err(e) => return Err(self.new_error(e)), - }; - let res = client.post(&url).json(¶ms).send().await; + + let mut request = minreq::post(url) + .with_header("Content-Type", "application/json") + .with_header("Authorization", format!("Bearer {}", self.api_key)); + + if let Some(organization) = &self.organization { + request = request.with_header("openai-organization", organization); + } + + let res = request.with_json(params).unwrap().send(); + match res { - Ok(res) => match res.status().is_success() { - true => Ok(res), - false => Err(APIError { - message: format!("{}: {}", res.status(), res.text().await.unwrap()), - }), - }, + Ok(res) => { + if (200..=299).contains(&res.status_code) { + Ok(res) + } else { + Err(APIError { + message: format!("{}: {}", res.status_code, res.as_str().unwrap()), + }) + } + } Err(e) => Err(self.new_error(e)), } } - pub async fn get(&self, path: &str) -> Result { + pub fn get(&self, path: &str) -> Result { let url = format!( "{api_endpoint}{path}", api_endpoint = self.api_endpoint, path = path ); - let client = match self.client_builder().await { - Ok(c) => c, - Err(e) => return Err(self.new_error(e)), - }; - let res = client.get(&url).send().await; + + let mut request = minreq::get(url) + .with_header("Content-Type", "application/json") + .with_header("Authorization", format!("Bearer {}", self.api_key)); + + if let Some(organization) = &self.organization { + request = request.with_header("openai-organization", organization); + } + + let res = request.send(); + match res { - Ok(res) => match res.status().is_success() { - true => Ok(res), - false => Err(APIError { - message: format!("{}: {}", res.status(), res.text().await.unwrap()), - }), - }, + Ok(res) => { + if (200..=299).contains(&res.status_code) { + Ok(res) + } else { + Err(APIError { + message: format!("{}: {}", res.status_code, res.as_str().unwrap()), + }) + } + } Err(e) => Err(self.new_error(e)), } } - pub async fn delete(&self, path: &str) -> Result { + pub fn delete(&self, path: &str) -> Result { let url = format!( "{api_endpoint}{path}", api_endpoint = self.api_endpoint, path = path ); - let client = match self.client_builder().await { - Ok(c) => c, - Err(e) => return Err(self.new_error(e)), - }; - let res = client.delete(&url).send().await; + + let mut request = minreq::delete(url) + .with_header("Content-Type", "application/json") + .with_header("Authorization", format!("Bearer {}", self.api_key)); + + if let Some(organization) = &self.organization { + request = request.with_header("openai-organization", organization); + } + + let res = request.send(); + match res { - Ok(res) => match res.status().is_success() { - true => Ok(res), - false => Err(APIError { - message: format!("{}: {}", res.status(), res.text().await.unwrap()), - }), - }, + Ok(res) => { + if (200..=299).contains(&res.status_code) { + Ok(res) + } else { + Err(APIError { + message: format!("{}: {}", res.status_code, res.as_str().unwrap()), + }) + } + } Err(e) => Err(self.new_error(e)), } } - pub async fn completion(&self, req: CompletionRequest) -> Result { - let res = self.post("/completions", &req).await?; - let r = res.json::().await; + pub fn completion(&self, req: CompletionRequest) -> Result { + let res = self.post("/completions", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn edit(&self, req: EditRequest) -> Result { - let res = self.post("/edits", &req).await?; - let r = res.json::().await; + pub fn edit(&self, req: EditRequest) -> Result { + let res = self.post("/edits", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn image_generation( + pub fn image_generation( &self, req: ImageGenerationRequest, ) -> Result { - let res = self.post("/images/generations", &req).await?; - let r = res.json::().await; + let res = self.post("/images/generations", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn image_edit(&self, req: ImageEditRequest) -> Result { - let res = self.post("/images/edits", &req).await?; - let r = res.json::().await; + pub fn image_edit(&self, req: ImageEditRequest) -> Result { + let res = self.post("/images/edits", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn image_variation( + pub fn image_variation( &self, req: ImageVariationRequest, ) -> Result { - let res = self.post("/images/variations", &req).await?; - let r = res.json::().await; + let res = self.post("/images/variations", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn embedding(&self, req: EmbeddingRequest) -> Result { - let res = self.post("/embeddings", &req).await?; - let r = res.json::().await; + pub fn embedding(&self, req: EmbeddingRequest) -> Result { + let res = self.post("/embeddings", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn file_list(&self) -> Result { - let res = self.get("/files").await?; - let r = res.json::().await; + pub fn file_list(&self) -> Result { + let res = self.get("/files")?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn file_upload( - &self, - req: FileUploadRequest, - ) -> Result { - let res = self.post("/files", &req).await?; - let r = res.json::().await; + pub fn file_upload(&self, req: FileUploadRequest) -> Result { + let res = self.post("/files", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn file_delete( - &self, - req: FileDeleteRequest, - ) -> Result { - let res = self - .delete(&format!("{}/{}", "/files", req.file_id)) - .await?; - let r = res.json::().await; + pub fn file_delete(&self, req: FileDeleteRequest) -> Result { + let res = self.delete(&format!("{}/{}", "/files", req.file_id))?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn file_retrieve( + pub fn file_retrieve( &self, req: FileRetrieveRequest, ) -> Result { - let res = self.get(&format!("{}/{}", "/files", req.file_id)).await?; - let r = res.json::().await; + let res = self.get(&format!("{}/{}", "/files", req.file_id))?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn file_retrieve_content( + pub fn file_retrieve_content( &self, req: FileRetrieveContentRequest, ) -> Result { - let res = self - .get(&format!("{}/{}/content", "/files", req.file_id)) - .await?; - let r = res.json::().await; + let res = self.get(&format!("{}/{}/content", "/files", req.file_id))?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn chat_completion( + pub fn chat_completion( &self, req: ChatCompletionRequest, ) -> Result { - let res = self.post("/chat/completions", &req).await?; - let r = res.json::().await; + let res = self.post("/chat/completions", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn audio_transcription( + pub fn audio_transcription( &self, req: AudioTranscriptionRequest, ) -> Result { - let res = self.post("/audio/transcriptions", &req).await?; - let r = res.json::().await; + let res = self.post("/audio/transcriptions", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn audio_translation( + pub fn audio_translation( &self, req: AudioTranslationRequest, ) -> Result { - let res = self.post("/audio/translations", &req).await?; - let r = res.json::().await; + let res = self.post("/audio/translations", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn create_fine_tune( + pub fn create_fine_tune( &self, req: CreateFineTuneRequest, ) -> Result { - let res = self.post("/fine-tunes", &req).await?; - let r = res.json::().await; + let res = self.post("/fine-tunes", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn list_fine_tune(&self) -> Result { - let res = self.get("/fine-tunes").await?; - let r = res.json::().await; + pub fn list_fine_tune(&self) -> Result { + let res = self.get("/fine-tunes")?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn retrieve_fine_tune( + pub fn retrieve_fine_tune( &self, req: RetrieveFineTuneRequest, ) -> Result { - let res = self - .get(&format!("/fine_tunes/{}", req.fine_tune_id)) - .await?; - let r = res.json::().await; + let res = self.get(&format!("/fine_tunes/{}", req.fine_tune_id))?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn cancel_fine_tune( + pub fn cancel_fine_tune( &self, req: CancelFineTuneRequest, ) -> Result { - let res = self - .post(&format!("/fine_tunes/{}/cancel", req.fine_tune_id), &req) - .await?; - let r = res.json::().await; + let res = self.post(&format!("/fine_tunes/{}/cancel", req.fine_tune_id), &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn list_fine_tune_events( + pub fn list_fine_tune_events( &self, req: ListFineTuneEventsRequest, ) -> Result { - let res = self - .get(&format!("/fine-tunes/{}/events", req.fine_tune_id)) - .await?; - let r = res.json::().await; + let res = self.get(&format!("/fine-tunes/{}/events", req.fine_tune_id))?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn delete_fine_tune( + pub fn delete_fine_tune( &self, req: DeleteFineTuneModelRequest, ) -> Result { - let res = self.delete(&format!("/models/{}", req.model_id)).await?; - let r = res.json::().await; + let res = self.delete(&format!("/models/{}", req.model_id))?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - pub async fn create_moderation( + pub fn create_moderation( &self, req: CreateModerationRequest, ) -> Result { - let res = self.post("/moderations", &req).await?; - let r = res.json::().await; + let res = self.post("/moderations", &req)?; + let r = res.json::(); match r { Ok(r) => Ok(r), Err(e) => Err(self.new_error(e)), } } - fn new_error(&self, err: reqwest::Error) -> APIError { + fn new_error(&self, err: minreq::Error) -> APIError { APIError { message: err.to_string(), }