diff --git a/examples/assistant.rs b/examples/assistant.rs new file mode 100644 index 0000000..0b344bf --- /dev/null +++ b/examples/assistant.rs @@ -0,0 +1,64 @@ +use openai_api_rs::v1::api::Client; +use openai_api_rs::v1::assistant::AssistantRequest; +use openai_api_rs::v1::common::GPT4_1106_PREVIEW; +use openai_api_rs::v1::message::{CreateMessageRequest, MessageRole}; +use openai_api_rs::v1::run::CreateRunRequest; +use openai_api_rs::v1::thread::CreateThreadRequest; +use std::collections::HashMap; +use std::env; + +fn main() -> Result<(), Box> { + let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); + + let mut tools = HashMap::new(); + tools.insert("type".to_string(), "code_interpreter".to_string()); + + let req = AssistantRequest::new(GPT4_1106_PREVIEW.to_string()); + req.clone() + .description("this is a test assistant".to_string()); + req.clone().instructions("You are a personal math tutor. When asked a question, write and run Python code to answer the question.".to_string()); + req.clone().tools(vec![tools]); + let result = client.create_assistant(req)?; + println!("{:?}", result.id); + + let thread_req = CreateThreadRequest::new(); + let thread_result = client.create_thread(thread_req)?; + println!("{:?}", thread_result.id.clone()); + + let message_req = CreateMessageRequest::new( + MessageRole::user, + "`I need to solve the equation 3x + 11 = 14. Can you help me?".to_string(), + ); + + let message_result = client.create_message(thread_result.id.clone(), message_req)?; + println!("{:?}", message_result.id.clone()); + + let run_req = CreateRunRequest::new(result.id); + let run_result = client.create_run(thread_result.id.clone(), run_req)?; + + loop { + let run_result = client + .retrieve_run(thread_result.id.clone(), run_result.id.clone()) + .unwrap(); + if run_result.status == "completed" { + break; + } else { + println!("waiting..."); + std::thread::sleep(std::time::Duration::from_secs(1)); + } + } + + let list_message_result = client.list_messages(thread_result.id.clone()).unwrap(); + for data in list_message_result.data { + for content in data.content { + println!( + "{:?}: {:?} {:?}", + data.role, content.text.value, content.text.annotations + ); + } + } + + Ok(()) +} + +// OPENAI_API_KEY=xxxx cargo run --package openai-api-rs --example assistant diff --git a/examples/chat_completion.rs b/examples/chat_completion.rs index 542e4d7..3135fc0 100644 --- a/examples/chat_completion.rs +++ b/examples/chat_completion.rs @@ -1,12 +1,13 @@ use openai_api_rs::v1::api::Client; use openai_api_rs::v1::chat_completion::{self, ChatCompletionRequest}; +use openai_api_rs::v1::common::GPT4; use std::env; fn main() -> Result<(), Box> { let client = Client::new(env::var("OPENAI_API_KEY").unwrap().to_string()); let req = ChatCompletionRequest::new( - chat_completion::GPT4.to_string(), + GPT4.to_string(), vec![chat_completion::ChatCompletionMessage { role: chat_completion::MessageRole::user, content: String::from("What is Bitcoin?"), diff --git a/examples/function_call.rs b/examples/function_call.rs index 3a32144..ed83bb0 100644 --- a/examples/function_call.rs +++ b/examples/function_call.rs @@ -1,5 +1,6 @@ use openai_api_rs::v1::api::Client; use openai_api_rs::v1::chat_completion::{self, ChatCompletionRequest, FunctionCallType}; +use openai_api_rs::v1::common::GPT3_5_TURBO_0613; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::{env, vec}; @@ -30,7 +31,7 @@ fn main() -> Result<(), Box> { ); let req = ChatCompletionRequest::new( - chat_completion::GPT3_5_TURBO_0613.to_string(), + GPT3_5_TURBO_0613.to_string(), vec![chat_completion::ChatCompletionMessage { role: chat_completion::MessageRole::user, content: String::from("What is the price of Ethereum?"), diff --git a/examples/function_call_role.rs b/examples/function_call_role.rs index 56ffc87..eeb2489 100644 --- a/examples/function_call_role.rs +++ b/examples/function_call_role.rs @@ -1,5 +1,6 @@ use openai_api_rs::v1::api::Client; use openai_api_rs::v1::chat_completion::{self, ChatCompletionRequest}; +use openai_api_rs::v1::common::GPT3_5_TURBO_0613; use serde::{Deserialize, Serialize}; use std::collections::HashMap; use std::{env, vec}; @@ -30,7 +31,7 @@ fn main() -> Result<(), Box> { ); let req = ChatCompletionRequest::new( - chat_completion::GPT3_5_TURBO_0613.to_string(), + GPT3_5_TURBO_0613.to_string(), vec![chat_completion::ChatCompletionMessage { role: chat_completion::MessageRole::user, content: String::from("What is the price of Ethereum?"), @@ -70,7 +71,7 @@ fn main() -> Result<(), Box> { let coin = c.coin; let req = ChatCompletionRequest::new( - chat_completion::GPT3_5_TURBO_0613.to_string(), + GPT3_5_TURBO_0613.to_string(), vec![ chat_completion::ChatCompletionMessage { role: chat_completion::MessageRole::user, diff --git a/src/v1/api.rs b/src/v1/api.rs index a4f5c95..b450601 100644 --- a/src/v1/api.rs +++ b/src/v1/api.rs @@ -1,3 +1,7 @@ +use crate::v1::assistant::{ + AssistantFileObject, AssistantFileRequest, AssistantObject, AssistantRequest, DeletionStatus, + ListAssistant, ListAssistantFile, +}; use crate::v1::audio::{ AudioTranscriptionRequest, AudioTranscriptionResponse, AudioTranslationRequest, AudioTranslationResponse, @@ -22,7 +26,16 @@ use crate::v1::image::{ ImageEditRequest, ImageEditResponse, ImageGenerationRequest, ImageGenerationResponse, ImageVariationRequest, ImageVariationResponse, }; +use crate::v1::message::{ + CreateMessageRequest, ListMessage, ListMessageFile, MessageFileObject, MessageObject, + ModifyMessageRequest, +}; use crate::v1::moderation::{CreateModerationRequest, CreateModerationResponse}; +use crate::v1::run::{ + CreateRunRequest, CreateThreadAndRunRequest, ListRun, ListRunStep, ModifyRunRequest, RunObject, + RunStepObject, +}; +use crate::v1::thread::{CreateThreadRequest, ModifyThreadRequest, ThreadObject}; use minreq::Response; @@ -57,13 +70,16 @@ impl Client { } } - pub fn build_request(&self, request: minreq::Request) -> minreq::Request { + pub fn build_request(&self, request: minreq::Request, is_beta: bool) -> 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); } + if is_beta { + request = request.with_header("OpenAI-Beta", "assistants=v1"); + } request } @@ -77,8 +93,7 @@ impl Client { api_endpoint = self.api_endpoint, path = path ); - - let request = self.build_request(minreq::post(url)); + let request = self.build_request(minreq::post(url), Self::is_beta(path)); let res = request.with_json(params).unwrap().send(); match res { Ok(res) => { @@ -100,8 +115,7 @@ impl Client { api_endpoint = self.api_endpoint, path = path ); - - let request = self.build_request(minreq::get(url)); + let request = self.build_request(minreq::get(url), Self::is_beta(path)); let res = request.send(); match res { Ok(res) => { @@ -123,8 +137,7 @@ impl Client { api_endpoint = self.api_endpoint, path = path ); - - let request = self.build_request(minreq::delete(url)); + let request = self.build_request(minreq::delete(url), Self::is_beta(path)); let res = request.send(); match res { Ok(res) => { @@ -368,9 +381,396 @@ impl Client { } } + pub fn create_assistant(&self, req: AssistantRequest) -> Result { + let res = self.post("/assistants", &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn retrieve_assistant(&self, assistant_id: String) -> Result { + let res = self.get(&format!("/assistants/{}", assistant_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn modify_assistant( + &self, + assistant_id: String, + req: AssistantRequest, + ) -> Result { + let res = self.post(&format!("/assistants/{}", assistant_id), &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn delete_assistant(&self, assistant_id: String) -> Result { + let res = self.delete(&format!("/assistants/{}", assistant_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn list_assistant( + &self, + limit: Option, + order: Option, + after: Option, + before: Option, + ) -> Result { + let mut url = "/assistants".to_owned(); + url = Self::query_params(limit, order, after, before, url); + let res = self.get(&url)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn create_assistant_file( + &self, + assistant_id: String, + req: AssistantFileRequest, + ) -> Result { + let res = self.post(&format!("/assistants/{}/files", assistant_id), &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn retrieve_assistant_file( + &self, + assistant_id: String, + file_id: String, + ) -> Result { + let res = self.get(&format!("/assistants/{}/files/{}", assistant_id, file_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn delete_assistant_file( + &self, + assistant_id: String, + file_id: String, + ) -> Result { + let res = self.delete(&format!("/assistants/{}/files/{}", assistant_id, file_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn list_assistant_file( + &self, + assistant_id: String, + limit: Option, + order: Option, + after: Option, + before: Option, + ) -> Result { + let mut url = format!("/assistants/{}/files", assistant_id); + url = Self::query_params(limit, order, after, before, url); + let res = self.get(&url)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn create_thread(&self, req: CreateThreadRequest) -> Result { + let res = self.post("/threads", &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn retrieve_thread(&self, thread_id: String) -> Result { + let res = self.get(&format!("/threads/{}", thread_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn modify_thread( + &self, + thread_id: String, + req: ModifyThreadRequest, + ) -> Result { + let res = self.post(&format!("/threads/{}", thread_id), &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn delete_thread(&self, thread_id: String) -> Result { + let res = self.delete(&format!("/threads/{}", thread_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn create_message( + &self, + thread_id: String, + req: CreateMessageRequest, + ) -> Result { + let res = self.post(&format!("/threads/{}/messages", thread_id), &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn retrieve_message( + &self, + thread_id: String, + message_id: String, + ) -> Result { + let res = self.get(&format!("/threads/{}/messages/{}", thread_id, message_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn modify_message( + &self, + thread_id: String, + message_id: String, + req: ModifyMessageRequest, + ) -> Result { + let res = self.post( + &format!("/threads/{}/messages/{}", thread_id, message_id), + &req, + )?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn list_messages(&self, thread_id: String) -> Result { + let res = self.get(&format!("/threads/{}/messages", thread_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn retrieve_message_file( + &self, + thread_id: String, + message_id: String, + file_id: String, + ) -> Result { + let res = self.get(&format!( + "/threads/{}/messages/{}/files/{}", + thread_id, message_id, file_id + ))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn list_message_file( + &self, + thread_id: String, + message_id: String, + limit: Option, + order: Option, + after: Option, + before: Option, + ) -> Result { + let mut url = format!("/threads/{}/messages/{}/files", thread_id, message_id); + url = Self::query_params(limit, order, after, before, url); + let res = self.get(&url)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn create_run( + &self, + thread_id: String, + req: CreateRunRequest, + ) -> Result { + let res = self.post(&format!("/threads/{}/runs", thread_id), &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn retrieve_run(&self, thread_id: String, run_id: String) -> Result { + let res = self.get(&format!("/threads/{}/runs/{}", thread_id, run_id))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn modify_run( + &self, + thread_id: String, + run_id: String, + req: ModifyRunRequest, + ) -> Result { + let res = self.post(&format!("/threads/{}/runs/{}", thread_id, run_id), &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn list_run( + &self, + thread_id: String, + limit: Option, + order: Option, + after: Option, + before: Option, + ) -> Result { + let mut url = format!("/threads/{}/runs", thread_id); + url = Self::query_params(limit, order, after, before, url); + let res = self.get(&url)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn cancel_run(&self, thread_id: String, run_id: String) -> Result { + let empty_req = ModifyRunRequest::new(); + let res = self.post( + &format!("/threads/{}/runs/{}/cancel", thread_id, run_id), + &empty_req, + )?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn create_thread_and_run( + &self, + req: CreateThreadAndRunRequest, + ) -> Result { + let res = self.post("/threads/runs", &req)?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn retrieve_run_step( + &self, + thread_id: String, + run_id: String, + step_id: String, + ) -> Result { + let res = self.get(&format!( + "/threads/{}/runs/{}/steps/{}", + thread_id, run_id, step_id + ))?; + let r = res.json::(); + match r { + Ok(r) => Ok(r), + Err(e) => Err(self.new_error(e)), + } + } + + pub fn list_run_step( + &self, + thread_id: String, + run_id: String, + limit: Option, + order: Option, + after: Option, + before: Option, + ) -> Result { + let mut url = format!("/threads/{}/runs/{}/steps", thread_id, run_id); + url = Self::query_params(limit, order, after, before, url); + let res = self.get(&url)?; + 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(), } } + + fn is_beta(path: &str) -> bool { + path.starts_with("/assistants") || path.starts_with("/threads") + } + + fn query_params( + limit: Option, + order: Option, + after: Option, + before: Option, + mut url: String, + ) -> String { + let mut params = vec![]; + if let Some(limit) = limit { + params.push(format!("limit={}", limit)); + } + if let Some(order) = order { + params.push(format!("order={}", order)); + } + if let Some(after) = after { + params.push(format!("after={}", after)); + } + if let Some(before) = before { + params.push(format!("before={}", before)); + } + if !params.is_empty() { + url = format!("{}?{}", url, params.join("&")); + } + url + } } diff --git a/src/v1/assistant.rs b/src/v1/assistant.rs new file mode 100644 index 0000000..bc77360 --- /dev/null +++ b/src/v1/assistant.rs @@ -0,0 +1,94 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::impl_builder_methods; + +#[derive(Debug, Serialize, Clone)] +pub struct AssistantRequest { + pub model: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tools: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub file_ids: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl AssistantRequest { + pub fn new(model: String) -> Self { + Self { + model, + name: None, + description: None, + instructions: None, + tools: None, + file_ids: None, + metadata: None, + } + } +} + +impl_builder_methods!( + AssistantRequest, + name: String, + description: String, + instructions: String, + tools: Vec>, + file_ids: Vec, + metadata: HashMap +); + +#[derive(Debug, Serialize, Deserialize)] +pub struct AssistantObject { + pub id: String, + pub object: String, + pub created_at: i64, + #[serde(skip_serializing_if = "Option::is_none")] + pub name: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub description: Option, + pub model: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub instructions: Option, + pub tools: Vec, + pub file_ids: Vec, + pub metadata: HashMap, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct DeletionStatus { + pub id: String, + pub object: String, + pub deleted: bool, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ListAssistant { + pub object: String, + pub data: Vec, +} + +#[derive(Debug, Serialize, Clone)] +pub struct AssistantFileRequest { + pub file_id: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct AssistantFileObject { + pub id: String, + pub object: String, + pub created_at: i64, + pub assistant_id: String, +} + +#[derive(Debug, Serialize, Deserialize)] +pub struct ListAssistantFile { + pub object: String, + pub data: Vec, +} diff --git a/src/v1/audio.rs b/src/v1/audio.rs index 1a5a954..9f92bf8 100644 --- a/src/v1/audio.rs +++ b/src/v1/audio.rs @@ -4,7 +4,7 @@ use crate::impl_builder_methods; pub const WHISPER_1: &str = "whisper-1"; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct AudioTranscriptionRequest { pub file: String, pub model: String, @@ -44,7 +44,7 @@ pub struct AudioTranscriptionResponse { pub text: String, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct AudioTranslationRequest { pub file: String, pub model: String, diff --git a/src/v1/chat_completion.rs b/src/v1/chat_completion.rs index fa4259b..b3d1708 100644 --- a/src/v1/chat_completion.rs +++ b/src/v1/chat_completion.rs @@ -6,35 +6,14 @@ use std::collections::HashMap; use crate::impl_builder_methods; use crate::v1::common; -// https://platform.openai.com/docs/models/gpt-3-5 -pub const GPT3_5_TURBO_1106: &str = "gpt-3.5-turbo-1106"; -pub const GPT3_5_TURBO: &str = "gpt-3.5-turbo"; -pub const GPT3_5_TURBO_16K: &str = "gpt-3.5-turbo-16k"; -pub const GPT3_5_TURBO_INSTRUCT: &str = "gpt-3.5-turbo-instruct"; -// - legacy -pub const GPT3_5_TURBO_0613: &str = "gpt-3.5-turbo-0613"; -pub const GPT3_5_TURBO_16K_0613: &str = "gpt-3.5-turbo-16k-0613"; -pub const GPT3_5_TURBO_0301: &str = "gpt-3.5-turbo-0301"; - -// https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo -pub const GPT4_1106_PREVIEW: &str = "gpt-4-1106-preview"; -pub const GPT4_VISION_PREVIEW: &str = "gpt-4-vision-preview"; -pub const GPT4: &str = "gpt-4"; -pub const GPT4_32K: &str = "gpt-4-32k"; -pub const GPT4_0613: &str = "gpt-4-0613"; -pub const GPT4_32K_0613: &str = "gpt-4-32k-0613"; -// - legacy -pub const GPT4_0314: &str = "gpt-4-0314"; -pub const GPT4_32K_0314: &str = "gpt-4-32k-0314"; - -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub enum FunctionCallType { None, Auto, Function { name: String }, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct ChatCompletionRequest { pub model: String, pub messages: Vec, @@ -153,7 +132,7 @@ pub struct ChatCompletionResponse { pub usage: common::Usage, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct Function { pub name: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -161,7 +140,7 @@ pub struct Function { pub parameters: FunctionParameters, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] #[serde(rename_all = "lowercase")] pub enum JSONSchemaType { Object, @@ -172,7 +151,7 @@ pub enum JSONSchemaType { Boolean, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct JSONSchemaDefine { #[serde(rename = "type")] pub schema_type: Option, @@ -188,7 +167,7 @@ pub struct JSONSchemaDefine { pub items: Option>, } -#[derive(Debug, Serialize, Deserialize)] +#[derive(Debug, Serialize, Deserialize, Clone)] pub struct FunctionParameters { #[serde(rename = "type")] pub schema_type: JSONSchemaType, diff --git a/src/v1/common.rs b/src/v1/common.rs index 312df28..7db9999 100644 --- a/src/v1/common.rs +++ b/src/v1/common.rs @@ -20,3 +20,24 @@ macro_rules! impl_builder_methods { } }; } + +// https://platform.openai.com/docs/models/gpt-3-5 +pub const GPT3_5_TURBO_1106: &str = "gpt-3.5-turbo-1106"; +pub const GPT3_5_TURBO: &str = "gpt-3.5-turbo"; +pub const GPT3_5_TURBO_16K: &str = "gpt-3.5-turbo-16k"; +pub const GPT3_5_TURBO_INSTRUCT: &str = "gpt-3.5-turbo-instruct"; +// - legacy +pub const GPT3_5_TURBO_0613: &str = "gpt-3.5-turbo-0613"; +pub const GPT3_5_TURBO_16K_0613: &str = "gpt-3.5-turbo-16k-0613"; +pub const GPT3_5_TURBO_0301: &str = "gpt-3.5-turbo-0301"; + +// https://platform.openai.com/docs/models/gpt-4-and-gpt-4-turbo +pub const GPT4_1106_PREVIEW: &str = "gpt-4-1106-preview"; +pub const GPT4_VISION_PREVIEW: &str = "gpt-4-vision-preview"; +pub const GPT4: &str = "gpt-4"; +pub const GPT4_32K: &str = "gpt-4-32k"; +pub const GPT4_0613: &str = "gpt-4-0613"; +pub const GPT4_32K_0613: &str = "gpt-4-32k-0613"; +// - legacy +pub const GPT4_0314: &str = "gpt-4-0314"; +pub const GPT4_32K_0314: &str = "gpt-4-32k-0314"; diff --git a/src/v1/completion.rs b/src/v1/completion.rs index aeb5550..6b29879 100644 --- a/src/v1/completion.rs +++ b/src/v1/completion.rs @@ -18,7 +18,7 @@ pub const GPT3_CURIE: &str = "curie"; pub const GPT3_ADA: &str = "ada"; pub const GPT3_BABBAGE: &str = "babbage"; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct CompletionRequest { pub model: String, pub prompt: String, diff --git a/src/v1/edit.rs b/src/v1/edit.rs index 0bf2c03..3ec5f3d 100644 --- a/src/v1/edit.rs +++ b/src/v1/edit.rs @@ -4,7 +4,7 @@ use std::option::Option; use crate::impl_builder_methods; use crate::v1::common; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct EditRequest { pub model: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/v1/embedding.rs b/src/v1/embedding.rs index a299841..420561b 100644 --- a/src/v1/embedding.rs +++ b/src/v1/embedding.rs @@ -10,7 +10,7 @@ pub struct EmbeddingData { pub index: i32, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct EmbeddingRequest { pub model: String, pub input: String, diff --git a/src/v1/fine_tune.rs b/src/v1/fine_tune.rs index a46e3af..3af17dc 100644 --- a/src/v1/fine_tune.rs +++ b/src/v1/fine_tune.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::impl_builder_methods; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct CreateFineTuneRequest { pub training_file: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/v1/image.rs b/src/v1/image.rs index 6d92be9..c51505c 100644 --- a/src/v1/image.rs +++ b/src/v1/image.rs @@ -8,7 +8,7 @@ pub struct ImageData { pub url: String, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct ImageGenerationRequest { pub prompt: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -47,7 +47,7 @@ pub struct ImageGenerationResponse { pub data: Vec, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct ImageEditRequest { pub image: String, #[serde(skip_serializing_if = "Option::is_none")] @@ -92,7 +92,7 @@ pub struct ImageEditResponse { pub data: Vec, } -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct ImageVariationRequest { pub image: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/v1/message.rs b/src/v1/message.rs new file mode 100644 index 0000000..47ee546 --- /dev/null +++ b/src/v1/message.rs @@ -0,0 +1,118 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::impl_builder_methods; + +#[derive(Debug, Serialize, Clone)] +pub struct CreateMessageRequest { + pub role: MessageRole, + pub content: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub file_ids: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl CreateMessageRequest { + pub fn new(role: MessageRole, content: String) -> Self { + Self { + role, + content, + file_ids: None, + metadata: None, + } + } +} + +impl_builder_methods!( + CreateMessageRequest, + file_ids: Vec, + metadata: HashMap +); + +#[derive(Debug, Serialize, Clone)] +pub struct ModifyMessageRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl ModifyMessageRequest { + pub fn new() -> Self { + Self { metadata: None } + } +} + +impl Default for ModifyMessageRequest { + fn default() -> Self { + Self::new() + } +} + +impl_builder_methods!( + ModifyMessageRequest, + metadata: HashMap +); + +#[derive(Debug, Deserialize)] +pub struct MessageObject { + pub id: String, + pub object: String, + pub created_at: i64, + pub thread_id: String, + pub role: MessageRole, + pub content: Vec, + #[serde(skip_serializing_if = "Option::is_none")] + pub assistant_id: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub run_id: Option, + pub file_ids: Vec, + pub metadata: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[allow(non_camel_case_types)] +pub enum MessageRole { + user, + system, + assistant, + function, +} + +#[derive(Debug, Deserialize)] +pub struct Content { + #[serde(rename = "type")] + pub content_type: String, + pub text: ContentText, +} + +#[derive(Debug, Deserialize)] +pub struct ContentText { + pub value: String, + pub annotations: Vec, +} + +#[derive(Debug, Deserialize)] +pub struct ListMessage { + pub object: String, + pub data: Vec, + pub first_id: String, + pub last_id: String, + pub has_more: bool, +} + +#[derive(Debug, Deserialize)] +pub struct MessageFileObject { + pub id: String, + pub object: String, + pub created_at: i64, + pub message_id: String, +} + +#[derive(Debug, Deserialize)] +pub struct ListMessageFile { + pub object: String, + pub data: Vec, + pub first_id: String, + pub last_id: String, + pub has_more: bool, +} diff --git a/src/v1/mod.rs b/src/v1/mod.rs index 718339f..acfd9ec 100644 --- a/src/v1/mod.rs +++ b/src/v1/mod.rs @@ -11,4 +11,10 @@ pub mod fine_tune; pub mod image; pub mod moderation; +// beta +pub mod assistant; +pub mod message; +pub mod run; +pub mod thread; + pub mod api; diff --git a/src/v1/moderation.rs b/src/v1/moderation.rs index ee920d0..bef1039 100644 --- a/src/v1/moderation.rs +++ b/src/v1/moderation.rs @@ -2,7 +2,7 @@ use serde::{Deserialize, Serialize}; use crate::impl_builder_methods; -#[derive(Debug, Serialize)] +#[derive(Debug, Serialize, Clone)] pub struct CreateModerationRequest { pub input: String, #[serde(skip_serializing_if = "Option::is_none")] diff --git a/src/v1/run.rs b/src/v1/run.rs new file mode 100644 index 0000000..5d0e017 --- /dev/null +++ b/src/v1/run.rs @@ -0,0 +1,150 @@ +use super::thread::CreateThreadRequest; +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::impl_builder_methods; + +#[derive(Debug, Serialize, Clone)] +pub struct CreateRunRequest { + assistant_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tools: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl CreateRunRequest { + pub fn new(assistant_id: String) -> Self { + Self { + assistant_id, + model: None, + instructions: None, + tools: None, + metadata: None, + } + } +} + +impl_builder_methods!( + CreateRunRequest, + model: String, + instructions: String, + tools: Vec>, + metadata: HashMap +); + +#[derive(Debug, Serialize, Clone)] +pub struct ModifyRunRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl ModifyRunRequest { + pub fn new() -> Self { + Self { metadata: None } + } +} + +impl Default for ModifyRunRequest { + fn default() -> Self { + Self::new() + } +} + +impl_builder_methods!( + ModifyRunRequest, + metadata: HashMap +); + +#[derive(Debug, Deserialize)] +pub struct RunObject { + pub id: String, + pub object: String, + pub created_at: i64, + pub thread_id: String, + pub assistant_id: String, + pub status: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub required_action: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub last_error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub expires_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub started_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cancelled_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub failed_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub completed_at: Option, + pub model: String, + pub instructions: Option, + pub tools: Vec>, + pub file_ids: Vec, + pub metadata: HashMap, +} + +#[derive(Debug, Deserialize)] +pub struct ListRun { + pub object: String, + pub data: Vec, + pub first_id: String, + pub last_id: String, + pub has_more: bool, +} + +#[derive(Debug, Serialize, Clone)] +pub struct CreateThreadAndRunRequest { + pub assistant_id: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub thread: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub model: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub instructions: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub tools: Option>>, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct RunStepObject { + pub id: String, + pub object: String, + pub created_at: i64, + pub assistant_id: String, + pub thread_id: String, + pub run_id: String, + #[serde(rename = "type")] + pub run_step_type: String, + pub status: String, + pub step_details: HashMap, + #[serde(skip_serializing_if = "Option::is_none")] + pub last_error: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub expires_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub started_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub cancelled_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub failed_at: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub completed_at: Option, + pub metadata: HashMap, +} + +#[derive(Debug, Deserialize, Clone)] +pub struct ListRunStep { + pub object: String, + pub data: Vec, + pub first_id: String, + pub last_id: String, + pub has_more: bool, +} diff --git a/src/v1/thread.rs b/src/v1/thread.rs new file mode 100644 index 0000000..1644d21 --- /dev/null +++ b/src/v1/thread.rs @@ -0,0 +1,83 @@ +use serde::{Deserialize, Serialize}; +use std::collections::HashMap; + +use crate::impl_builder_methods; + +#[derive(Debug, Serialize, Clone)] +pub struct CreateThreadRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub messages: Option>, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl CreateThreadRequest { + pub fn new() -> Self { + Self { + messages: None, + metadata: None, + } + } +} + +impl Default for CreateThreadRequest { + fn default() -> Self { + Self::new() + } +} + +impl_builder_methods!( + CreateThreadRequest, + messages: Vec, + metadata: HashMap +); + +#[derive(Debug, Serialize, Deserialize)] +pub struct ThreadObject { + pub id: String, + pub object: String, + pub created_at: i64, + pub metadata: HashMap, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +pub struct Message { + pub role: MessageRole, + pub content: String, + #[serde(skip_serializing_if = "Option::is_none")] + pub file_ids: Option, + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +#[derive(Debug, Serialize, Deserialize, Clone)] +#[allow(non_camel_case_types)] +pub enum MessageRole { + user, + system, + assistant, + function, +} + +#[derive(Debug, Serialize, Clone)] +pub struct ModifyThreadRequest { + #[serde(skip_serializing_if = "Option::is_none")] + pub metadata: Option>, +} + +impl ModifyThreadRequest { + pub fn new() -> Self { + Self { metadata: None } + } +} + +impl Default for ModifyThreadRequest { + fn default() -> Self { + Self::new() + } +} + +impl_builder_methods!( + ModifyThreadRequest, + metadata: HashMap +);