Skip to content

Commit

Permalink
desquished complete
Browse files Browse the repository at this point in the history
  • Loading branch information
trrwilson committed Jan 16, 2025
1 parent 449431a commit 3c540dc
Show file tree
Hide file tree
Showing 34 changed files with 517 additions and 595 deletions.
8 changes: 3 additions & 5 deletions .dotnet/CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,9 @@

- Chat completion now supports audio input and output!
- To configure a chat completion to request audio output using the `gpt-4o-audio-preview` model, create a `ChatAudioOptions` instance and provide it on `ChatCompletionOptions.AudioOptions`.
- Audio is always represented as a `ChatMessageContentPart`:
- User audio input can be instantiated via `ChatMessageContentPart.CreateAudioPart(BinaryData, ChatAudioInputFormat)` and will populate the `AudioBytes` and `AudioInputFormat` properties on `ChatMessageContentPart`
- Response audio associated with the items in `Content` of a `ChatCompletion` or `ContentUpdate` of a `StreamingChatCompletionUpdate` will populate the `AudioBytes`, `AudioTranscript`, `AudioExpiresAt`, and `AudioCorrelationId` properties
- Audio referring to a previous response's output can be created via `ChatMessageContentPart.CreateAudioPart(string)` and will populate the `AudioCorrelationId` property.
- The `AssistantChatMessage(IEnumerable<ChatMessageContentPart>)` and `AssistantChatMessage(ChatCompletion)` constructors will automatically infer `AudioCorrelationId`, simplifying conversation history management
- Input chat audio is provided to `UserChatMessage` instances using `ChatContentPart.CreateInputAudioPart()`
- Output chat audio is provided on the `ResponseAudio` property of `ChatCompletion`
- References to prior assistant audio are provided via `ResponseAudioReference` instances on the `AudioReference` property of `AssistantChatMessage`; `AssistantChatMessage(chatCompletion)` will automatically handle this, too
- For more information, see the example in the README

## 2.1.0 (2024-12-04)
Expand Down
23 changes: 9 additions & 14 deletions .dotnet/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -389,28 +389,23 @@ ChatCompletion completion = client.CompleteChat(messages, options);

void PrintAudioContent()
{
foreach (ChatMessageContentPart contentPart in completion.Content)
if (completion.ResponseAudio is ChatResponseAudio responseAudio)
{
if (contentPart.AudioCorrelationId is not null)
Console.WriteLine($"Response audio transcript: {responseAudio.Transcript}");
string outputFilePath = $"{responseAudio.Id}.mp3";
using (FileStream outputFileStream = File.OpenWrite(outputFilePath))
{
Console.WriteLine($"Response audio transcript: {contentPart.AudioTranscript}");

string outputFilePath = $"{contentPart.AudioCorrelationId}.mp3";
using (FileStream outputFileStream = File.OpenWrite(outputFilePath))
{
outputFileStream.Write(contentPart.AudioBytes);
}
Console.WriteLine($"Response audio written to file: {outputFilePath}");
Console.WriteLine($"Valid on followup requests until: {contentPart.AudioExpiresAt}");
outputFileStream.Write(responseAudio.Data);
}
Console.WriteLine($"Response audio written to file: {outputFilePath}");
Console.WriteLine($"Valid on followup requests until: {responseAudio.ExpiresAt}");
}
}

PrintAudioContent();

// To refer to past audio output, create an assistant message from the earlier ChatCompletion, use the earlier
// response content part, or use ChatMessageContentPart.CreateAudioPart(string) to manually instantiate a part.
// To refer to past audio output, create an assistant message from the earlier ChatCompletion or instantiate a
// ChatResponseAudioReference(string) from the .Id of the completion's .ResponseAudio property.
messages.Add(new AssistantChatMessage(completion));
messages.Add("Can you say that like a pirate?");

