Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Adding a minimal Azure OpenAI Inference SDK. #1813

Draft
wants to merge 71 commits into
base: main
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
6709010
New inference crate builds
jpalvarezl Aug 29, 2024
ae57b1b
Added more dependencies
jpalvarezl Aug 29, 2024
897078b
Internal state of clients is setup
jpalvarezl Aug 30, 2024
67e5a12
Somehow hitting the noop client
jpalvarezl Aug 30, 2024
5b5e3a9
getting 200 but no content
jpalvarezl Aug 30, 2024
80e2853
format
jpalvarezl Sep 3, 2024
5157d81
Attempt at using the pipeline
jpalvarezl Sep 6, 2024
4967330
tried implementing custom type as policy and pass in the pipeline
jpalvarezl Sep 6, 2024
817cd3b
Works with policy
jpalvarezl Sep 6, 2024
9056e38
WIP
jpalvarezl Sep 10, 2024
d65edb4
Extracted client_options to a separate module
jpalvarezl Sep 12, 2024
0e9f8e2
Re-organized models
jpalvarezl Sep 12, 2024
060a944
Renamed new to with_key and provided context on request method
jpalvarezl Sep 12, 2024
3417181
Added api service version as a policy
jpalvarezl Sep 12, 2024
d6be9f3
Refactored clients
jpalvarezl Sep 12, 2024
f8b21a8
Added comment for clarity
jpalvarezl Sep 12, 2024
96eec97
More clarity in comments
jpalvarezl Sep 12, 2024
ba1490b
Added client option modules to differenciate between nonAzure and Azure
jpalvarezl Sep 12, 2024
c92cbe5
added module visibility
jpalvarezl Sep 12, 2024
12e61e4
Made auth classes private
jpalvarezl Sep 13, 2024
eff2f89
wip
jpalvarezl Sep 13, 2024
b07cece
cleanup
jpalvarezl Sep 13, 2024
5cd0925
Project compiles and runs, but request errors
jpalvarezl Sep 13, 2024
ff6cba0
Functionality restored
jpalvarezl Sep 13, 2024
bcba5b9
Running state after rebase
jpalvarezl Sep 13, 2024
053b4f7
Running state after rebase
jpalvarezl Sep 13, 2024
a2c1752
Adding comment for clarity
jpalvarezl Sep 13, 2024
35fd48c
OpenAIClient code builds
jpalvarezl Sep 13, 2024
b2fdf31
non-Azure OpenAI works again
jpalvarezl Sep 13, 2024
8836f2b
Old approach to streaming response not working yet
jpalvarezl Sep 13, 2024
e888582
Resolved one issue, but now having ownership problems
jpalvarezl Sep 13, 2024
74ac180
Streaming verified working with Azure
jpalvarezl Sep 13, 2024
69fcf6c
Added example for non Azure usage for streaming
jpalvarezl Sep 13, 2024
acbd3f3
Using correct method in examples for deserialization
jpalvarezl Sep 13, 2024
1e288ef
Added lifetime parameter for string slice to be able to move to lambda
jpalvarezl Sep 17, 2024
f9869ff
Added ctx for AAD and related sample
jpalvarezl Sep 18, 2024
4757c9f
Cleaned up imports
jpalvarezl Sep 18, 2024
af6ab14
Renamed methods to be guideline compliant
jpalvarezl Sep 18, 2024
53d23b0
Restored ChatCompletionStreamHandler struct
jpalvarezl Sep 18, 2024
1465a7b
stream mapping function handles errors better
jpalvarezl Sep 18, 2024
6349265
Ported tests for string_chunks
jpalvarezl Sep 18, 2024
624e988
Made crate level visible the BaseOpenAIClientMethods trait
jpalvarezl Sep 18, 2024
49a19cf
Extracted json request builder method
jpalvarezl Sep 19, 2024
becd225
Added unhappy path test
jpalvarezl Sep 19, 2024
3f8f4cd
Updated comment
jpalvarezl Sep 19, 2024
f471f29
Added crate readme
jpalvarezl Sep 19, 2024
711aa97
Example docs and comments
jpalvarezl Sep 19, 2024
48d7dfa
Added comments to crate::options module
jpalvarezl Sep 19, 2024
cf98e01
Added docs for models and renamed methods
jpalvarezl Sep 19, 2024
32f59ef
restricted visibility of EventHandler trait
jpalvarezl Sep 19, 2024
de711b6
Various visibility restrictions
jpalvarezl Sep 19, 2024
2c1a3c0
Added documentation for auth module
jpalvarezl Sep 19, 2024
a071db5
Documented helpers module
jpalvarezl Sep 19, 2024
cbfa466
wip
jpalvarezl Sep 19, 2024
ba93ea4
Added docs for azure openai client
jpalvarezl Sep 19, 2024
3eb7cac
Added docs for ChatCompletionsClient
jpalvarezl Sep 19, 2024
f22d74e
clients module documented
jpalvarezl Sep 19, 2024
811279b
renamed auth module to credentials
jpalvarezl Sep 30, 2024
b2b8f45
Removed usage of azure_core::Result in examples
jpalvarezl Sep 30, 2024
e427611
crate description correction
jpalvarezl Sep 30, 2024
3802d55
Update sdk/openai/inference/src/lib.rs
jpalvarezl Sep 30, 2024
984645b
request module flattening fixes
jpalvarezl Sep 30, 2024
a82e472
Added license header to source files
jpalvarezl Sep 30, 2024
687858a
rebase new changes to feature/track2 and changed from auth->credentia…
jpalvarezl Sep 30, 2024
611f716
Addressed clippy warnings and errors
jpalvarezl Sep 30, 2024
8f82cd1
Fixed tests in comments
jpalvarezl Sep 30, 2024
bc04acd
No longer holding reference to options in the base clients
jpalvarezl Sep 30, 2024
4240214
Broken struct link fixed
jpalvarezl Sep 30, 2024
fe004b7
Added openai to word list
jpalvarezl Sep 30, 2024
314dc75
More spell checks
jpalvarezl Sep 30, 2024
d148db8
more spell checks
jpalvarezl Sep 30, 2024
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
2 changes: 2 additions & 0 deletions .vscode/cspell.json
Original file line number Diff line number Diff line change
Expand Up @@ -32,13 +32,15 @@
"downcasted",
"downcasting",
"entra",
"endregion",
"etag",
"eventhub",
"eventhubs",
"hmac",
"iothub",
"keyvault",
"msrc",
"openai",
"pageable",
"pkce",
"pkcs",
Expand Down
1 change: 1 addition & 0 deletions Cargo.toml
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@ members = [
"eng/test/mock_transport",
"sdk/storage",
"sdk/storage/azure_storage_blob",
"sdk/openai/inference",
]

