mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
.NET: Adding AgentRunContext to allow accessing agent run info in external downstream components (#3476)
* Add an AsyncLocal AgentRunContext * Update AgentRunContext session naming * Make AgentRunContext readonly and add ADR * Make session nullable and add unit tests * Add unit tests for setting the context in AIAgent * Apply suggestions from code review Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com> * Fix sample in ADR * Fix broken unit test * Add unit test for checking if middleware can access AgentRunContext * Fix build error after merge. * Fix AgentRunContextTests after merge from main --------- Co-authored-by: Copilot <175728472+Copilot@users.noreply.github.com>
This commit is contained in:
@@ -3,6 +3,8 @@
|
||||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Diagnostics;
|
||||
using System.Linq;
|
||||
using System.Runtime.CompilerServices;
|
||||
using System.Text.Json;
|
||||
using System.Threading;
|
||||
using System.Threading.Tasks;
|
||||
@@ -22,6 +24,8 @@ namespace Microsoft.Agents.AI;
|
||||
[DebuggerDisplay("{DebuggerDisplay,nq}")]
|
||||
public abstract class AIAgent
|
||||
{
|
||||
private static readonly AsyncLocal<AgentRunContext?> s_currentContext = new();
|
||||
|
||||
[DebuggerBrowsable(DebuggerBrowsableState.Never)]
|
||||
private string DebuggerDisplay =>
|
||||
this.Name is { } name ? $"Id = {this.Id}, Name = {name}" : $"Id = {this.Id}";
|
||||
@@ -76,6 +80,18 @@ public abstract class AIAgent
|
||||
/// </remarks>
|
||||
public virtual string? Description { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Gets or sets the <see cref="AgentRunContext"/> for the current agent run.
|
||||
/// </summary>
|
||||
/// <remarks>
|
||||
/// This value flows across async calls.
|
||||
/// </remarks>
|
||||
public static AgentRunContext? CurrentRunContext
|
||||
{
|
||||
get => s_currentContext.Value;
|
||||
protected set => s_currentContext.Value = value;
|
||||
}
|
||||
|
||||
/// <summary>Asks the <see cref="AIAgent"/> for an object of the specified type <paramref name="serviceType"/>.</summary>
|
||||
/// <param name="serviceType">The type of object being requested.</param>
|
||||
/// <param name="serviceKey">An optional key that can be used to help identify the target service.</param>
|
||||
@@ -252,8 +268,11 @@ public abstract class AIAgent
|
||||
IEnumerable<ChatMessage> messages,
|
||||
AgentSession? session = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.RunCoreAsync(messages, session, options, cancellationToken);
|
||||
CancellationToken cancellationToken = default)
|
||||
{
|
||||
CurrentRunContext = new(this, session, messages as IReadOnlyCollection<ChatMessage> ?? messages.ToList(), options);
|
||||
return this.RunCoreAsync(messages, session, options, cancellationToken);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core implementation of the agent invocation logic with a collection of chat messages.
|
||||
@@ -370,12 +389,22 @@ public abstract class AIAgent
|
||||
/// to display partial results, implement progressive loading, or provide immediate feedback to users.
|
||||
/// </para>
|
||||
/// </remarks>
|
||||
public IAsyncEnumerable<AgentResponseUpdate> RunStreamingAsync(
|
||||
public async IAsyncEnumerable<AgentResponseUpdate> RunStreamingAsync(
|
||||
IEnumerable<ChatMessage> messages,
|
||||
AgentSession? session = null,
|
||||
AgentRunOptions? options = null,
|
||||
CancellationToken cancellationToken = default) =>
|
||||
this.RunCoreStreamingAsync(messages, session, options, cancellationToken);
|
||||
[EnumeratorCancellation] CancellationToken cancellationToken = default)
|
||||
{
|
||||
AgentRunContext context = new(this, session, messages as IReadOnlyCollection<ChatMessage> ?? messages.ToList(), options);
|
||||
CurrentRunContext = context;
|
||||
await foreach (var update in this.RunCoreStreamingAsync(messages, session, options, cancellationToken).ConfigureAwait(false))
|
||||
{
|
||||
yield return update;
|
||||
|
||||
// Restore context again when resuming after the caller code executes.
|
||||
CurrentRunContext = context;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Core implementation of the agent streaming invocation logic with a collection of chat messages.
|
||||
|
||||
@@ -0,0 +1,42 @@
|
||||
// Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
using System.Collections.Generic;
|
||||
using Microsoft.Extensions.AI;
|
||||
using Microsoft.Shared.Diagnostics;
|
||||
|
||||
namespace Microsoft.Agents.AI;
|
||||
|
||||
/// <summary>Provides context for an in-flight agent run.</summary>
|
||||
public sealed class AgentRunContext
|
||||
{
|
||||
/// <summary>
|
||||
/// Initializes a new instance of the <see cref="AgentRunContext"/> class.
|
||||
/// </summary>
|
||||
/// <param name="agent">The <see cref="AIAgent"/> that is executing the current run.</param>
|
||||
/// <param name="session">The <see cref="AgentSession"/> that is associated with the current run if any.</param>
|
||||
/// <param name="requestMessages">The request messages passed into the current run.</param>
|
||||
/// <param name="agentRunOptions">The <see cref="AgentRunOptions"/> that was passed to the current run.</param>
|
||||
public AgentRunContext(
|
||||
AIAgent agent,
|
||||
AgentSession? session,
|
||||
IReadOnlyCollection<ChatMessage> requestMessages,
|
||||
AgentRunOptions? agentRunOptions)
|
||||
{
|
||||
this.Agent = Throw.IfNull(agent);
|
||||
this.Session = session;
|
||||
this.RequestMessages = Throw.IfNull(requestMessages);
|
||||
this.RunOptions = agentRunOptions;
|
||||
}
|
||||
|
||||
/// <summary>Gets the <see cref="AIAgent"/> that is executing the current run.</summary>
|
||||
public AIAgent Agent { get; }
|
||||
|
||||
/// <summary>Gets the <see cref="AgentSession"/> that is associated with the current run.</summary>
|
||||
public AgentSession? Session { get; }
|
||||
|
||||
/// <summary>Gets the request messages passed into the current run.</summary>
|
||||
public IReadOnlyCollection<ChatMessage> RequestMessages { get; }
|
||||
|
||||
/// <summary>Gets the <see cref="AgentRunOptions"/> that was passed to the current run.</summary>
|
||||
public AgentRunOptions? RunOptions { get; }
|
||||
}
|
||||
Reference in New Issue
Block a user