diff --git a/src/libs/OpenAI/Conversions/ChatCompletionRequestUserMessageContentPart.cs b/src/libs/OpenAI/Conversions/ChatCompletionRequestUserMessageContentPart.cs index 6a24aa58..ddee1e01 100644 --- a/src/libs/OpenAI/Conversions/ChatCompletionRequestUserMessageContentPart.cs +++ b/src/libs/OpenAI/Conversions/ChatCompletionRequestUserMessageContentPart.cs @@ -7,8 +7,8 @@ public override string ToString() { return IsText ? Text?.Text ?? string.Empty - : IsImage - ? Image?.ImageUrl.Url ?? string.Empty + : IsImageUrl + ? ImageUrl?.ImageUrl.Url ?? string.Empty : string.Empty; } diff --git a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AllTools.cs b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AllTools.cs index 48345328..4bdb2304 100644 --- a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AllTools.cs +++ b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AllTools.cs @@ -106,7 +106,7 @@ ListMessagesResponse messages Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: "); foreach (ContentItem2 contentItem in message.Content) { - if (contentItem.MessageImageFileObject is {} imageFile) + if (contentItem.ImageFile is {} imageFile) { OpenAIFile imageInfo = await api.Files.RetrieveFileAsync(imageFile.ImageFile.FileId); byte[] imageBytes = await api.Files.DownloadFileAsync(imageFile.ImageFile.FileId); @@ -117,11 +117,11 @@ ListMessagesResponse messages Console.WriteLine($""); } - if (contentItem.MessageImageUrlObject is {} imageUrl) + if (contentItem.ImageUrl is {} imageUrl) { Console.WriteLine($" {imageUrl.ImageUrl.Url}"); } - if (contentItem.MessageTextObject is {} text) + if (contentItem.Text is {} text) { Console.WriteLine($"{text.Text.Value}"); @@ -131,13 +131,13 @@ ListMessagesResponse messages Console.WriteLine(); foreach (AnnotationsItem annotation in text.Text.Annotations) { - if (annotation.MessageContentTextFileCitationObject is {} fileCitation) + if (annotation.FileCitation is {} fileCitation) { Console.WriteLine($"* File citation, file ID: {fileCitation.FileCitation.FileId}"); Console.WriteLine($"* Text to replace: {fileCitation.Text}"); Console.WriteLine($"* Message content index range: {fileCitation.StartIndex}-{fileCitation.EndIndex}"); } - if (annotation.MessageContentTextFilePathObject is {} filePath) + if (annotation.FilePath is {} filePath) { Console.WriteLine($"* File output, new file ID: {filePath.FilePath.FileId}"); Console.WriteLine($"* Text to replace: {filePath.Text}"); @@ -146,7 +146,7 @@ ListMessagesResponse messages } } } - if (contentItem.MessageRefusalObject is {} refusal) + if (contentItem.Refusal is {} refusal) { Console.WriteLine($"Refusal: {refusal.Refusal}"); } @@ -165,7 +165,7 @@ ListMessagesResponse messages Console.WriteLine($"Run step: {step.Status}"); foreach (var toolCall in step.StepDetails.ToolCalls?.ToolCalls ?? []) { - if (toolCall.RunStepDetailsCodeObject is {} codeInterpreter) + if (toolCall.CodeInterpreter is {} codeInterpreter) { Console.WriteLine($" --> Tool call: {codeInterpreter.Type}"); foreach (var output in codeInterpreter.CodeInterpreter.Outputs) @@ -180,7 +180,7 @@ ListMessagesResponse messages } } } - if (toolCall.RunStepDetailsFileSearchObject is {} fileSearch) + if (toolCall.FileSearch is {} fileSearch) { Console.WriteLine($" --> Tool call: {fileSearch.Type}"); foreach (var output in fileSearch.FileSearch.Results ?? []) @@ -188,7 +188,7 @@ ListMessagesResponse messages Console.WriteLine($" --> Output: {output.FileId}"); } } - if (toolCall.RunStepDetailsFunctionObject is {} tool) + if (toolCall.Function is {} tool) { Console.WriteLine($" --> Tool call: {tool.Type}"); } diff --git a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AssistantsWithVision.cs b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AssistantsWithVision.cs index bdf03564..17be8e05 100644 --- a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AssistantsWithVision.cs +++ b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.AssistantsWithVision.cs @@ -1,104 +1,95 @@ -// using System.Diagnostics.CodeAnalysis; -// -// namespace OpenAI.IntegrationTests.Examples; -// -// public partial class Examples -// { -// [Test] -// [Explicit] -// [Experimental("OPENAI_BETA_001")] -// public async Task AssistantsWithVision() -// { -// using var api = GetAuthenticatedClient(); -// -// ImagesResponse appleImage = await api.Images.CreateImageAsync( -// prompt: "picture of apple", -// responseFormat: CreateImageRequestResponseFormat.B64Json); -// byte[] appleBytes = appleImage.Data[0].Bytes; -// -// FileInfo appleFileInfo = new($"{Guid.NewGuid()}.png"); -// -// await File.WriteAllBytesAsync(appleFileInfo.FullName, appleBytes); -// -// Console.WriteLine($"Apple image available at:\n{new Uri(appleFileInfo.FullName).AbsoluteUri}"); -// -// Console.WriteLine($"Orange image available at:\n{new Uri("https://raw.githubusercontent.com/tryAGI/OpenAI/d237b700b03fe9913a6ff53fa623041e87705f2f/assets/orange.png")}"); -// -// OpenAIFile pictureOfAppleFile = await api.Files.CreateFileAsync( -// file: appleBytes, -// filename: appleFileInfo.Name, -// purpose: CreateFileRequestPurpose.Vision); -// -// AssistantObject assistant = await api.Assistants.CreateAssistantAsync( -// model: CreateAssistantRequestModel.Gpt4o, -// instructions: "When asked a question, attempt to answer very concisely. " + -// "Prefer one-sentence answers whenever feasible."); -// -// ThreadObject thread = await api.Assistants.CreateThreadAsync(new CreateThreadRequest -// { -// Messages = [ -// "Hello, assistant! Please compare these two images for me:", -// pictureOfAppleFile, -// new Uri("https://raw.githubusercontent.com/tryAGI/OpenAI/d237b700b03fe9913a6ff53fa623041e87705f2f/assets/orange.png"), -// ] -// }); -// -// var streamingUpdates = api.Assistants.CreateRunAsStreamAsync( -// threadId: thread.Id, -// assistantId: assistant.Id, -// instructions: "When possible, try to sneak in puns if you're asked to compare things."); -// -// await foreach (AssistantStreamEvent streamingUpdate in streamingUpdates) -// { -// if (streamingUpdate.Error is {} error) -// { -// Console.WriteLine("--- Error ---"); -// Console.WriteLine($"Message: {error.Data.Message}"); -// Console.WriteLine($"Code: {error.Data.Code}"); -// Console.WriteLine($"Type: {error.Data.Type}"); -// } -// if (streamingUpdate.Run is {} run) -// { -// if (run.Value1 is { Event: RunStreamEventVariant1Event.ThreadRunCreated }) -// { -// Console.WriteLine("--- Run created! ---"); -// } -// } -// if (streamingUpdate.Message is {} message) -// { -// if (message.Value3 is -// { -// Event: MessageStreamEventVariant3Event.ThreadMessageDelta -// } delta) -// { -// foreach (var deltaVariation in delta.Data.Delta.Content ?? []) -// { -// if (deltaVariation.Value1 is {} imageFile) -// { -// Console.WriteLine(); -// Console.WriteLine(imageFile.ImageFile?.FileId); -// } -// if (deltaVariation.Value2 is {} text) -// { -// Console.Write(text.Text?.Value); -// } -// if (deltaVariation.Value3 is {} refusal) -// { -// Console.WriteLine(); -// Console.WriteLine(refusal.Refusal); -// } -// if (deltaVariation.Value4 is {} imageUrl) -// { -// Console.WriteLine(); -// Console.WriteLine(imageUrl.ImageUrl?.Url); -// } -// } -// } -// } -// } -// -// _ = await api.Files.DeleteFileAsync(pictureOfAppleFile.Id); -// _ = await api.Assistants.DeleteThreadAsync(thread.Id); -// _ = await api.Assistants.DeleteAssistantAsync(assistant.Id); -// } -// } +using System.Diagnostics.CodeAnalysis; + +namespace OpenAI.IntegrationTests.Examples; + +public partial class Examples +{ + [Test] + [Explicit] + [Experimental("OPENAI_BETA_001")] + public async Task AssistantsWithVision() + { + using var api = GetAuthenticatedClient(); + + ImagesResponse appleImage = await api.Images.CreateImageAsync( + prompt: "picture of apple", + responseFormat: CreateImageRequestResponseFormat.B64Json); + byte[] appleBytes = appleImage.Data[0].Bytes; + + FileInfo appleFileInfo = new($"{Guid.NewGuid()}.png"); + + await File.WriteAllBytesAsync(appleFileInfo.FullName, appleBytes); + + Console.WriteLine($"Apple image available at:\n{new Uri(appleFileInfo.FullName).AbsoluteUri}"); + + Console.WriteLine($"Orange image available at:\n{new Uri("https://raw.githubusercontent.com/tryAGI/OpenAI/d237b700b03fe9913a6ff53fa623041e87705f2f/assets/orange.png")}"); + + OpenAIFile pictureOfAppleFile = await api.Files.CreateFileAsync( + file: appleBytes, + filename: appleFileInfo.Name, + purpose: CreateFileRequestPurpose.Vision); + + AssistantObject assistant = await api.Assistants.CreateAssistantAsync( + model: CreateAssistantRequestModel.Gpt4o, + instructions: "When asked a question, attempt to answer very concisely. " + + "Prefer one-sentence answers whenever feasible."); + + ThreadObject thread = await api.Assistants.CreateThreadAsync(new CreateThreadRequest + { + Messages = [ + "Hello, assistant! Please compare these two images for me:", + pictureOfAppleFile, + new Uri("https://raw.githubusercontent.com/tryAGI/OpenAI/d237b700b03fe9913a6ff53fa623041e87705f2f/assets/orange.png"), + ] + }); + + var streamingUpdates = api.Assistants.CreateRunAsStreamAsync( + threadId: thread.Id, + assistantId: assistant.Id, + instructions: "When possible, try to sneak in puns if you're asked to compare things."); + + await foreach (AssistantStreamEvent streamingUpdate in streamingUpdates) + { + if (streamingUpdate.Error is {} error) + { + Console.WriteLine("--- Error ---"); + Console.WriteLine($"Message: {error.Data.Message}"); + Console.WriteLine($"Code: {error.Data.Code}"); + Console.WriteLine($"Type: {error.Data.Type}"); + } + if (streamingUpdate.ThreadRunCreated is not null) + { + Console.WriteLine("--- Run created! ---"); + } + if (streamingUpdate.ThreadMessageDelta is {} delta) + { + foreach (var deltaVariation in delta.Data.Delta.Content ?? []) + { + if (deltaVariation.ImageFile is {} imageFile) + { + Console.WriteLine(); + Console.WriteLine(imageFile.ImageFile?.FileId); + } + if (deltaVariation.Text is {} text) + { + Console.Write(text.Text?.Value); + } + if (deltaVariation.Refusal is {} refusal) + { + Console.WriteLine(); + Console.WriteLine(refusal.Refusal); + } + if (deltaVariation.ImageUrl is {} imageUrl) + { + Console.WriteLine(); + Console.WriteLine(imageUrl.ImageUrl?.Url); + } + } + } + } + + _ = await api.Files.DeleteFileAsync(pictureOfAppleFile.Id); + _ = await api.Assistants.DeleteThreadAsync(thread.Id); + _ = await api.Assistants.DeleteAssistantAsync(assistant.Id); + } +} diff --git a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCalling.cs b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCalling.cs index 3ddd8a05..e89300bc 100644 --- a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCalling.cs +++ b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCalling.cs @@ -73,15 +73,15 @@ ListMessagesResponse messages Console.WriteLine($"[{message.Role.ToString().ToUpper()}]: "); foreach (ContentItem2 contentItem in message.Content) { - if (contentItem.MessageImageFileObject is {} imageFile) + if (contentItem.ImageFile is {} imageFile) { Console.WriteLine($" {imageFile.ImageFile.FileId}"); } - if (contentItem.MessageImageUrlObject is {} imageUrl) + if (contentItem.ImageUrl is {} imageUrl) { Console.WriteLine($" {imageUrl.ImageUrl.Url}"); } - if (contentItem.MessageTextObject is {} text) + if (contentItem.Text is {} text) { Console.WriteLine($"{text.Text.Value}"); @@ -91,13 +91,13 @@ ListMessagesResponse messages Console.WriteLine(); foreach (AnnotationsItem annotation in text.Text.Annotations) { - if (annotation.MessageContentTextFileCitationObject is {} fileCitation) + if (annotation.FileCitation is {} fileCitation) { Console.WriteLine($"* File citation, file ID: {fileCitation.FileCitation.FileId}"); Console.WriteLine($"* Text to replace: {fileCitation.Text}"); Console.WriteLine($"* Message content index range: {fileCitation.StartIndex}-{fileCitation.EndIndex}"); } - if (annotation.MessageContentTextFilePathObject is {} filePath) + if (annotation.FilePath is {} filePath) { Console.WriteLine($"* File output, new file ID: {filePath.FilePath.FileId}"); Console.WriteLine($"* Text to replace: {filePath.Text}"); @@ -106,7 +106,7 @@ ListMessagesResponse messages } } } - if (contentItem.MessageRefusalObject is {} refusal) + if (contentItem.Refusal is {} refusal) { Console.WriteLine($"Refusal: {refusal.Refusal}"); } diff --git a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCallingStreaming.cs b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCallingStreaming.cs index 099e09b0..e7f076ae 100644 --- a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCallingStreaming.cs +++ b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.FunctionCallingStreaming.cs @@ -1,117 +1,104 @@ -// using System.Diagnostics.CodeAnalysis; -// -// namespace OpenAI.IntegrationTests.Examples; -// -// public partial class Examples -// { -// [Test] -// [Explicit] -// [Experimental("OPENAI_BETA_001")] -// public async Task Assistants_FunctionCallingStreaming() -// { -// using var api = GetAuthenticatedClient(); -// -// var service = new FunctionCallingService(); -// IList tools = service.AsTools().AsOpenAiTools(); -// -// AssistantObject assistant = await api.Assistants.CreateAssistantAsync( -// model: CreateAssistantRequestModel.Gpt4o, -// name: "Example: Function Calling", -// instructions: "Don't make assumptions about what values to plug into functions." -// + " Ask for clarification if a user request is ambiguous.", -// tools: tools.Select(x => new ToolsItem2(new AssistantToolsFunction -// { -// Function = x.Function, -// })).ToArray()); -// -// ThreadObject thread = await api.Assistants.CreateThreadAsync(new CreateThreadRequest -// { -// Messages = ["What's the weather like today?"], -// }); -// -// var streamingUpdates = api.Assistants.CreateRunAsStreamAsync( -// threadId: thread.Id, -// assistantId: assistant.Id); -// -// RunObject? currentRun; -// do -// { -// currentRun = null; -// List outputsToSubmit = []; -// -// await foreach (AssistantStreamEvent streamingUpdate in streamingUpdates) -// { -// if (streamingUpdate.Run is {} run) -// { -// if (run.Value1 is { Event: RunStreamEventVariant1Event.ThreadRunCreated, Data: {} newRun }) -// { -// Console.WriteLine("--- Run created! ---"); -// -// currentRun = newRun; -// } -// if (run.Value4 is -// { -// Event: RunStreamEventVariant4Event.ThreadRunRequiresAction, -// Data.RequiredAction: {} requiredAction, -// }) -// { -// foreach (RunToolCallObject toolCall in requiredAction.SubmitToolOutputs.ToolCalls) -// { -// var json = await service.CallAsync( -// functionName: toolCall.Function.Name, -// argumentsAsJson: toolCall.Function.Arguments); -// outputsToSubmit.Add(new SubmitToolOutputsRunRequestToolOutput -// { -// ToolCallId = toolCall.Id, -// Output = json, -// }); -// } -// } -// } -// if (streamingUpdate.Message is {} message) -// { -// if (message.Value3 is -// { -// Event: MessageStreamEventVariant3Event.ThreadMessageDelta -// } delta) -// { -// foreach (var deltaVariation in delta.Data.Delta.Content ?? []) -// { -// if (deltaVariation.Value1 is {} imageFile) -// { -// Console.WriteLine(); -// Console.WriteLine(imageFile.ImageFile?.FileId); -// } -// if (deltaVariation.Value2 is {} text) -// { -// Console.Write(text.Text?.Value); -// } -// if (deltaVariation.Value3 is {} refusal) -// { -// Console.WriteLine(); -// Console.WriteLine(refusal.Refusal); -// } -// if (deltaVariation.Value4 is {} imageUrl) -// { -// Console.WriteLine(); -// Console.WriteLine(imageUrl.ImageUrl?.Url); -// } -// } -// } -// } -// } -// -// if (outputsToSubmit.Count > 0) -// { -// // streamingUpdates = await api.Assistants.SubmitToolOuputsToRunAsStreamAsync( -// // threadId: currentRun.ThreadId, -// // runId: currentRun.Id, -// // outputsToSubmit); -// } -// } -// while (currentRun?.Status is RunObjectStatus.Queued or RunObjectStatus.InProgress or RunObjectStatus.RequiresAction); -// -// _ = await api.Assistants.DeleteThreadAsync(thread.Id); -// _ = await api.Assistants.DeleteAssistantAsync(assistant.Id); -// } -// } +using System.Diagnostics.CodeAnalysis; + +namespace OpenAI.IntegrationTests.Examples; + +public partial class Examples +{ + [Test] + [Explicit] + [Experimental("OPENAI_BETA_001")] + public async Task Assistants_FunctionCallingStreaming() + { + using var api = GetAuthenticatedClient(); + + var service = new FunctionCallingService(); + IList tools = service.AsTools().AsOpenAiTools(); + + AssistantObject assistant = await api.Assistants.CreateAssistantAsync( + model: CreateAssistantRequestModel.Gpt4o, + name: "Example: Function Calling", + instructions: "Don't make assumptions about what values to plug into functions." + + " Ask for clarification if a user request is ambiguous.", + tools: tools.Select(x => new ToolsItem2(new AssistantToolsFunction + { + Function = x.Function, + })).ToArray()); + + ThreadObject thread = await api.Assistants.CreateThreadAsync(new CreateThreadRequest + { + Messages = ["What's the weather like today?"], + }); + + var streamingUpdates = api.Assistants.CreateRunAsStreamAsync( + threadId: thread.Id, + assistantId: assistant.Id); + + RunObject? currentRun; + do + { + currentRun = null; + List outputsToSubmit = []; + + await foreach (AssistantStreamEvent streamingUpdate in streamingUpdates) + { + if (streamingUpdate.ThreadRunCreated is { Data: {} newRun }) + { + Console.WriteLine("--- Run created! ---"); + + currentRun = newRun; + } + if (streamingUpdate.ThreadRunRequiresAction is { Data.RequiredAction: {} requiredAction }) + { + foreach (RunToolCallObject toolCall in requiredAction.SubmitToolOutputs.ToolCalls) + { + var json = await service.CallAsync( + functionName: toolCall.Function.Name, + argumentsAsJson: toolCall.Function.Arguments); + outputsToSubmit.Add(new SubmitToolOutputsRunRequestToolOutput + { + ToolCallId = toolCall.Id, + Output = json, + }); + } + } + if (streamingUpdate.ThreadMessageDelta is {} delta) + { + foreach (var deltaVariation in delta.Data.Delta.Content ?? []) + { + if (deltaVariation.ImageFile is {} imageFile) + { + Console.WriteLine(); + Console.WriteLine(imageFile.ImageFile?.FileId); + } + if (deltaVariation.Text is {} text) + { + Console.Write(text.Text?.Value); + } + if (deltaVariation.Refusal is {} refusal) + { + Console.WriteLine(); + Console.WriteLine(refusal.Refusal); + } + if (deltaVariation.ImageUrl is {} imageUrl) + { + Console.WriteLine(); + Console.WriteLine(imageUrl.ImageUrl?.Url); + } + } + } + } + + if (outputsToSubmit.Count > 0) + { + // streamingUpdates = await api.Assistants.SubmitToolOuputsToRunAsStreamAsync( + // threadId: currentRun.ThreadId, + // runId: currentRun.Id, + // outputsToSubmit); + } + } + while (currentRun?.Status is RunObjectStatus.Queued or RunObjectStatus.InProgress or RunObjectStatus.RequiresAction); + + _ = await api.Assistants.DeleteThreadAsync(thread.Id); + _ = await api.Assistants.DeleteAssistantAsync(assistant.Id); + } +} diff --git a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.RetrievalAugmentedGeneration.cs b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.RetrievalAugmentedGeneration.cs index 46cd5596..73fb9e2b 100644 --- a/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.RetrievalAugmentedGeneration.cs +++ b/src/tests/OpenAI.IntegrationTests/Examples/Examples.Assistants.RetrievalAugmentedGeneration.cs @@ -109,7 +109,7 @@ ListMessagesResponse messages Console.Write($"[{message.Role.ToString().ToUpper()}]: "); foreach (ContentItem2 contentItem in message.Content) { - if (contentItem.MessageTextObject is {} text) + if (contentItem.Text is {} text) { Console.WriteLine($"{text.Text.Value}"); @@ -121,19 +121,19 @@ ListMessagesResponse messages // Include annotations, if any. foreach (AnnotationsItem annotation in text.Text.Annotations) { - if (annotation.MessageContentTextFileCitationObject is {} citation && + if (annotation.FileCitation is {} citation && !string.IsNullOrEmpty(citation.FileCitation.FileId)) { Console.WriteLine($"* File citation, file ID: {citation.FileCitation.FileId}"); } - if (annotation.MessageContentTextFilePathObject is {} path && + if (annotation.FilePath is {} path && !string.IsNullOrEmpty(path.FilePath.FileId)) { Console.WriteLine($"* File output, new file ID: {path.FilePath.FileId}"); } } } - if (contentItem.MessageImageFileObject is {} imageFile) + if (contentItem.ImageFile is {} imageFile) { OpenAIFile imageInfo = await api.Files.RetrieveFileAsync(imageFile.ImageFile.FileId); byte[] imageBytes = await api.Files.DownloadFileAsync(imageFile.ImageFile.FileId);