1use anyhow::{anyhow, Result};
2use log::{error, info};
3use reqwest::{header, Client};
4use schemars::JsonSchema;
5use serde::{Deserialize, Serialize};
6
7use crate::enums::{OpenAIAssistantRole, OpenAIRunStatus, OpenAIToolTypes};
8
9#[derive(Deserialize, Serialize, Debug, Clone)]
11pub struct OpenAPICompletionsResponse {
12 pub id: Option<String>,
13 pub object: Option<String>,
14 pub created: Option<u32>,
15 pub model: Option<String>,
16 pub choices: Option<Vec<OpenAPICompletionsChoices>>,
17 pub usage: Option<OpenAPIUsage>,
18}
19
20#[derive(Deserialize, Serialize, Debug, Clone)]
21pub struct OpenAPICompletionsChoices {
22 pub text: Option<String>,
23 pub index: Option<u32>,
24 pub logprobs: Option<u32>,
25 pub finish_reason: Option<String>,
26}
27
28#[derive(Deserialize, Serialize, Debug, Clone)]
30pub struct OpenAPIChatResponse {
31 pub id: Option<String>,
32 pub object: Option<String>,
33 pub created: Option<u32>,
34 pub model: Option<String>,
35 pub choices: Option<Vec<OpenAPIChatChoices>>,
36 pub usage: Option<OpenAPIUsage>,
37}
38
39#[derive(Deserialize, Serialize, Debug, Clone)]
40pub struct OpenAPIChatChoices {
41 pub message: OpenAPIChatMessage,
42 pub index: Option<u32>,
43 pub finish_reason: Option<String>,
44}
45
46#[derive(Deserialize, Serialize, Debug, Clone)]
47pub struct OpenAPIChatMessage {
48 pub role: String,
49 pub content: Option<String>,
50 pub function_call: Option<OpenAPIChatFunctionCall>,
51}
52
53#[derive(Deserialize, Serialize, Debug, Clone)]
54pub struct OpenAPIChatFunctionCall {
55 pub(crate) name: String,
56 pub(crate) arguments: String,
57}
58
59#[derive(Deserialize, Serialize, Debug, Clone)]
60pub struct OpenAPIUsage {
61 prompt_tokens: Option<u32>,
62 completion_tokens: Option<u32>,
63 total_tokens: Option<u32>,
64}
65
66#[derive(Deserialize, Serialize, Debug, Clone)]
67pub struct OpenAIRateLimit {
68 pub tpm: usize, pub rpm: usize, }
71
72#[derive(Deserialize, Serialize, Debug, Clone, JsonSchema)]
73pub struct OpenAIDataResponse<T: JsonSchema> {
74 pub data: T,
75}
76
77#[derive(Deserialize, Serialize, Debug, Clone)]
78pub struct OpenAIAssistantResp {
79 pub id: String,
80 pub object: String,
81 pub created_at: u32,
82 pub name: Option<String>,
83 pub description: Option<String>,
84 pub instructions: Option<String>,
85 pub model: String,
86 pub tools: Vec<OpenAITools>,
87 pub file_ids: Vec<String>,
88}
89
90#[derive(Deserialize, Serialize, Debug, Clone)]
91pub struct OpenAITools {
92 #[serde(rename(deserialize = "type", serialize = "type"))]
93 tool_type: OpenAIToolTypes,
94}
95
96#[derive(Deserialize, Serialize, Debug, Clone)]
97pub struct OpenAIThreadResp {
98 pub id: String,
99 pub object: String,
100 pub created_at: u32,
101}
102
103#[derive(Deserialize, Serialize, Debug, Clone)]
104pub struct OpenAIMessageListResp {
105 pub object: String,
106 pub data: Vec<OpenAIMessageResp>,
107}
108
109#[derive(Deserialize, Serialize, Debug, Clone)]
110pub struct OpenAIMessageResp {
111 pub id: String,
112 pub object: String,
113 pub created_at: u32,
114 pub thread_id: String,
115 pub role: OpenAIAssistantRole,
116 pub content: Vec<OpenAIContent>,
117 }
119
120#[derive(Deserialize, Serialize, Debug, Clone)]
121pub struct OpenAIContent {
122 #[serde(rename(deserialize = "type", serialize = "type"))]
123 pub content_type: String,
124 pub text: Option<OpenAIContentText>,
125}
126
127#[derive(Deserialize, Serialize, Debug, Clone)]
128pub struct OpenAIContentText {
129 pub value: String,
130}
131
132#[derive(Deserialize, Serialize, Debug, Clone)]
133pub struct OpenAIRunResp {
134 pub id: String,
135 pub object: String,
136 pub created_at: u32,
137 pub status: OpenAIRunStatus,
138}
139
140#[derive(Deserialize, Serialize, Debug, Clone)]
141pub struct OpenAIFile {
142 pub id: String,
143 debug: bool,
144 api_key: String,
145}
146
147#[derive(Deserialize, Serialize, Debug, Clone)]
148pub struct OpenAIFileResp {
149 id: String,
150}
151
152impl OpenAIFile {
153 pub async fn new(file_bytes: Vec<u8>, open_ai_key: &str, debug: bool) -> Result<Self> {
155 let mut new_file = OpenAIFile {
156 id: "this-will-be-overwritten".to_string(),
157 debug,
158 api_key: open_ai_key.to_string(),
159 };
160 new_file.upload_file(file_bytes).await?;
162 Ok(new_file)
163 }
164
165 async fn upload_file(&mut self, file_bytes: Vec<u8>) -> Result<()> {
169 let files_url = "https://api.openai.com/v1/files";
170
171 let form = reqwest::multipart::Form::new()
172 .text("purpose", "assistants")
173 .part(
174 "file",
175 reqwest::multipart::Part::bytes(file_bytes).file_name("file.pdf"),
176 );
177
178 let client = Client::new();
180
181 let response = client
182 .post(files_url)
183 .header(header::CONTENT_TYPE, "application/json")
184 .header("OpenAI-Beta", "assistants=v1")
185 .bearer_auth(&self.api_key)
186 .multipart(form)
187 .send()
188 .await?;
189
190 let response_status = response.status();
191 let response_text = response.text().await?;
192
193 if self.debug {
194 info!(
195 "[debug] OpenAI Files status API response: [{}] {:#?}",
196 &response_status, &response_text
197 );
198 }
199
200 let response_deser: OpenAIFileResp =
202 serde_json::from_str(&response_text).map_err(|error| {
203 error!(
204 "[OpenAIAssistant] Files API response serialization error: {}",
205 &error
206 );
207 anyhow!("Error: {}", error)
208 })?;
209
210 self.id = response_deser.id;
211
212 Ok(())
213 }
214}