diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ErrorContent.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ErrorContent.cs index ceca3002f88..4588531262b 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ErrorContent.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/ErrorContent.cs @@ -2,8 +2,7 @@ // The .NET Foundation licenses this file to you under the MIT license. using System.Diagnostics; -using System.Text.Json.Serialization; -using Microsoft.Shared.Diagnostics; +using System.Diagnostics.CodeAnalysis; namespace Microsoft.Extensions.AI; @@ -16,33 +15,33 @@ namespace Microsoft.Extensions.AI; public class ErrorContent : AIContent { /// The error message. - private string _message; + private string? _message; - /// Initializes a new instance of the class with the specified message. - /// The message to store in this content. - [JsonConstructor] - public ErrorContent(string message) + /// Initializes a new instance of the class with the specified error message. + /// The error message to store in this content. + public ErrorContent(string? message) { - _message = Throw.IfNull(message); + _message = message; } /// Gets or sets the error message. + [AllowNull] public string Message { - get => _message; - set => _message = Throw.IfNull(value); + get => _message ?? string.Empty; + set => _message = value; } - /// Gets or sets the error code. + /// Gets or sets an error code associated with the error. public string? ErrorCode { get; set; } - /// Gets or sets the error details. + /// Gets or sets additional details about the error. public string? Details { get; set; } /// Gets a string representing this instance to display in the debugger. [DebuggerBrowsable(DebuggerBrowsableState.Never)] private string DebuggerDisplay => - $"Error = {Message}" + - (ErrorCode is not null ? $" ({ErrorCode})" : string.Empty) + - (Details is not null ? $" - {Details}" : string.Empty); + $"Error = \"{Message}\"" + + (!string.IsNullOrWhiteSpace(ErrorCode) ? $" ({ErrorCode})" : string.Empty) + + (!string.IsNullOrWhiteSpace(Details) ? $" - \"{Details}\"" : string.Empty); } diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs index 566aac8fa63..70768f07caa 100644 --- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs @@ -132,6 +132,11 @@ public async Task GetResponseAsync( break; } } + + if (openAIResponse.Error is { } error) + { + message.Contents.Add(new ErrorContent(error.Message) { ErrorCode = error.Code }); + } } return response; @@ -246,6 +251,24 @@ public async IAsyncEnumerable GetStreamingResponseAsync( break; } + + case StreamingResponseErrorUpdate errorUpdate: + yield return new ChatResponseUpdate + { + CreatedAt = createdAt, + MessageId = lastMessageId, + ModelId = modelId, + ResponseId = responseId, + Contents = + [ + new ErrorContent(errorUpdate.Message) + { + ErrorCode = errorUpdate.Code, + Details = errorUpdate.Param, + } + ], + }; + break; } } } diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ErrorContentTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ErrorContentTests.cs index 2564f6bc2c9..8744636b6ec 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ErrorContentTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/Contents/ErrorContentTests.cs @@ -8,6 +8,19 @@ namespace Microsoft.Extensions.AI; public class ErrorContentTests { + [Fact] + public void Constructor_NormalizesNullToEmpty() + { + ErrorContent content = new(null!); + Assert.Empty(content.Message); + + content.Message = "test"; + Assert.Equal("test", content.Message); + + content.Message = null!; + Assert.Empty(content.Message); + } + [Fact] public void Constructor_ShouldInitializeProperties() {