[workspace.package]
Expand Down
30 changes: 30 additions & 0 deletions sdk/openai/inference/Cargo.toml
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
[package]
name = "azure_openai_inference"
version = "1.0.0-beta.1"
description = "Rust client library for Azure OpenAI Inference"
readme = "README.md"
authors.workspace = true
edition.workspace = true
license.workspace = true
repository.workspace = true
rust-version.workspace = true
keywords = ["sdk", "azure", "rest"]
categories = ["api-bindings"]

[lints]
workspace = true

[dependencies]
azure_core = { workspace = true }
serde = { workspace = true }
serde_json = { workspace = true }
async-trait = { workspace = true }
futures = { workspace = true }
bytes = { workspace = true }
typespec_client_core = { workspace = true, features = ["derive"] }

[dev-dependencies]
azure_core = { workspace = true, features = ["reqwest"] }
azure_identity = { workspace = true }
reqwest = { workspace = true }
tokio = { workspace = true }
39 changes: 39 additions & 0 deletions sdk/openai/inference/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
# Azure OpenAI Inference SDK for Rust

## Introduction

This SDK provides Rust types to interact with both OpenAI and Azure OpenAI services.

Note: Currently request and response models have as few fields as possible, leveraging the server side defaults wherever they can.

### Features

All features are showcased in the `example` folder of this crate. The following is a list of what is currently supported:

- Supporting both usage with OpenAI and Azure OpenAI services by using `OpenAIClient` or `AzureOpenAIClient`, respectively.
- Key credential authentication is supported.
- [Azure Only] Azure Active Directory (AAD) authentication is supported.
- `ChatCompletions` operation supported (limited fields).
- Streaming for `ChatCompletions` is supported

