use crate::v1::audio::{ AudioTranscriptionRequest, AudioTranscriptionResponse, AudioTranslationRequest, AudioTranslationResponse, }; use crate::v1::chat_completion::{ChatCompletionRequest, ChatCompletionResponse}; use crate::v1::completion::{CompletionRequest, CompletionResponse}; use crate::v1::edit::{EditRequest, EditResponse}; use crate::v1::embedding::{EmbeddingRequest, EmbeddingResponse}; use crate::v1::error::APIError; use crate::v1::file::{ FileDeleteRequest, FileDeleteResponse, FileListResponse, FileRetrieveContentRequest, FileRetrieveContentResponse, FileRetrieveRequest, FileRetrieveResponse, FileUploadRequest, FileUploadResponse, }; use crate::v1::fine_tune::{ CancelFineTuneRequest, CancelFineTuneResponse, CreateFineTuneRequest, CreateFineTuneResponse, DeleteFineTuneModelRequest, DeleteFineTuneModelResponse, ListFineTuneEventsRequest, ListFineTuneEventsResponse, ListFineTuneResponse, RetrieveFineTuneRequest, RetrieveFineTuneResponse, }; use crate::v1::image::{ ImageEditRequest, ImageEditResponse, ImageGenerationRequest, ImageGenerationResponse, ImageVariationRequest, ImageVariationResponse, }; use crate::v1::moderation::{CreateModerationRequest, CreateModerationResponse}; use minreq::Response; const API_URL_V1: &str = "https://api.openai.com/v1"; pub struct Client { pub api_endpoint: String, pub api_key: String, pub organization: Option, } impl Client { pub fn new(api_key: String) -> Self { let endpoint = std::env::var("OPENAI_API_BASE").unwrap_or_else(|_| API_URL_V1.to_owned()); Self::new_with_endpoint(endpoint, api_key) } pub fn new_with_endpoint(api_endpoint: String, api_key: String) -> Self { Self { api_endpoint, api_key, organization: None, } } pub fn new_with_organization(api_key: String, organization: String) -> Self { let endpoint = std::env::var("OPENAI_API_BASE").unwrap_or_else(|_| API_URL_V1.to_owned()); Self { api_endpoint: endpoint, api_key, organization: organization.into(), } } pub fn build_request(&self, request: minreq::Request) -> minreq::Request { let mut request = request .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); } request } pub fn post( &self, path: &str, params: &T, ) -> Result { let url = format!( "{api_endpoint}{path}", api_endpoint = self.api_endpoint, path = path ); let request = self.build_request(minreq::post(url)); let res = request.with_json(params).unwrap().send(); match res { 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 fn get(&self, path: &str) -> Result { let url = format!( "{api_endpoint}{path}", api_endpoint = self.api_endpoint, path = path ); let request = self.build_request(minreq::get(url)); let res = request.send(); match res { 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 fn delete(&self, path: &str) -> Result { let url = format!( "{api_endpoint}{path}", api_endpoint = self.api_endpoint, path = path ); let request = self.build_request(minreq::delete(url)); let res = request.send(); match res { 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 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 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 fn image_generation( &self, req: ImageGenerationRequest, ) -> Result { 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 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 fn image_variation( &self, req: ImageVariationRequest, ) -> Result { 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 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 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 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 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 fn file_retrieve( &self, req: FileRetrieveRequest, ) -> Result { 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 fn file_retrieve_content( &self, req: FileRetrieveContentRequest, ) -> Result { 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 fn chat_completion( &self, req: ChatCompletionRequest, ) -> Result { 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 fn audio_transcription( &self, req: AudioTranscriptionRequest, ) -> Result { 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 fn audio_translation( &self, req: AudioTranslationRequest, ) -> Result { 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 fn create_fine_tune( &self, req: CreateFineTuneRequest, ) -> Result { 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 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 fn retrieve_fine_tune( &self, req: RetrieveFineTuneRequest, ) -> Result { 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 fn cancel_fine_tune( &self, req: CancelFineTuneRequest, ) -> Result { 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 fn list_fine_tune_events( &self, req: ListFineTuneEventsRequest, ) -> Result { 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 fn delete_fine_tune( &self, req: DeleteFineTuneModelRequest, ) -> Result { 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 fn create_moderation( &self, req: CreateModerationRequest, ) -> Result { 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: minreq::Error) -> APIError { APIError { message: err.to_string(), } } }