Skip to content

Commit 3d61c10

Browse files
authored
Updates for new openapi spec v1.2 (#44)
* types for Chat API; API group for chat; example for chat completion * oops: example for chat completion * types to use for chat streamingc * chat streaming example * add comment for reason for different type * add audio transcribe support * add audio transcribe example * audio translate * add translation example * update types with updated openapi spec * update openapi spec yaml file * more merge conflict resolution * additional output * update readme * update example dependencies * remove unused duplicate types * fix
1 parent b326ca7 commit 3d61c10

File tree

36 files changed

+1138
-229
lines changed

36 files changed

+1138
-229
lines changed

async-openai/README.md

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -23,7 +23,9 @@
2323

2424
- It's based on [OpenAI OpenAPI spec](https://github.com/openai/openai-openapi)
2525
- Current features:
26-
- [x] Completions (including SSE streaming & Chat)
26+
- [x] Audio
27+
- [x] Chat (including SSE streaming)
28+
- [x] Completions (including SSE streaming)
2729
- [x] Edits
2830
- [x] Embeddings
2931
- [x] Files

async-openai/src/audio.rs

Lines changed: 73 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,73 @@
1+
use crate::{
2+
error::OpenAIError,
3+
types::{
4+
CreateTranscriptionRequest, CreateTranscriptionResponse, CreateTranslationRequest,
5+
CreateTranslationResponse,
6+
},
7+
util::create_file_part,
8+
Client,
9+
};
10+
11+
/// Turn audio into text
12+
/// Related guide: [Speech to text](https://platform.openai.com/docs/guides/speech-to-text)
13+
pub struct Audio<'c> {
14+
client: &'c Client,
15+
}
16+
17+
impl<'c> Audio<'c> {
18+
pub fn new(client: &'c Client) -> Self {
19+
Self { client }
20+
}
21+
22+
/// Transcribes audio into the input language.
23+
pub async fn transcribe(
24+
&self,
25+
request: CreateTranscriptionRequest,
26+
) -> Result<CreateTranscriptionResponse, OpenAIError> {
27+
let audio_part = create_file_part(&request.file.path).await?;
28+
29+
let mut form = reqwest::multipart::Form::new()
30+
.part("file", audio_part)
31+
.text("model", request.model);
32+
33+
if let Some(prompt) = request.prompt {
34+
form = form.text("prompt", prompt);
35+
}
36+
37+
if let Some(response_format) = request.response_format {
38+
form = form.text("response_format", response_format.to_string())
39+
}
40+
41+
if let Some(temperature) = request.temperature {
42+
form = form.text("temperature", temperature.to_string())
43+
}
44+
45+
self.client.post_form("/audio/transcriptions", form).await
46+
}
47+
48+
/// Translates audio into into English.
49+
pub async fn translate(
50+
&self,
51+
request: CreateTranslationRequest,
52+
) -> Result<CreateTranslationResponse, OpenAIError> {
53+
let audio_part = create_file_part(&request.file.path).await?;
54+
55+
let mut form = reqwest::multipart::Form::new()
56+
.part("file", audio_part)
57+
.text("model", request.model);
58+
59+
if let Some(prompt) = request.prompt {
60+
form = form.text("prompt", prompt);
61+
}
62+
63+
if let Some(response_format) = request.response_format {
64+
form = form.text("response_format", response_format.to_string())
65+
}
66+
67+
if let Some(temperature) = request.temperature {
68+
form = form.text("temperature", temperature.to_string())
69+
}
70+
71+
self.client.post_form("/audio/translations", form).await
72+
}
73+
}

async-openai/src/chat.rs

Lines changed: 13 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
11
use crate::{
2-
client::Client,
32
error::OpenAIError,
4-
types::{ChatResponseStream, CreateChatRequest, CreateChatResponse},
3+
types::{
4+
ChatCompletionResponseStream, CreateChatCompletionRequest, CreateChatCompletionResponse,
5+
},
6+
Client,
57
};
68

7-
/// Given a series of messages, the model will return one or more predicted
8-
/// completion messages.
9+
/// Given a chat conversation, the model will return a chat completion response.
910
pub struct Chat<'c> {
1011
client: &'c Client,
1112
}
@@ -15,11 +16,11 @@ impl<'c> Chat<'c> {
1516
Self { client }
1617
}
1718