## Authentication methods

### Azure Active Directory

This authentication method is only supported for Azure OpenAI services.

```rust
AzureOpenAIClient::new(
endpoint,
Arc::new(DefaultAzureCredentialBuilder::new().build()?),
None,
)?
```

### Key Credentials

This method of authentication is supported both for Azure and non-Azure OpenAI services.

```rust
OpenAIClient::with_key_credential(secret, None)?
```
48 changes: 48 additions & 0 deletions sdk/openai/inference/examples/azure_chat_completions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
use azure_openai_inference::{
clients::{AzureOpenAIClient, AzureOpenAIClientMethods, ChatCompletionsClientMethods},
AzureOpenAIClientOptions, AzureServiceVersion, CreateChatCompletionsRequest,
};

// This example illustrates how to use Azure OpenAI with key credential authentication to generate a chat completion.
#[tokio::main]
pub async fn main() {
let endpoint =
std::env::var("AZURE_OPENAI_ENDPOINT").expect("Set AZURE_OPENAI_ENDPOINT env variable");
let secret = std::env::var("AZURE_OPENAI_KEY").expect("Set AZURE_OPENAI_KEY env variable");

let chat_completions_client = AzureOpenAIClient::with_key_credential(
endpoint,
secret,
Some(
AzureOpenAIClientOptions::builder()
.with_api_version(AzureServiceVersion::V2023_12_01Preview)
.build(),
),
)
.unwrap()
.chat_completions_client();

let chat_completions_request = CreateChatCompletionsRequest::with_user_message(
"gpt-4-1106-preview",
"Tell me a joke about pineapples",
);

let response = chat_completions_client
.create_chat_completions(&chat_completions_request.model, &chat_completions_request)
.await;

match response {
Ok(chat_completions_response) => {
let chat_completions = chat_completions_response
.deserialize_body()
.await
.expect("Failed to deserialize response");
println!("{:#?}", &chat_completions);
}
Err(e) => {
println!("Error: {}", e);
}
};
}
50 changes: 50 additions & 0 deletions sdk/openai/inference/examples/azure_chat_completions_aad.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
use azure_identity::DefaultAzureCredentialBuilder;
use azure_openai_inference::{
clients::{AzureOpenAIClient, AzureOpenAIClientMethods, ChatCompletionsClientMethods},
AzureOpenAIClientOptions, AzureServiceVersion, CreateChatCompletionsRequest,
};

/// This example illustrates how to use Azure OpenAI Chat Completions with Azure Active Directory authentication.
#[tokio::main]
async fn main() {
let endpoint =
std::env::var("AZURE_OPENAI_ENDPOINT").expect("Set AZURE_OPENAI_ENDPOINT env variable");

let chat_completions_client = AzureOpenAIClient::new(
endpoint,
DefaultAzureCredentialBuilder::new()
.build()
.expect("Failed to create Azure credential"),
Some(
AzureOpenAIClientOptions::builder()
.with_api_version(AzureServiceVersion::V2023_12_01Preview)
.build(),
),
)
.unwrap()
.chat_completions_client();

let chat_completions_request = CreateChatCompletionsRequest::with_user_message(
"gpt-4-1106-preview",
"Tell me a joke about pineapples",
);

let response = chat_completions_client
.create_chat_completions(&chat_completions_request.model, &chat_completions_request)
.await;

match response {
Ok(chat_completions_response) => {
let chat_completions = chat_completions_response
.deserialize_body()
.await
.expect("Failed to deserialize response");
println!("{:#?}", &chat_completions);
}
Err(e) => {
println!("Error: {}", e);
}
};
}
57 changes: 57 additions & 0 deletions sdk/openai/inference/examples/azure_chat_completions_stream.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
use azure_openai_inference::{
clients::{AzureOpenAIClient, AzureOpenAIClientMethods, ChatCompletionsClientMethods},
AzureOpenAIClientOptions, AzureServiceVersion, CreateChatCompletionsRequest,
};
use futures::stream::StreamExt;
use std::io::{self, Write};

