diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseExtensions.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseExtensions.cs index 87a957130f5..cbc00dd98ec 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseExtensions.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatResponseExtensions.cs @@ -306,26 +306,19 @@ private static void FinalizeResponse(ChatResponse response) private static void ProcessUpdate(ChatResponseUpdate update, ChatResponse response) { // If there is no message created yet, or if the last update we saw had a different - // message ID or role than the newest update, create a new message. - ChatMessage message; - var isNewMessage = false; - if (response.Messages.Count == 0) - { - isNewMessage = true; - } - else if (update.MessageId is { Length: > 0 } updateMessageId - && response.Messages[response.Messages.Count - 1].MessageId is string lastMessageId - && updateMessageId != lastMessageId) - { - isNewMessage = true; - } - else if (update.Role is { } updateRole - && response.Messages[response.Messages.Count - 1].Role is { } lastRole - && updateRole != lastRole) + // identifying parts, create a new message. + bool isNewMessage = true; + if (response.Messages.Count != 0) { - isNewMessage = true; + var lastMessage = response.Messages[response.Messages.Count - 1]; + isNewMessage = + NotEmptyOrEqual(update.AuthorName, lastMessage.AuthorName) || + NotEmptyOrEqual(update.MessageId, lastMessage.MessageId) || + NotNullOrEqual(update.Role, lastMessage.Role); } + // Get the message to target, either a new one or the last ones. + ChatMessage message; if (isNewMessage) { message = new(ChatRole.Assistant, []); @@ -418,4 +411,12 @@ private static void ProcessUpdate(ChatResponseUpdate update, ChatResponse respon } } } + + /// Gets whether both strings are not null/empty and not the same as each other. + private static bool NotEmptyOrEqual(string? s1, string? s2) => + s1 is { Length: > 0 } str1 && s2 is { Length: > 0 } str2 && str1 != str2; + + /// Gets whether two roles are not null and not the same as each other. + private static bool NotNullOrEqual(ChatRole? r1, ChatRole? r2) => + r1.HasValue && r2.HasValue && r1.Value != r2.Value; } diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/ChatCompletion/ChatResponseUpdateExtensionsTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/ChatCompletion/ChatResponseUpdateExtensionsTests.cs index 3328d0be083..ee9ae354677 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/ChatCompletion/ChatResponseUpdateExtensionsTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/ChatCompletion/ChatResponseUpdateExtensionsTests.cs @@ -36,8 +36,8 @@ public async Task ToChatResponse_SuccessfullyCreatesResponse(bool useAsync) ]; ChatResponse response = useAsync ? - updates.ToChatResponse() : - await YieldAsync(updates).ToChatResponseAsync(); + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); Assert.NotNull(response); Assert.NotNull(response.Usage); @@ -67,7 +67,7 @@ public async Task ToChatResponse_SuccessfullyCreatesResponse(bool useAsync) [Theory] [InlineData(false)] [InlineData(true)] - public async Task ToChatResponse_RoleOrIdChangeDictatesMessageChange(bool useAsync) + public async Task ToChatResponse_RoleOrIdOrAuthorNameChangeDictatesMessageChange(bool useAsync) { ChatResponseUpdate[] updates = [ @@ -88,8 +88,8 @@ public async Task ToChatResponse_RoleOrIdChangeDictatesMessageChange(bool useAsy ]; ChatResponse response = useAsync ? - updates.ToChatResponse() : - await YieldAsync(updates).ToChatResponseAsync(); + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); Assert.Equal(9, response.Messages.Count); Assert.Equal("!a", response.Messages[0].Text); @@ -120,6 +120,329 @@ public async Task ToChatResponse_RoleOrIdChangeDictatesMessageChange(bool useAsy Assert.Equal(ChatRole.Assistant, response.Messages[8].Role); } + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_AuthorNameChangeDictatesMessageBoundary(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // First message with AuthorName "Alice" + new(ChatRole.Assistant, "Hello ") { AuthorName = "Alice" }, + new(null, "from ") { AuthorName = "Alice" }, + new(null, "Alice!"), + + // Second message - AuthorName changes to "Bob" + new(null, "Hi ") { AuthorName = "Bob" }, + new(null, "from ") { AuthorName = "Bob" }, + new(null, "Bob!"), + + // Third message - AuthorName changes to "Charlie" + new(ChatRole.Assistant, "Greetings ") { AuthorName = "Charlie" }, + new(null, "from Charlie!") { AuthorName = "Charlie" }, + + // Fourth message - AuthorName changes back to "Alice" + new(null, "Alice again!") { AuthorName = "Alice" }, + + // Fifth message - empty/null AuthorName should continue with last message + new(null, " Still Alice.") { AuthorName = "" }, + new(null, " And more."), + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + Assert.Equal(4, response.Messages.Count); + + Assert.Equal("Hello from Alice!", response.Messages[0].Text); + Assert.Equal("Alice", response.Messages[0].AuthorName); + Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); + + Assert.Equal("Hi from Bob!", response.Messages[1].Text); + Assert.Equal("Bob", response.Messages[1].AuthorName); + Assert.Equal(ChatRole.Assistant, response.Messages[1].Role); + + Assert.Equal("Greetings from Charlie!", response.Messages[2].Text); + Assert.Equal("Charlie", response.Messages[2].AuthorName); + Assert.Equal(ChatRole.Assistant, response.Messages[2].Role); + + Assert.Equal("Alice again! Still Alice. And more.", response.Messages[3].Text); + Assert.Equal("Alice", response.Messages[3].AuthorName); + Assert.Equal(ChatRole.Assistant, response.Messages[3].Role); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_AuthorNameWithOtherBoundaries(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // Message 1: Role=Assistant, MessageId="1", AuthorName="Alice" + new(ChatRole.Assistant, "A") { MessageId = "1", AuthorName = "Alice" }, + new(null, "B") { MessageId = "1", AuthorName = "Alice" }, + + // Message 2: AuthorName changes to "Bob", same MessageId and Role + new(null, "C") { MessageId = "1", AuthorName = "Bob" }, + + // Message 3: MessageId changes to "2", AuthorName stays "Bob" + new(null, "D") { MessageId = "2", AuthorName = "Bob" }, + new(null, "E") { MessageId = "2", AuthorName = "Bob" }, + + // Message 4: Role changes to User, AuthorName stays "Bob" + new(ChatRole.User, "F") { MessageId = "2", AuthorName = "Bob" }, + + // Message 5: All three boundaries change + new(ChatRole.Tool, "G") { MessageId = "3", AuthorName = "Charlie" }, + new(null, "H") { MessageId = "3", AuthorName = "Charlie" }, + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + Assert.Equal(5, response.Messages.Count); + + Assert.Equal("AB", response.Messages[0].Text); + Assert.Equal("Alice", response.Messages[0].AuthorName); + Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); + Assert.Equal("1", response.Messages[0].MessageId); + + Assert.Equal("C", response.Messages[1].Text); + Assert.Equal("Bob", response.Messages[1].AuthorName); + Assert.Equal(ChatRole.Assistant, response.Messages[1].Role); + Assert.Equal("1", response.Messages[1].MessageId); + + Assert.Equal("DE", response.Messages[2].Text); + Assert.Equal("Bob", response.Messages[2].AuthorName); + Assert.Equal(ChatRole.Assistant, response.Messages[2].Role); + Assert.Equal("2", response.Messages[2].MessageId); + + Assert.Equal("F", response.Messages[3].Text); + Assert.Equal("Bob", response.Messages[3].AuthorName); + Assert.Equal(ChatRole.User, response.Messages[3].Role); + Assert.Equal("2", response.Messages[3].MessageId); + + Assert.Equal("GH", response.Messages[4].Text); + Assert.Equal("Charlie", response.Messages[4].AuthorName); + Assert.Equal(ChatRole.Tool, response.Messages[4].Role); + Assert.Equal("3", response.Messages[4].MessageId); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_EmptyOrNullAuthorNameDoesNotCreateBoundary(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // First message with AuthorName "Assistant" + new(ChatRole.Assistant, "Hello") { AuthorName = "Assistant" }, + + // Empty AuthorName should not create new message + new(null, " world") { AuthorName = "" }, + + // Null AuthorName should not create new message + new(null, "!"), + + // Another empty AuthorName + new(null, " How") { AuthorName = "" }, + new(null, " are") { AuthorName = "" }, + + // Null again + new(null, " you?") { AuthorName = null }, + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + ChatMessage message = Assert.Single(response.Messages); + Assert.Equal("Hello world! How are you?", message.Text); + Assert.Equal("Assistant", message.AuthorName); + Assert.Equal(ChatRole.Assistant, message.Role); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_AuthorNameNullToNonNullDoesNotCreateBoundary(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // First message with no AuthorName + new(ChatRole.Assistant, "Hello") { MessageId = "1" }, + new(null, " there") { MessageId = "1" }, + + // AuthorName becomes non-empty but doesn't create boundary + new(null, " I'm Bob") { MessageId = "1", AuthorName = "Bob" }, + new(null, " speaking") { MessageId = "1", AuthorName = "Bob" }, + + // Second message - AuthorName changes to "Alice" creates boundary + new(null, "Now Alice") { MessageId = "1", AuthorName = "Alice" }, + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + Assert.Equal(2, response.Messages.Count); + + Assert.Equal("Hello there I'm Bob speaking", response.Messages[0].Text); + Assert.Equal("Bob", response.Messages[0].AuthorName); // Last AuthorName wins + Assert.Equal("1", response.Messages[0].MessageId); + + Assert.Equal("Now Alice", response.Messages[1].Text); + Assert.Equal("Alice", response.Messages[1].AuthorName); + Assert.Equal("1", response.Messages[1].MessageId); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_MessageIdNullToNonNullDoesNotCreateBoundary(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // First message with no MessageId + new(ChatRole.Assistant, "Hello"), + new(null, " there"), + + // MessageId becomes non-empty but doesn't create boundary + new(null, " from") { MessageId = "msg1" }, + new(null, " AI") { MessageId = "msg1" }, + + // Second message - MessageId changes to different value creates boundary + new(null, "Next message") { MessageId = "msg2" }, + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + Assert.Equal(2, response.Messages.Count); + + Assert.Equal("Hello there from AI", response.Messages[0].Text); + Assert.Equal("msg1", response.Messages[0].MessageId); // Last MessageId wins + Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); + + Assert.Equal("Next message", response.Messages[1].Text); + Assert.Equal("msg2", response.Messages[1].MessageId); + Assert.Equal(ChatRole.Assistant, response.Messages[1].Role); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_EmptyMessageIdDoesNotCreateBoundary(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // First message with MessageId + new(ChatRole.Assistant, "Hello") { MessageId = "msg1" }, + new(null, " world") { MessageId = "msg1" }, + + // Empty MessageId should not create new message + new(null, "!") { MessageId = "" }, + + // Null MessageId should not create new message + new(null, " How"), + + // Another message with empty MessageId + new(null, " are") { MessageId = "" }, + new(null, " you?"), + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + ChatMessage message = Assert.Single(response.Messages); + Assert.Equal("Hello world! How are you?", message.Text); + Assert.Equal("msg1", message.MessageId); + Assert.Equal(ChatRole.Assistant, message.Role); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_RoleNullToNonNullDoesNotCreateBoundary(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // First message with no explicit Role (will default to Assistant) + new(null, "Hello") { MessageId = "1" }, + new(null, " there") { MessageId = "1" }, + + // Role becomes explicit Assistant - shouldn't create boundary + new(ChatRole.Assistant, " from") { MessageId = "1" }, + new(null, " AI") { MessageId = "1" }, + + // Second message - Role changes to User creates boundary + new(ChatRole.User, "User message") { MessageId = "1" }, + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + Assert.Equal(2, response.Messages.Count); + + Assert.Equal("Hello there from AI", response.Messages[0].Text); + Assert.Equal(ChatRole.Assistant, response.Messages[0].Role); + Assert.Equal("1", response.Messages[0].MessageId); + + Assert.Equal("User message", response.Messages[1].Text); + Assert.Equal(ChatRole.User, response.Messages[1].Role); + Assert.Equal("1", response.Messages[1].MessageId); + } + + [Theory] + [InlineData(false)] + [InlineData(true)] + public async Task ToChatResponse_CustomRoleChangesCreateBoundary(bool useAsync) + { + ChatResponseUpdate[] updates = + [ + + // First message with custom role "agent1" + new(new ChatRole("agent1"), "Hello") { MessageId = "1" }, + new(null, " from") { MessageId = "1" }, + new(new ChatRole("agent1"), " agent1") { MessageId = "1" }, + + // Second message - custom role changes to "agent2" + new(new ChatRole("agent2"), "Hi") { MessageId = "1" }, + new(null, " from") { MessageId = "1" }, + new(new ChatRole("agent2"), " agent2") { MessageId = "1" }, + + // Third message - changes to standard role + new(ChatRole.Assistant, "Assistant here") { MessageId = "1" }, + ]; + + ChatResponse response = useAsync ? + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); + + Assert.Equal(3, response.Messages.Count); + + Assert.Equal("Hello from agent1", response.Messages[0].Text); + Assert.Equal(new ChatRole("agent1"), response.Messages[0].Role); + + Assert.Equal("Hi from agent2", response.Messages[1].Text); + Assert.Equal(new ChatRole("agent2"), response.Messages[1].Role); + + Assert.Equal("Assistant here", response.Messages[2].Text); + Assert.Equal(ChatRole.Assistant, response.Messages[2].Role); + } + [Theory] [InlineData(false)] [InlineData(true)] @@ -128,22 +451,25 @@ public async Task ToChatResponse_UpdatesProduceMultipleResponseMessages(bool use ChatResponseUpdate[] updates = [ - // First message - ID "msg1" + // First message - ID "msg1", AuthorName "Assistant" new(null, "Hi! ") { CreatedAt = new DateTimeOffset(2023, 1, 1, 10, 0, 0, TimeSpan.Zero), AuthorName = "Assistant" }, new(ChatRole.Assistant, "Hello") { MessageId = "msg1", CreatedAt = new DateTimeOffset(2024, 1, 1, 10, 0, 0, TimeSpan.Zero), AuthorName = "Assistant" }, new(null, " from") { MessageId = "msg1", CreatedAt = new DateTimeOffset(2024, 1, 1, 10, 1, 0, TimeSpan.Zero) }, // Later CreatedAt should win - new(null, " AI") { MessageId = "msg1", AuthorName = "AI Assistant" }, // Later AuthorName should win + new(null, " AI") { MessageId = "msg1", AuthorName = "Assistant" }, // Keep same AuthorName to avoid creating new message + + // Second message - ID "msg1" changes to "msg2", still AuthorName "Assistant" + new(null, "More text") { MessageId = "msg2", CreatedAt = new DateTimeOffset(2024, 1, 1, 10, 2, 0, TimeSpan.Zero), AuthorName = "Assistant" }, - // Second message - ID "msg2" - new(ChatRole.User, "How") { MessageId = "msg2", CreatedAt = new DateTimeOffset(2024, 1, 1, 11, 0, 0, TimeSpan.Zero), AuthorName = "User" }, - new(null, " are") { MessageId = "msg2", CreatedAt = new DateTimeOffset(2024, 1, 1, 11, 1, 0, TimeSpan.Zero) }, - new(null, " you?") { MessageId = "msg2", AuthorName = "Human User" }, // Later AuthorName should win + // Third message - ID "msg3", Role changes to User + new(ChatRole.User, "How") { MessageId = "msg3", CreatedAt = new DateTimeOffset(2024, 1, 1, 11, 0, 0, TimeSpan.Zero), AuthorName = "User" }, + new(null, " are") { MessageId = "msg3", CreatedAt = new DateTimeOffset(2024, 1, 1, 11, 1, 0, TimeSpan.Zero) }, + new(null, " you?") { MessageId = "msg3", AuthorName = "User" }, // Keep same AuthorName - // Third message - ID "msg3" - new(ChatRole.Assistant, "I'm doing well,") { MessageId = "msg3", CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero) }, - new(null, " thank you!") { MessageId = "msg3", CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 2, 0, TimeSpan.Zero) }, // Later CreatedAt should win + // Fourth message - ID "msg4", Role changes back to Assistant + new(ChatRole.Assistant, "I'm doing well,") { MessageId = "msg4", CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 0, 0, TimeSpan.Zero) }, + new(null, " thank you!") { MessageId = "msg4", CreatedAt = new DateTimeOffset(2024, 1, 1, 12, 2, 0, TimeSpan.Zero) }, // Later CreatedAt should win - // Updates without MessageId should continue the last message (msg3) + // Updates without MessageId should continue the last message (msg4) new(null, " How can I help?"), ]; @@ -152,31 +478,39 @@ await YieldAsync(updates).ToChatResponseAsync() : updates.ToChatResponse(); Assert.NotNull(response); - Assert.Equal(3, response.Messages.Count); + Assert.Equal(4, response.Messages.Count); // Verify first message ChatMessage message1 = response.Messages[0]; Assert.Equal("msg1", message1.MessageId); Assert.Equal(ChatRole.Assistant, message1.Role); - Assert.Equal("AI Assistant", message1.AuthorName); // Last value should win + Assert.Equal("Assistant", message1.AuthorName); Assert.Equal(new DateTimeOffset(2024, 1, 1, 10, 1, 0, TimeSpan.Zero), message1.CreatedAt); // Last value should win Assert.Equal("Hi! Hello from AI", message1.Text); // Verify second message ChatMessage message2 = response.Messages[1]; Assert.Equal("msg2", message2.MessageId); - Assert.Equal(ChatRole.User, message2.Role); - Assert.Equal("Human User", message2.AuthorName); // Last value should win - Assert.Equal(new DateTimeOffset(2024, 1, 1, 11, 1, 0, TimeSpan.Zero), message2.CreatedAt); // Last value should win - Assert.Equal("How are you?", message2.Text); + Assert.Equal(ChatRole.Assistant, message2.Role); + Assert.Equal("Assistant", message2.AuthorName); + Assert.Equal(new DateTimeOffset(2024, 1, 1, 10, 2, 0, TimeSpan.Zero), message2.CreatedAt); + Assert.Equal("More text", message2.Text); // Verify third message ChatMessage message3 = response.Messages[2]; Assert.Equal("msg3", message3.MessageId); - Assert.Equal(ChatRole.Assistant, message3.Role); - Assert.Null(message3.AuthorName); // No AuthorName set in later updates - Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 2, 0, TimeSpan.Zero), message3.CreatedAt); // Last value should win - Assert.Equal("I'm doing well, thank you! How can I help?", message3.Text); + Assert.Equal(ChatRole.User, message3.Role); + Assert.Equal("User", message3.AuthorName); + Assert.Equal(new DateTimeOffset(2024, 1, 1, 11, 1, 0, TimeSpan.Zero), message3.CreatedAt); // Last value should win + Assert.Equal("How are you?", message3.Text); + + // Verify fourth message + ChatMessage message4 = response.Messages[3]; + Assert.Equal("msg4", message4.MessageId); + Assert.Equal(ChatRole.Assistant, message4.Role); + Assert.Null(message4.AuthorName); // No AuthorName set + Assert.Equal(new DateTimeOffset(2024, 1, 1, 12, 2, 0, TimeSpan.Zero), message4.CreatedAt); // Last value should win + Assert.Equal("I'm doing well, thank you! How can I help?", message4.Text); } public static IEnumerable ToChatResponse_Coalescing_VariousSequenceAndGapLengths_MemberData() @@ -395,8 +729,8 @@ public async Task ToChatResponse_AlternativeTimestamps(bool useAsync) ]; ChatResponse response = useAsync ? - updates.ToChatResponse() : - await YieldAsync(updates).ToChatResponseAsync(); + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); Assert.Single(response.Messages); Assert.Equal("abcdefg", response.Messages[0].Text); @@ -442,8 +776,8 @@ public async Task ToChatResponse_TimestampFolding(bool useAsync, string? timesta ]; ChatResponse response = useAsync ? - updates.ToChatResponse() : - await YieldAsync(updates).ToChatResponseAsync(); + await YieldAsync(updates).ToChatResponseAsync() : + updates.ToChatResponse(); Assert.Single(response.Messages); Assert.Equal("ab", response.Messages[0].Text); diff --git a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/SpeechToText/SpeechToTextResponseUpdateExtensionsTests.cs b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/SpeechToText/SpeechToTextResponseUpdateExtensionsTests.cs index 5d5a035bfe8..f288d3db28b 100644 --- a/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/SpeechToText/SpeechToTextResponseUpdateExtensionsTests.cs +++ b/test/Libraries/Microsoft.Extensions.AI.Abstractions.Tests/SpeechToText/SpeechToTextResponseUpdateExtensionsTests.cs @@ -54,8 +54,8 @@ public async Task ToSpeechToTextResponse_SuccessfullyCreatesResponse(bool useAsy ]; SpeechToTextResponse response = useAsync ? - updates.ToSpeechToTextResponse() : - await YieldAsync(updates).ToSpeechToTextResponseAsync(); + await YieldAsync(updates).ToSpeechToTextResponseAsync() : + updates.ToSpeechToTextResponse(); Assert.NotNull(response);