18-
/// Creates a completion for the provided messages and parameters
19+
/// Creates a completion for the chat message
1920
pub async fn create(
2021
&self,
21-
request: CreateChatRequest,
22-
) -> Result<CreateChatResponse, OpenAIError> {
22+
request: CreateChatCompletionRequest,
23+
) -> Result<CreateChatCompletionResponse, OpenAIError> {
2324
if request.stream.is_some() && request.stream.unwrap() {
2425
return Err(OpenAIError::InvalidArgument(
2526
"When stream is true, use Chat::create_stream".into(),
@@ -28,17 +29,15 @@ impl<'c> Chat<'c> {
2829
self.client.post("/chat/completions", request).await
2930
}
3031

31-
/// Creates a completion request for the provided messages and parameters
32+
/// Creates a completion for the chat message
3233
///
33-
/// Stream back partial progress. Tokens will be sent as data-only
34-
/// [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#event_stream_format)
35-
/// as they become available, with the stream terminated by a data: \[DONE\] message.
34+
/// partial message deltas will be sent, like in ChatGPT. Tokens will be sent as data-only [server-sent events](https://developer.mozilla.org/en-US/docs/Web/API/Server-sent_events/Using_server-sent_events#Event_stream_format) as they become available, with the stream terminated by a `data: [DONE]` message.
3635
///
37-
/// [ChatResponseStream] is a parsed SSE stream until a \[DONE\] is received from server.
36+
/// [ChatCompletionResponseStream] is a parsed SSE stream until a \[DONE\] is received from server.
3837
pub async fn create_stream(
3938
&self,
40-
mut request: CreateChatRequest,
41-
) -> Result<ChatResponseStream, OpenAIError> {
39+
mut request: CreateChatCompletionRequest,
40+
) -> Result<ChatCompletionResponseStream, OpenAIError> {
4241
if request.stream.is_some() && !request.stream.unwrap() {
4342
return Err(OpenAIError::InvalidArgument(
4443
"When stream is false, use Chat::create".into(),

async-openai/src/client.rs

Lines changed: 6 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -6,13 +6,12 @@ use reqwest_eventsource::{Event, EventSource, RequestBuilderExt};
66
use serde::{de::DeserializeOwned, Serialize};
77

88
use crate::{
9-
chat::Chat,
109
edit::Edits,
1110
error::{OpenAIError, WrappedError},
1211
file::Files,
1312
image::Images,
1413
moderation::Moderations,
15-
Completions, Embeddings, FineTunes, Models,
14+
Audio, Chat, Completions, Embeddings, FineTunes, Models,
1615
};
1716

1817
#[derive(Debug, Clone)]
@@ -127,6 +126,11 @@ impl Client {
127126
Embeddings::new(self)
128127
}
129128

129+
/// To call [Audio] group related APIs using this client.
130+
pub fn audio(&self) -> Audio {
131+
Audio::new(self)
132+
}
133+
130134
fn headers(&self) -> HeaderMap {
131135
let mut headers = HeaderMap::new();
132136
if !self.org_id.is_empty() {

async-openai/src/image.rs

Lines changed: 5 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -30,13 +30,16 @@ impl<'c> Images<'c> {
3030
request: CreateImageEditRequest,
3131
) -> Result<ImageResponse, OpenAIError> {
3232
let image_part = create_file_part(&request.image.path).await?;
33-
let mask_part = create_file_part(&request.mask.path).await?;
3433

3534
let mut form = reqwest::multipart::Form::new()
3635
.part("image", image_part)
37-
.part("mask", mask_part)
3836
.text("prompt", request.prompt);
3937

38+
if let Some(mask) = request.mask {
39+
let mask_part = create_file_part(&mask.path).await?;
40+
form = form.part("mask", mask_part);
41+
}
42+
4043
if request.n.is_some() {
4144
form = form.text("n", request.n.unwrap().to_string())
4245
}

async-openai/src/lib.rs

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,7 @@
4848
//! ## Examples
4949
//! For full working examples for all supported features see [examples](https://github.com/64bit/async-openai/tree/main/examples) directory in the repository.
5050
//!
51+
mod audio;
5152
mod chat;
5253
mod client;
5354
mod completion;
@@ -63,6 +64,8 @@ mod moderation;
6364
pub mod types;
6465
mod util;
6566

67+
pub use audio::Audio;
68+
pub use chat::Chat;
6669
pub use client::Client;
6770
pub use client::API_BASE;
6871
pub use client::ORGANIZATION_HEADER;

async-openai/src/types/impls.rs

Lines changed: 33 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,8 @@ use crate::{
99
};
1010

1111
use super::{
12-
EmbeddingInput, FileInput, ImageData, ImageInput, ImageResponse, ImageSize, ModerationInput,
13-
Prompt, ResponseFormat, Stop,
12+
AudioInput, AudioResponseFormat, EmbeddingInput, FileInput, ImageData, ImageInput,
13+
ImageResponse, ImageSize, ModerationInput, Prompt, ResponseFormat, Role, Stop,
1414
};
1515

1616
macro_rules! impl_from {
@@ -89,6 +89,7 @@ macro_rules! file_path_input {
8989

9090
file_path_input!(ImageInput);
9191
file_path_input!(FileInput);
92+
file_path_input!(AudioInput);
9293

9394
impl Display for ImageSize {
9495
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
@@ -117,6 +118,36 @@ impl Display for ResponseFormat {
117118
}
118119
}
119120

121+
impl Display for AudioResponseFormat {
122+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
123+
write!(
124+
f,
125+
"{}",
126+
match self {
127+
AudioResponseFormat::Json => "json",
128+
AudioResponseFormat::Srt => "srt",
129+
AudioResponseFormat::Text => "text",
130+
AudioResponseFormat::VerboseJson => "verbose_json",
131+
AudioResponseFormat::Vtt => "vtt",
132+
}
133+
)
134+
}
135+
}
136+
137+
impl Display for Role {
138+
fn fmt(&self, f: &mut std::fmt::Formatter<'_>) -> std::fmt::Result {
139+
write!(
140+
f,
141+
"{}",
142+
match self {
143+
Role::User => "user",
144+
Role::System => "system",
145+
Role::Assistant => "assistant",
146+
}
147+
)
148+
}
149+
}
150+
120151
impl ImageResponse {
121152
/// Save each image in a dedicated Tokio task and return paths to saved files.
122153
/// For [ResponseFormat::Url] each file is downloaded in dedicated Tokio task.

0 commit comments

Comments
 (0)