/// This example illustrates how to use Azure OpenAI with key credential authentication to stream chat completions.
#[tokio::main]
async fn main() {
let endpoint =
std::env::var("AZURE_OPENAI_ENDPOINT").expect("Set AZURE_OPENAI_ENDPOINT env variable");
let secret = std::env::var("AZURE_OPENAI_KEY").expect("Set AZURE_OPENAI_KEY env variable");

let chat_completions_client = AzureOpenAIClient::with_key_credential(
endpoint,
secret,
Some(
AzureOpenAIClientOptions::builder()
.with_api_version(AzureServiceVersion::V2023_12_01Preview)
.build(),
),
)
.unwrap()
.chat_completions_client();

let chat_completions_request = CreateChatCompletionsRequest::with_user_message_and_stream(
"gpt-4-1106-preview",
"Write me an essay that is at least 200 words long on the nutritional values (or lack thereof) of fast food.
Start the essay by stating 'this essay will be x many words long' where x is the number of words in the essay.",);

let response = chat_completions_client
.stream_chat_completions(&chat_completions_request.model, &chat_completions_request)
.await
.unwrap();

// this pins the stream to the stack so it is safe to poll it (namely, it won't be de-allocated or moved)
futures::pin_mut!(response);

while let Some(result) = response.next().await {
match result {
Ok(delta) => {
if let Some(choice) = delta.choices.get(0) {
choice.delta.as_ref().map(|d| {
d.content.as_ref().map(|c| {
print!("{}", c);
let _ = io::stdout().flush();
});
});
}
}
Err(e) => println!("Error: {:?}", e),
}
}
}
38 changes: 38 additions & 0 deletions sdk/openai/inference/examples/chat_completions.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
use azure_openai_inference::{
clients::{ChatCompletionsClientMethods, OpenAIClient, OpenAIClientMethods},
CreateChatCompletionsRequest,
};

/// This example illustrates how to use OpenAI to generate a chat completion.
#[tokio::main]
pub async fn main() {
let secret = std::env::var("OPENAI_KEY").expect("Set OPENAI_KEY env variable");

let chat_completions_client = OpenAIClient::with_key_credential(secret, None)
.unwrap()
.chat_completions_client();

let chat_completions_request = CreateChatCompletionsRequest::with_user_message(
"gpt-3.5-turbo-1106",
"Tell me a joke about pineapples",
);

let response = chat_completions_client
.create_chat_completions(&chat_completions_request.model, &chat_completions_request)
.await;

match response {
Ok(chat_completions_response) => {
let chat_completions = chat_completions_response
.deserialize_body()
.await
.expect("Failed to deserialize response");
println!("{:#?}", &chat_completions);
}
Err(e) => {
println!("Error: {}", e);
}
};
}
47 changes: 47 additions & 0 deletions sdk/openai/inference/examples/chat_completions_stream.rs
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
// Copyright (c) Microsoft Corporation. All rights reserved.
// Licensed under the MIT License.
use azure_openai_inference::{
clients::{ChatCompletionsClientMethods, OpenAIClient, OpenAIClientMethods},
CreateChatCompletionsRequest,
};
use futures::stream::StreamExt;
use std::io::{self, Write};

/// This example illustrates how to use OpenAI to stream chat completions.
#[tokio::main]
async fn main() {
let secret = std::env::var("OPENAI_KEY").expect("Set OPENAI_KEY env variable");

let chat_completions_client = OpenAIClient::with_key_credential(secret, None)
.unwrap()
.chat_completions_client();

let chat_completions_request = CreateChatCompletionsRequest::with_user_message_and_stream(
"gpt-3.5-turbo-1106",
"Write me an essay that is at least 200 words long on the nutritional values (or lack thereof) of fast food.
Start the essay by stating 'this essay will be x many words long' where x is the number of words in the essay.",);

let response = chat_completions_client
.stream_chat_completions(&chat_completions_request.model, &chat_completions_request)
.await
.unwrap();

// this pins the stream to the stack so it is safe to poll it (namely, it won't be de-allocated or moved)
futures::pin_mut!(response);

while let Some(result) = response.next().await {
match result {
Ok(delta) => {
if let Some(choice) = delta.choices.get(0) {
choice.delta.as_ref().map(|d| {
d.content.as_ref().map(|c| {
print!("{}", c);
let _ = io::stdout().flush();
});
});
}
}
Err(e) => println!("Error: {:?}", e),
}
}
}
Loading