diff --git a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs index c4a1261844c..f3bcc5e91c5 100644 --- a/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI.OpenAI/OpenAIResponseChatClient.cs @@ -111,10 +111,15 @@ public async Task GetResponseAsync( switch (outputItem) { case MessageResponseItem messageItem: + if (message.MessageId is not null && message.MessageId != messageItem.Id) + { + message = new ChatMessage(); + response.Messages.Add(message); + } + message.MessageId = messageItem.Id; message.RawRepresentation = messageItem; message.Role = ToChatRole(messageItem.Role); - (message.AdditionalProperties ??= []).Add(nameof(messageItem.Id), messageItem.Id); ((List)message.Contents).AddRange(ToAIContents(messageItem.Content)); break; diff --git a/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs b/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs index b98eb89197f..72df6002ca2 100644 --- a/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.OpenAI.Tests/OpenAIResponseClientTests.cs @@ -479,6 +479,122 @@ public async Task ChatOptions_DoNotOverwrite_NotNullPropertiesInRawRepresentatio Assert.Equal("Hello! How can I assist you today?", response.Text); } + [Fact] + public async Task MultipleOutputItems_NonStreaming() + { + const string Input = """ + { + "temperature":0.5, + "model":"gpt-4o-mini", + "input": [{ + "type":"message", + "role":"user", + "content":[{"type":"input_text","text":"hello"}] + }], + "max_output_tokens":20 + } + """; + + const string Output = """ + { + "id": "resp_67d327649b288191aeb46a824e49dc40058a5e08c46a181d", + "object": "response", + "created_at": 1741891428, + "status": "completed", + "error": null, + "incomplete_details": null, + "instructions": null, + "max_output_tokens": 20, + "model": "gpt-4o-mini-2024-07-18", + "output": [ + { + "type": "message", + "id": "msg_67d32764fcdc8191bcf2e444d4088804058a5e08c46a181d", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": "Hello!", + "annotations": [] + } + ] + }, + { + "type": "message", + "id": "msg_67d32764fcdc8191bcf2e444d4088804058a5e08c46a182e", + "status": "completed", + "role": "assistant", + "content": [ + { + "type": "output_text", + "text": " How can I assist you today?", + "annotations": [] + } + ] + } + ], + "parallel_tool_calls": true, + "previous_response_id": null, + "reasoning": { + "effort": null, + "generate_summary": null + }, + "store": true, + "temperature": 0.5, + "text": { + "format": { + "type": "text" + } + }, + "tool_choice": "auto", + "tools": [], + "top_p": 1.0, + "usage": { + "input_tokens": 26, + "input_tokens_details": { + "cached_tokens": 0 + }, + "output_tokens": 10, + "output_tokens_details": { + "reasoning_tokens": 0 + }, + "total_tokens": 36 + }, + "user": null, + "metadata": {} + } + """; + + using VerbatimHttpHandler handler = new(Input, Output); + using HttpClient httpClient = new(handler); + using IChatClient client = CreateResponseClient(httpClient, "gpt-4o-mini"); + + var response = await client.GetResponseAsync("hello", new() + { + MaxOutputTokens = 20, + Temperature = 0.5f, + }); + Assert.NotNull(response); + + Assert.Equal("resp_67d327649b288191aeb46a824e49dc40058a5e08c46a181d", response.ResponseId); + Assert.Equal("resp_67d327649b288191aeb46a824e49dc40058a5e08c46a181d", response.ConversationId); + Assert.Equal("gpt-4o-mini-2024-07-18", response.ModelId); + Assert.Equal(DateTimeOffset.FromUnixTimeSeconds(1_741_891_428), response.CreatedAt); + Assert.Null(response.FinishReason); + + Assert.Equal(2, response.Messages.Count); + Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); + Assert.Equal("Hello!", response.Messages[0].Text); + Assert.Equal(ChatRole.Assistant, response.Messages[1].Role); + Assert.Equal(" How can I assist you today?", response.Messages[1].Text); + + Assert.NotNull(response.Usage); + Assert.Equal(26, response.Usage.InputTokenCount); + Assert.Equal(10, response.Usage.OutputTokenCount); + Assert.Equal(36, response.Usage.TotalTokenCount); + } + /// Converts an Extensions function to an OpenAI response chat tool. private static ResponseTool ToOpenAIResponseChatTool(AIFunction aiFunction) {