Run Response ADR & Updates (#104)

* Add ADR for different run response options

* Add another option to the list.

* Update agno non-streaming with further clarification

* Add another option

* Adding optional includeUpdates option

* Adding Pros/Cons for each option

* Make pros/cons a list

* Add some thoughts on structured outputs and custom AIContent types

* Update design doc to clarify primary and secondary better and split out custom response types with it's own options

* Add structured outputs competitive comparison and suggestion

* Address PR comments.

* Remove AgentRunFinishReason until we can find a good use case for it.

* Add finish reason to list of excluded properties.

* Add custom agent run response types.
Usage to follow.

* Update Agent run response types

* Add additional code coverage

* Remove onIntermediateMessage since it is unecessary with the new response approach.

* Add AgentId to response.

* Rename ParseAsStructuredOutput to Deserialize

* Update decision doc.

* Fix formatting.

* Update CopilotStudio to return new response types

* Address PR comment

Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>

---------

Co-authored-by: Roger Barreto <19890735+rogerbarreto@users.noreply.github.com>
This commit is contained in:
westey
2025-07-11 12:39:18 +01:00
committed by GitHub
Unverified
parent 33d09d263b
commit 715769e649
36 changed files with 2143 additions and 260 deletions
+11 -10
View File
@@ -4,6 +4,7 @@ using System.Reflection;
using System.Text;
using System.Text.Json;
using Microsoft.Extensions.AI;
using Microsoft.Extensions.AI.Agents;
using Microsoft.Extensions.Configuration;
using Microsoft.Extensions.Logging;
using Microsoft.Shared.Samples;
@@ -87,29 +88,29 @@ public abstract class BaseSample : TextWriter
/// Processes and writes the latest agent chat response to the console, including metadata and content details.
/// </summary>
/// <remarks>This method formats and outputs the most recent message from the provided <see
/// cref="ChatResponse"/> object. It includes the message role, author name (if available), text content, and
/// cref="AgentRunResponse"/> object. It includes the message role, author name (if available), text content, and
/// additional content such as images, function calls, and function results. Usage statistics, including token
/// counts, are also displayed.</remarks>
/// <param name="chatResponse">The <see cref="ChatResponse"/> object containing the chat messages and usage data.</param>
/// <param name="response">The <see cref="AgentRunResponse"/> object containing the chat messages and usage data.</param>
/// <param name="printUsage">The flag to indicate whether to print usage information. Defaults to <see langword="true"/>.</param>
protected void WriteResponseOutput(ChatResponse chatResponse, bool? printUsage = true)
protected void WriteResponseOutput(AgentRunResponse response, bool? printUsage = true)
{
if (chatResponse.Messages.Count == 0)
if (response.Messages.Count == 0)
{
// If there are no messages, we can skip writing the message.
return;
}
var message = chatResponse.Messages.Last();
var message = response.Messages.Last();
this.WriteMessageOutput(message);
WriteUsage();
void WriteUsage()
{
if (!(printUsage ?? true) || chatResponse.Usage is null) { return; }
if (!(printUsage ?? true) || response.Usage is null) { return; }
UsageDetails usageDetails = chatResponse.Usage;
UsageDetails usageDetails = response.Usage;
Console.WriteLine($" [Usage] Tokens: {usageDetails.TotalTokenCount}, Input: {usageDetails.InputTokenCount}, Output: {usageDetails.OutputTokenCount}");
}
@@ -151,11 +152,11 @@ public abstract class BaseSample : TextWriter
/// Writes the streaming agent response updates to the console.
/// </summary>
/// <remarks>This method formats and outputs the most recent message from the provided <see
/// cref="ChatResponseUpdate"/> object. It includes the message role, author name (if available), text content, and
/// cref="AgentRunResponseUpdate"/> object. It includes the message role, author name (if available), text content, and
/// additional content such as images, function calls, and function results. Usage statistics, including token
/// counts, are also displayed.</remarks>
/// <param name="update">The <see cref="ChatResponseUpdate"/> object containing the chat messages and usage data.</param>
protected void WriteAgentOutput(ChatResponseUpdate update)
/// <param name="update">The <see cref="AgentRunResponseUpdate"/> object containing the chat messages and usage data.</param>
protected void WriteAgentOutput(AgentRunResponseUpdate update)
{
if (update.Contents.Count == 0)
{
@@ -74,7 +74,7 @@ public abstract class OrchestrationSample : BaseSample
}
/// <summary>
/// Writes the provided chat response messages to the console or test output, including role and author information.
/// Writes the provided messages to the console or test output, including role and author information.
/// </summary>
/// <param name="response">An enumerable of <see cref="ChatMessage"/> objects to write.</param>
protected static void WriteResponse(IEnumerable<ChatMessage> response)
@@ -89,15 +89,15 @@ public abstract class OrchestrationSample : BaseSample
}
/// <summary>
/// Writes the streamed chat response updates to the console or test output, including role and author information.
/// Writes the streamed agent run response updates to the console or test output, including role and author information.
/// </summary>
/// <param name="streamedResponses">An enumerable of <see cref="ChatResponseUpdate"/> objects representing streamed responses.</param>
protected static void WriteStreamedResponse(IEnumerable<ChatResponseUpdate> streamedResponses)
/// <param name="streamedResponses">An enumerable of <see cref="AgentRunResponseUpdate"/> objects representing streamed responses.</param>
protected static void WriteStreamedResponse(IEnumerable<AgentRunResponseUpdate> streamedResponses)
{
string? authorName = null;
ChatRole? authorRole = null;
StringBuilder builder = new();
foreach (ChatResponseUpdate response in streamedResponses)
foreach (AgentRunResponseUpdate response in streamedResponses)
{
authorName ??= response.AuthorName;
authorRole ??= response.Role;
@@ -122,7 +122,7 @@ public abstract class OrchestrationSample : BaseSample
/// <summary>
/// Gets the list of streamed response updates received so far.
/// </summary>
public List<ChatResponseUpdate> StreamedResponses { get; } = [];
public List<AgentRunResponseUpdate> StreamedResponses { get; } = [];
/// <summary>
/// Gets the list of chat messages representing the conversation history.
@@ -142,12 +142,12 @@ public abstract class OrchestrationSample : BaseSample
}
/// <summary>
/// Callback to handle a streamed chat response update, adding it to the list and writing output if final.
/// Callback to handle a streamed agent run response update, adding it to the list and writing output if final.
/// </summary>
/// <param name="streamedResponse">The <see cref="ChatResponseUpdate"/> to process.</param>
/// <param name="streamedResponse">The <see cref="AgentRunResponseUpdate"/> to process.</param>
/// <param name="isFinal">Indicates whether this is the final update in the stream.</param>
/// <returns>A <see cref="ValueTask"/> representing the asynchronous operation.</returns>
public ValueTask StreamingResultCallback(ChatResponseUpdate streamedResponse, bool isFinal)
public ValueTask StreamingResultCallback(AgentRunResponseUpdate streamedResponse, bool isFinal)
{
this.StreamedResponses.Add(streamedResponse);