Expand Down
30 changes: 23 additions & 7 deletions .dotnet/api/OpenAI.netstandard2.0.cs
Original file line number Diff line number Diff line change
Expand Up @@ -1131,13 +1131,15 @@ public class AssistantChatMessage : ChatMessage, IJsonModel<AssistantChatMessage
[Obsolete("This constructor is obsolete. Please use the constructor that takes an IEnumerable<ChatToolCall> parameter instead.")]
public AssistantChatMessage(ChatFunctionCall functionCall);
public AssistantChatMessage(params ChatMessageContentPart[] contentParts);
public AssistantChatMessage(ChatResponseAudioReference responseAudioReference);
public AssistantChatMessage(IEnumerable<ChatMessageContentPart> contentParts);
public AssistantChatMessage(IEnumerable<ChatToolCall> toolCalls);
public AssistantChatMessage(string content);
[Obsolete("This property is obsolete. Please use ToolCalls instead.")]
public ChatFunctionCall FunctionCall { get; set; }
public string ParticipantName { get; set; }
public string Refusal { get; set; }
public ChatResponseAudioReference ResponseAudioReference { get; set; }
public IList<ChatToolCall> ToolCalls { get; }
protected override ChatMessage JsonModelCreateCore(ref Utf8JsonReader reader, ModelReaderWriterOptions options);
protected override void JsonModelWriteCore(Utf8JsonWriter writer, ModelReaderWriterOptions options);
Expand Down Expand Up @@ -1184,6 +1186,7 @@ public class ChatCompletion : IJsonModel<ChatCompletion>, IPersistableModel<Chat
public string Model { get; }
public string Refusal { get; }
public IReadOnlyList<ChatTokenLogProbabilityDetails> RefusalTokenLogProbabilities { get; }
public ChatResponseAudio ResponseAudio { get; }
public ChatMessageRole Role { get; }
public string SystemFingerprint { get; }
public IReadOnlyList<ChatToolCall> ToolCalls { get; }
Expand Down Expand Up @@ -1289,6 +1292,7 @@ public class ChatMessage : IJsonModel<ChatMessage>, IPersistableModel<ChatMessag
public static AssistantChatMessage CreateAssistantMessage(ChatCompletion chatCompletion);
public static AssistantChatMessage CreateAssistantMessage(ChatFunctionCall functionCall);
public static AssistantChatMessage CreateAssistantMessage(params ChatMessageContentPart[] contentParts);
public static AssistantChatMessage CreateAssistantMessage(ChatResponseAudioReference audioReference);
public static AssistantChatMessage CreateAssistantMessage(IEnumerable<ChatMessageContentPart> contentParts);
public static AssistantChatMessage CreateAssistantMessage(IEnumerable<ChatToolCall> toolCalls);
public static AssistantChatMessage CreateAssistantMessage(string content);
Expand All @@ -1315,21 +1319,17 @@ public class ChatMessageContent : ObjectModel.Collection<ChatMessageContentPart>
}
public class ChatMessageContentPart : IJsonModel<ChatMessageContentPart>, IPersistableModel<ChatMessageContentPart> {
public BinaryData AudioBytes { get; }
public string AudioCorrelationId { get; }
public DateTimeOffset? AudioExpiresAt { get; }
public ChatInputAudioFormat? AudioInputFormat { get; }
public string AudioTranscript { get; }
public BinaryData ImageBytes { get; }
public string ImageBytesMediaType { get; }
public ChatImageDetailLevel? ImageDetailLevel { get; }
public Uri ImageUri { get; }
public ChatMessageContentPartKind Kind { get; }
public string Refusal { get; }
public string Text { get; }
public static ChatMessageContentPart CreateAudioPart(BinaryData audioBytes, ChatInputAudioFormat audioFormat);
public static ChatMessageContentPart CreateAudioPart(string audioCorrelationId);
public static ChatMessageContentPart CreateImagePart(BinaryData imageBytes, string imageBytesMediaType, ChatImageDetailLevel? imageDetailLevel = null);
public static ChatMessageContentPart CreateImagePart(Uri imageUri, ChatImageDetailLevel? imageDetailLevel = null);
public static ChatMessageContentPart CreateInputAudioPart(BinaryData audioBytes, ChatInputAudioFormat audioFormat);
public static ChatMessageContentPart CreateRefusalPart(string refusal);
public static ChatMessageContentPart CreateTextPart(string text);
public static explicit operator ChatMessageContentPart(ClientResult result);
Expand Down Expand Up @@ -1372,6 +1372,20 @@ public class ChatOutputTokenUsageDetails : IJsonModel<ChatOutputTokenUsageDetail
public static explicit operator ChatOutputTokenUsageDetails(ClientResult result);
public static implicit operator BinaryContent(ChatOutputTokenUsageDetails chatOutputTokenUsageDetails);
}
public class ChatResponseAudio : IJsonModel<ChatResponseAudio>, IPersistableModel<ChatResponseAudio> {
public BinaryData Data { get; }
public DateTimeOffset ExpiresAt { get; }
public string Id { get; }
public string Transcript { get; }
public static explicit operator ChatResponseAudio(ClientResult result);
public static implicit operator BinaryContent(ChatResponseAudio chatResponseAudio);
}
public class ChatResponseAudioReference : IJsonModel<ChatResponseAudioReference>, IPersistableModel<ChatResponseAudioReference> {
public ChatResponseAudioReference(string id);
public string Id { get; }
public static explicit operator ChatResponseAudioReference(ClientResult result);
public static implicit operator BinaryContent(ChatResponseAudioReference chatResponseAudioReference);
}
public class ChatResponseFormat : IJsonModel<ChatResponseFormat>, IPersistableModel<ChatResponseFormat> {
public static ChatResponseFormat CreateJsonObjectFormat();
public static ChatResponseFormat CreateJsonSchemaFormat(string jsonSchemaFormatName, BinaryData jsonSchema, string jsonSchemaFormatDescription = null, bool? jsonSchemaIsStrict = null);
Expand Down Expand Up @@ -1466,13 +1480,14 @@ public class FunctionChatMessage : ChatMessage, IJsonModel<FunctionChatMessage>,
protected override BinaryData PersistableModelWriteCore(ModelReaderWriterOptions options);
}
public static class OpenAIChatModelFactory {
public static ChatCompletion ChatCompletion(string id = null, ChatFinishReason finishReason = ChatFinishReason.Stop, ChatMessageContent content = null, string refusal = null, IEnumerable<ChatToolCall> toolCalls = null, ChatMessageRole role = ChatMessageRole.System, ChatFunctionCall functionCall = null, IEnumerable<ChatTokenLogProbabilityDetails> contentTokenLogProbabilities = null, IEnumerable<ChatTokenLogProbabilityDetails> refusalTokenLogProbabilities = null, DateTimeOffset createdAt = default, string model = null, string systemFingerprint = null, ChatTokenUsage usage = null, BinaryData audioBytes = null, string audioCorrelationId = null, string audioTranscript = null, DateTimeOffset? audioExpiresAt = null);
public static ChatCompletion ChatCompletion(string id = null, ChatFinishReason finishReason = ChatFinishReason.Stop, ChatMessageContent content = null, string refusal = null, IEnumerable<ChatToolCall> toolCalls = null, ChatMessageRole role = ChatMessageRole.System, ChatFunctionCall functionCall = null, IEnumerable<ChatTokenLogProbabilityDetails> contentTokenLogProbabilities = null, IEnumerable<ChatTokenLogProbabilityDetails> refusalTokenLogProbabilities = null, DateTimeOffset createdAt = default, string model = null, string systemFingerprint = null, ChatTokenUsage usage = null, ChatResponseAudio responseAudio = null);
public static ChatInputTokenUsageDetails ChatInputTokenUsageDetails(int audioTokenCount = 0, int cachedTokenCount = 0);
public static ChatOutputTokenUsageDetails ChatOutputTokenUsageDetails(int reasoningTokenCount = 0, int audioTokenCount = 0);
public static ChatResponseAudio ChatResponseAudio(BinaryData data, string id = null, string transcript = null, DateTimeOffset expiresAt = default);
public static ChatTokenLogProbabilityDetails ChatTokenLogProbabilityDetails(string token = null, float logProbability = 0, ReadOnlyMemory<byte>? utf8Bytes = null, IEnumerable<ChatTokenTopLogProbabilityDetails> topLogProbabilities = null);
public static ChatTokenTopLogProbabilityDetails ChatTokenTopLogProbabilityDetails(string token = null, float logProbability = 0, ReadOnlyMemory<byte>? utf8Bytes = null);
public static ChatTokenUsage ChatTokenUsage(int outputTokenCount = 0, int inputTokenCount = 0, int totalTokenCount = 0, ChatOutputTokenUsageDetails outputTokenDetails = null, ChatInputTokenUsageDetails inputTokenDetails = null);
public static StreamingChatCompletionUpdate StreamingChatCompletionUpdate(string completionId = null, ChatMessageContent contentUpdate = null, StreamingChatFunctionCallUpdate functionCallUpdate = null, IEnumerable<StreamingChatToolCallUpdate> toolCallUpdates = null, ChatMessageRole? role = null, string refusalUpdate = null, IEnumerable<ChatTokenLogProbabilityDetails> contentTokenLogProbabilities = null, IEnumerable<ChatTokenLogProbabilityDetails> refusalTokenLogProbabilities = null, ChatFinishReason? finishReason = null, DateTimeOffset createdAt = default, string model = null, string systemFingerprint = null, ChatTokenUsage usage = null, string audioCorrelationId = null, string audioTranscript = null, BinaryData audioBytes = null, DateTimeOffset? audioExpiresAt = null);
public static StreamingChatCompletionUpdate StreamingChatCompletionUpdate(string completionId = null, ChatMessageContent contentUpdate = null, StreamingChatFunctionCallUpdate functionCallUpdate = null, IEnumerable<StreamingChatToolCallUpdate> toolCallUpdates = null, ChatMessageRole? role = null, string refusalUpdate = null, IEnumerable<ChatTokenLogProbabilityDetails> contentTokenLogProbabilities = null, IEnumerable<ChatTokenLogProbabilityDetails> refusalTokenLogProbabilities = null, ChatFinishReason? finishReason = null, DateTimeOffset createdAt = default, string model = null, string systemFingerprint = null, ChatTokenUsage usage = null, ChatResponseAudio responseAudio = null);
[Obsolete("This class is obsolete. Please use StreamingChatToolCallUpdate instead.")]
public static StreamingChatFunctionCallUpdate StreamingChatFunctionCallUpdate(string functionName = null, BinaryData functionArgumentsUpdate = null);
public static StreamingChatToolCallUpdate StreamingChatToolCallUpdate(int index = 0, string toolCallId = null, ChatToolCallKind kind = ChatToolCallKind.Function, string functionName = null, BinaryData functionArgumentsUpdate = null);
Expand All @@ -1488,6 +1503,7 @@ public class StreamingChatCompletionUpdate : IJsonModel<StreamingChatCompletionU
public string Model { get; }
public IReadOnlyList<ChatTokenLogProbabilityDetails> RefusalTokenLogProbabilities { get; }
public string RefusalUpdate { get; }
public ChatResponseAudio ResponseAudio { get; }
public ChatMessageRole? Role { get; }
public string SystemFingerprint { get; }
public IReadOnlyList<StreamingChatToolCallUpdate> ToolCallUpdates { get; }
Expand Down
20 changes: 8 additions & 12 deletions .dotnet/examples/Chat/Example09_ChatWithAudio.cs
Original file line number Diff line number Diff line change
Expand Up @@ -20,7 +20,7 @@ public void Example09_ChatWithAudio()
BinaryData audioData = BinaryData.FromBytes(audioFileRawBytes);
List<ChatMessage> messages =
[
new UserChatMessage(ChatMessageContentPart.CreateAudioPart(audioData, ChatInputAudioFormat.Wav)),
new UserChatMessage(ChatMessageContentPart.CreateInputAudioPart(audioData, ChatInputAudioFormat.Wav)),
];

// Output audio is requested by configuring AudioOptions on ChatCompletionOptions
Expand All @@ -33,20 +33,16 @@ public void Example09_ChatWithAudio()

void PrintAudioContent()
{
foreach (ChatMessageContentPart contentPart in completion.Content)
if (completion.ResponseAudio is ChatResponseAudio responseAudio)
{
if (contentPart.AudioCorrelationId is not null)
Console.WriteLine($"Response audio transcript: {responseAudio.Transcript}");
string outputFilePath = $"{responseAudio.Id}.mp3";
using (FileStream outputFileStream = File.OpenWrite(outputFilePath))
{
Console.WriteLine($"Response audio transcript: {contentPart.AudioTranscript}");

string outputFilePath = $"{contentPart.AudioCorrelationId}.mp3";
using (FileStream outputFileStream = File.OpenWrite(outputFilePath))
{
outputFileStream.Write(contentPart.AudioBytes);
}
Console.WriteLine($"Response audio written to file: {outputFilePath}");
Console.WriteLine($"Valid on followup requests until: {contentPart.AudioExpiresAt}");
outputFileStream.Write(responseAudio.Data);
}
Console.WriteLine($"Response audio written to file: {outputFilePath}");
Console.WriteLine($"Valid on followup requests until: {responseAudio.ExpiresAt}");
}
}

Expand Down
20 changes: 8 additions & 12 deletions .dotnet/examples/Chat/Example10_ChatWithAudioAsync.cs
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,7 @@ public async Task Example09_ChatWithAudioAsync()
BinaryData audioData = BinaryData.FromBytes(audioFileRawBytes);
List<ChatMessage> messages =
[
new UserChatMessage(ChatMessageContentPart.CreateAudioPart(audioData, ChatInputAudioFormat.Wav)),
new UserChatMessage(ChatMessageContentPart.CreateInputAudioPart(audioData, ChatInputAudioFormat.Wav)),
];

// Output audio is requested by configuring AudioOptions on ChatCompletionOptions
Expand All @@ -34,20 +34,16 @@ public async Task Example09_ChatWithAudioAsync()

async Task PrintAudioContentAsync()
{
foreach (ChatMessageContentPart contentPart in completion.Content)
if (completion.ResponseAudio is ChatResponseAudio responseAudio)
{
if (contentPart.AudioCorrelationId is not null)
Console.WriteLine($"Response audio transcript: {responseAudio.Transcript}");
string outputFilePath = $"{responseAudio.Id}.mp3";
using (FileStream outputFileStream = File.OpenWrite(outputFilePath))
{
Console.WriteLine($"Response audio transcript: {contentPart.AudioTranscript}");

string outputFilePath = $"{contentPart.AudioCorrelationId}.mp3";
using (FileStream outputFileStream = File.OpenWrite(outputFilePath))
{
await outputFileStream.WriteAsync(contentPart.AudioBytes);
}
Console.WriteLine($"Response audio written to file: {outputFilePath}");
Console.WriteLine($"Valid on followup requests until: {contentPart.AudioExpiresAt}");
await outputFileStream.WriteAsync(responseAudio.Data);
}
Console.WriteLine($"Response audio written to file: {outputFilePath}");
Console.WriteLine($"Valid on followup requests until: {responseAudio.ExpiresAt}");
}
}

Expand Down
Loading

0 comments on commit 3c540dc

Please sign in to comment.