From f21b9d14f85169f6053a4c5f9a002c0d2377c425 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Wed, 1 Oct 2025 04:44:33 -0400 Subject: [PATCH] .NET: Use copilot to improve XML docs for M.Agents.AI{.Abstractions} (#1042) * Use copilot to improve XML docs for M.Agents.AI{.Abstractions} Asked copilot to help improve some of the docs, then I reviewed them. I also cleaned up a few things along the way, like a couple of extension method types that should be combined. * Update dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatMessageStore.cs --------- Co-authored-by: Mark Wallace <127216156+markwallace-microsoft@users.noreply.github.com> --- .../AIAgent.cs | 241 +++++++++++------- .../AIAgentMetadata.cs | 19 +- .../AIContext.cs | 88 ++++++- .../AIContextProvider.cs | 121 ++++++--- .../AgentAbstractionsJsonUtilities.cs | 8 +- .../AgentRunOptions.cs | 8 +- .../AgentRunResponse.cs | 143 +++++++++-- .../AgentRunResponseExtensions.cs | 57 +++-- .../AgentThreadMetadata.cs | 4 +- .../ChatMessageStore.cs | 65 ++++- .../DelegatingAIAgent.cs | 34 ++- .../InMemoryAgentThread.cs | 42 ++- .../InMemoryChatMessageStore.cs | 43 +++- .../ServiceIdAgentThread.cs | 43 +++- .../src/Microsoft.Agents.AI/AIAgentBuilder.cs | 25 +- .../AIAgentBuilderAIAgentExtensions.cs | 25 -- .../AIAgentBuilderExtensions.cs | 75 +++++- .../Microsoft.Agents.AI/AgentExtensions.cs | 57 ++++- .../ChatClient/ChatClientAgent.cs | 91 +++++-- .../ChatClient/ChatClientAgentRunOptions.cs | 32 ++- .../ChatClient/ChatClientAgentThread.cs | 22 +- .../OpenTelemetryAIAgentBuilderExtensions.cs | 33 --- .../Microsoft.Agents.AI/OpenTelemetryAgent.cs | 17 +- .../AgentExtensionsTests.cs | 4 +- ...nTelemetryAIAgentBuilderExtensionsTests.cs | 5 +- 25 files changed, 960 insertions(+), 342 deletions(-) delete mode 100644 dotnet/src/Microsoft.Agents.AI/AIAgentBuilderAIAgentExtensions.cs delete mode 100644 dotnet/src/Microsoft.Agents.AI/OpenTelemetryAIAgentBuilderExtensions.cs diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs index e866b148a1..8eb4839ae0 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgent.cs @@ -11,32 +11,66 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Base abstraction for all agents. An agent instance may participate in one or more conversations. -/// A conversation may include one or more agents. +/// Provides the base abstraction for all AI agents, defining the core interface for agent interactions and conversation management. /// +/// +/// serves as the foundational class for implementing AI agents that can participate in conversations +/// and process user requests. An agent instance may participate in multiple concurrent conversations, and each conversation +/// may involve multiple agents working together. +/// public abstract class AIAgent { - /// - /// Gets the identifier of the agent. - /// - /// - /// The identifier of the agent. The default is a random GUID value, but for service agents, it will match the id of the agent in the service. - /// - public virtual string Id { get; } = Guid.NewGuid().ToString("N"); + /// Default ID of this agent instance. + private readonly string _id = Guid.NewGuid().ToString("N"); /// - /// Gets the name of the agent (optional). + /// Gets the unique identifier for this agent instance. /// + /// + /// A unique string identifier for the agent. For in-memory agents, this defaults to a randomly-generated ID, + /// while service-backed agents typically use the identifier assigned by the backing service. + /// + /// + /// Agent identifiers are used for tracking, telemetry, and distinguishing between different + /// agent instances in multi-agent scenarios. They should remain stable for the lifetime + /// of the agent instance. + /// + public virtual string Id => _id; + + /// + /// Gets the human-readable name of the agent. + /// + /// + /// The agent's name, or if no name has been assigned. + /// + /// + /// The agent name is typically used for display purposes and to help users identify + /// the agent's purpose or capabilities in user interfaces. + /// public virtual string? Name { get; } /// - /// Gets a display name for the agent, which is either the or if the name is not set. + /// Gets a display-friendly name for the agent. /// - public virtual string DisplayName => this.Name ?? this.Id; + /// + /// The agent's if available, otherwise the . + /// + /// + /// This property provides a guaranteed non-null string suitable for display in user interfaces, + /// logs, or other contexts where a readable identifier is needed. + /// + public virtual string DisplayName => this.Name ?? this.Id ?? this._id; // final fallback to _id in case Id override returns null /// - /// Gets the description of the agent (optional). + /// Gets a description of the agent's purpose, capabilities, or behavior. /// + /// + /// A descriptive text explaining what the agent does, or if no description is available. + /// + /// + /// The description helps models and users understand the agent's intended purpose and capabilities, + /// which is particularly useful in multi-agent systems. + /// public virtual string? Description { get; } /// Asks the for an object of the specified type . @@ -70,38 +104,52 @@ public abstract class AIAgent => this.GetService(typeof(TService), serviceKey) is TService service ? service : default; /// - /// Get a new instance that is compatible with the agent. + /// Creates a new conversation thread that is compatible with this agent. /// - /// A new instance. + /// A new instance ready for use with this agent. /// /// - /// If an agent supports multiple thread types, this method should return the default thread - /// type for the agent or whatever the agent was configured to use. + /// This method creates a fresh conversation thread that can be used to maintain state + /// and context for interactions with this agent. Each thread represents an independent + /// conversation session. /// /// - /// If the thread needs to be created via a service call it would be created on first use. + /// If the agent supports multiple thread types, this method returns the default or + /// configured thread type. For service-backed agents, the actual thread creation + /// may be deferred until first use to optimize performance. /// /// public abstract AgentThread GetNewThread(); /// - /// Deserialize the thread from JSON. + /// Deserializes an agent thread from its JSON serialized representation. /// - /// The representing the thread state. - /// Optional to use for deserializing the thread state. - /// The deserialized instance. + /// A containing the serialized thread state. + /// Optional settings to customize the deserialization process. + /// A restored instance with the state from . + /// The is not in the expected format. + /// The serialized data is invalid or cannot be deserialized. + /// + /// This method enables restoration of conversation threads from previously saved state, + /// allowing conversations to resume across application restarts or be migrated between + /// different agent instances. + /// public abstract AgentThread DeserializeThread(JsonElement serializedThread, JsonSerializerOptions? jsonSerializerOptions = null); /// /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread. /// /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with any response messages generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// A containing the list of items. + /// A task that represents the asynchronous operation. The task result contains an with the agent's output. + /// + /// This overload is useful when the agent has sufficient context from previous messages in the thread + /// or from its initial configuration to generate a meaningful response without additional input. + /// public Task RunAsync( AgentThread? thread = null, AgentRunOptions? options = null, @@ -109,18 +157,20 @@ public abstract class AIAgent this.RunAsync([], thread, options, cancellationToken); /// - /// Run the agent with the provided message and arguments. + /// Runs the agent with a text message from the user. /// - /// The message to pass to the agent. + /// The user message to send to the agent. /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input message and any response messages generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// A containing the list of items. + /// A task that represents the asynchronous operation. The task result contains an with the agent's output. + /// is , empty, or contains only whitespace. /// - /// The provided message string will be treated as a user message. + /// The provided text will be wrapped in a with the role + /// before being sent to the agent. This is a convenience method for simple text-based interactions. /// public Task RunAsync( string message, @@ -134,16 +184,17 @@ public abstract class AIAgent } /// - /// Run the agent with the provided message and arguments. + /// Runs the agent with a single chat message. /// - /// The message to pass to the agent. + /// The chat message to send to the agent. /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input message and any response messages generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// A containing the list of items. + /// A task that represents the asynchronous operation. The task result contains an with the agent's output. + /// is . public Task RunAsync( ChatMessage message, AgentThread? thread = null, @@ -156,16 +207,27 @@ public abstract class AIAgent } /// - /// Run the agent with the provided message and arguments. + /// Runs the agent with a collection of chat messages, providing the core invocation logic that all other overloads delegate to. /// - /// The messages to pass to the agent. + /// The collection of messages to send to the agent for processing. /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input messages and any response messages generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// A containing the list of items. + /// A task that represents the asynchronous operation. The task result contains an with the agent's output. + /// + /// + /// This is the primary invocation method that implementations must override. It handles collections of messages, + /// allowing for complex conversational scenarios including multi-turn interactions, function calls, and + /// context-rich conversations. + /// + /// + /// The messages are processed in the order provided and become part of the conversation history. + /// The agent's response will also be added to if one is provided. + /// + /// public abstract Task RunAsync( IEnumerable messages, AgentThread? thread = null, @@ -173,15 +235,15 @@ public abstract class AIAgent CancellationToken cancellationToken = default); /// - /// Run the agent with no message assuming that all required instructions are already provided to the agent or on the thread. + /// Runs the agent in streaming mode without providing new input messages, relying on existing context and instructions. /// /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with any response messages generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// An async list of response items that each contain a . + /// An asynchronous enumerable of instances representing the streaming response. public IAsyncEnumerable RunStreamingAsync( AgentThread? thread = null, AgentRunOptions? options = null, @@ -189,18 +251,20 @@ public abstract class AIAgent this.RunStreamingAsync([], thread, options, cancellationToken); /// - /// Run the agent with the provided message and arguments. + /// Runs the agent in streaming mode with a text message from the user. /// - /// The message to pass to the agent. + /// The user message to send to the agent. /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input message and any response messages generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// An async list of response items that each contain a . + /// An asynchronous enumerable of instances representing the streaming response. + /// is , empty, or contains only whitespace. /// - /// The provided message string will be treated as a user message. + /// The provided text will be wrapped in a with the role. + /// Streaming invocation provides real-time updates as the agent generates its response. /// public IAsyncEnumerable RunStreamingAsync( string message, @@ -214,16 +278,17 @@ public abstract class AIAgent } /// - /// Run the agent with the provided message and arguments. + /// Runs the agent in streaming mode with a single chat message. /// - /// The message to pass to the agent. + /// The chat message to send to the agent. /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input message and any response messages generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// An async list of response items that each contain a . + /// An asynchronous enumerable of instances representing the streaming response. + /// is . public IAsyncEnumerable RunStreamingAsync( ChatMessage message, AgentThread? thread = null, @@ -236,16 +301,26 @@ public abstract class AIAgent } /// - /// Run the agent with the provided message and arguments. + /// Runs the agent in streaming mode with a collection of chat messages, providing the core streaming invocation logic. /// - /// The messages to pass to the agent. + /// The collection of messages to send to the agent for processing. /// - /// The conversation thread to continue with this invocation. If not provided, creates a new thread. - /// The thread will be mutated with the provided messages and agent response. + /// The conversation thread to use for this invocation. If , a new thread will be created. + /// The thread will be updated with the input messages and any response updates generated during invocation. /// - /// Optional parameters for agent invocation. + /// Optional configuration parameters for controlling the agent's invocation behavior. /// The to monitor for cancellation requests. The default is . - /// An async list of response items that each contain a . + /// An asynchronous enumerable of instances representing the streaming response. + /// + /// + /// This is the primary streaming invocation method that implementations must override. It provides real-time + /// updates as the agent processes the input and generates its response, enabling more responsive user experiences. + /// + /// + /// Each represents a portion of the complete response, allowing consumers + /// to display partial results, implement progressive loading, or provide immediate feedback to users. + /// + /// public abstract IAsyncEnumerable RunStreamingAsync( IEnumerable messages, AgentThread? thread = null, @@ -253,29 +328,21 @@ public abstract class AIAgent CancellationToken cancellationToken = default); /// - /// Notfiy the given thread that new messages are available. + /// Notifies the specified thread about new messages that have been added to the conversation. /// + /// The conversation thread to notify about the new messages. + /// The collection of new messages to report to the thread. + /// The to monitor for cancellation requests. The default is . + /// A task that represents the asynchronous notification operation. + /// or is . /// /// - /// Note that while all agents should notify their threads of new messages, - /// not all threads will necessarily take action. For some treads, this may be - /// the only way that they would know that a new message is available to be added - /// to their history. - /// - /// - /// For other thread types, where history is managed by the service, the thread may - /// not need to take any action. - /// - /// - /// Where threads manage other memory components that need access to new messages, - /// notifying the thread will be important, even if the thread itself does not - /// require the message. + /// This method ensures that conversation threads are kept informed about message additions, which + /// is important for threads that manage their own state, memory components, or derived context. + /// While all agent implementations should notify their threads, the specific actions taken by + /// each thread type may vary. /// /// - /// The thread to notify of the new messages. - /// The messages to pass to the thread. - /// The to monitor for cancellation requests. The default is . - /// An async task that completes once the notification is complete. protected static async Task NotifyThreadOfNewMessagesAsync(AgentThread thread, IEnumerable messages, CancellationToken cancellationToken) { _ = Throw.IfNull(thread); diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs index 92a30ce864..e7923f4e78 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIAgentMetadata.cs @@ -2,10 +2,18 @@ namespace Microsoft.Agents.AI; -/// Provides metadata about an . +/// +/// Provides metadata information about an instance. +/// +/// +/// This class contains descriptive information about an agent that can be used for identification, +/// telemetry, and logging purposes. +/// public class AIAgentMetadata { - /// Initializes a new instance of the class. + /// + /// Initializes a new instance of the class. + /// /// /// The name of the agent provider, if applicable. Where possible, this should map to the /// appropriate name defined in the OpenTelemetry Semantic Conventions for Generative AI systems. @@ -15,7 +23,12 @@ public class AIAgentMetadata ProviderName = providerName; } - /// Gets the name of the chat provider. + /// + /// Gets the name of the agent provider. + /// + /// + /// The provider name that identifies the underlying service or implementation powering the agent. + /// /// /// Where possible, this maps to the appropriate name defined in the /// OpenTelemetry Semantic Conventions for Generative AI systems. diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContext.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContext.cs index 7a7dc9090b..c0b418b625 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContext.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContext.cs @@ -6,39 +6,101 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; /// -/// A class containing any context that should be provided to the AI model -/// as supplied by an . +/// Represents additional context information that can be dynamically provided to AI models during agent invocations. /// /// -/// Each has the ability to provide its own context for each invocation. -/// The class contains the additional context supplied by the . -/// This context will be combined with context supplied by other providers before being passed to the AI model. +/// +/// serves as a container for contextual information that instances +/// can supply to enhance AI model interactions. This context is combined across multiple providers and merged with +/// the agent's base configuration before being passed to the underlying AI model. +/// +/// +/// The context system enables dynamic, runtime-specific enhancements to agent capabilities including: +/// +/// Adding relevant background information from knowledge bases +/// Injecting task-specific instructions or guidelines +/// Providing specialized tools or functions for the current interaction +/// Including contextual messages that inform the AI about the current situation +/// +/// +/// +/// Context information is transient by default and applies only to the current invocation, though messages +/// added through the property will be permanently incorporated into the conversation history. +/// /// public sealed class AIContext { /// - /// Gets or sets any instructions to pass to the AI model in addition to any other prompts - /// that it may already have (in the case of an agent), or chat history that may - /// already exist. + /// Gets or sets additional instructions to provide to the AI model for the current invocation. /// + /// + /// Instructions text that will be combined with any existing agent instructions or system prompts, + /// or if no additional instructions should be provided. + /// /// - /// These instructions will be transient and only apply to the current invocation. + /// + /// These instructions are transient and apply only to the current AI model invocation. They are combined + /// with any existing agent instructions, system prompts, and conversation history to provide comprehensive + /// context to the AI model. + /// + /// + /// Instructions can be used to: + /// + /// Provide context-specific behavioral guidance + /// Add domain-specific knowledge or constraints + /// Modify the agent's persona or response style for the current interaction + /// Include situational awareness information + /// + /// /// public string? Instructions { get; set; } /// - /// Gets or sets a list of messages to add to the chat history. + /// Gets or sets a collection of messages to add to the conversation history. /// + /// + /// A list of instances to be permanently added to the conversation history, + /// or if no messages should be added. + /// /// - /// These messages will permanently be added to the chat history. + /// + /// Unlike and , messages added through this property become + /// permanent additions to the conversation history. They will persist beyond the current invocation and + /// will be available in future interactions within the same conversation thread. + /// + /// + /// This property is useful for: + /// + /// Injecting relevant historical context or background information + /// Adding system messages that provide ongoing context + /// Including retrieved information that should be part of the conversation record + /// Inserting contextual exchanges that inform the current conversation + /// + /// /// public IList? Messages { get; set; } /// - /// Gets or sets a list of functions/tools to make available to the AI model for the current invocation. + /// Gets or sets a collection of tools or functions to make available to the AI model for the current invocation. /// + /// + /// A list of instances that will be available to the AI model during the current invocation, + /// or if no additional tools should be provided. + /// /// - /// These functions/tools will be transient and only apply to the current invocation. + /// + /// These tools are transient and apply only to the current AI model invocation. They are combined with any + /// tools already configured for the agent to provide an expanded set of capabilities for the specific interaction. + /// + /// + /// Context-specific tools enable: + /// + /// Providing specialized functions based on user intent or conversation context + /// Adding domain-specific capabilities for particular types of queries + /// Enabling access to external services or data sources relevant to the current task + /// Offering interactive capabilities tailored to the current conversation state + /// + /// /// public IList? Tools { get; set; } } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs index 6e8e2a3531..e850b4068f 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AIContextProvider.cs @@ -11,37 +11,68 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Base class for all AI context providers. +/// Provides an abstract base class for components that enhance AI context management during agent invocations. /// /// -/// An AI context provider is a component that can be used to enhance the AI's context management. -/// It can listen to changes in the conversation, provide additional context to -/// the Model/Agent/etc. just before invocation and supply additional function tools. +/// +/// An AI context provider is a component that participates in the agent invocation lifecycle by: +/// +/// Listening to changes in conversations +/// Providing additional context to AI models or agents before invocation +/// Supplying additional function tools for enhanced capabilities +/// Processing invocation results for state management or learning +/// +/// +/// +/// Context providers operate through a two-phase lifecycle: they are called before invocation via +/// to provide context, and optionally called after invocation via +/// to process results. +/// /// public abstract class AIContextProvider { /// - /// Called just before the Model/Agent/etc. is invoked. - /// Implementers can load any additional context required at this time, - /// and they should return any context that should be passed to the Model/Agent/etc. + /// Called immediately before an AI model or agent is invoked to provide additional context. /// - /// Contains the event context. + /// Contains the request context including the messages that will be sent to the AI model or agent. /// The to monitor for cancellation requests. The default is . - /// A task that completes when the context has been rendered and returned. + /// A task that represents the asynchronous operation. The task result contains the with additional context to be provided to the AI model or agent. + /// + /// + /// Implementers can load any additional context required at this time, such as: + /// + /// Retrieving relevant information from knowledge bases + /// Adding system instructions or prompts + /// Providing function tools for the current invocation + /// Injecting contextual messages from conversation history + /// + /// + /// + /// The returned context will be combined with context from other providers before being passed to the AI model or agent. + /// + /// public abstract ValueTask InvokingAsync(InvokingContext context, CancellationToken cancellationToken = default); /// - /// Called just after the Model/Agent/etc. is invoked. - /// Implementers can use the request and response messages in the provided to update - /// any internal state or perform any necessary actions based on the outcome of the invocation. - /// E.g. extracting memories from the user messages to remember a user preference. + /// Called immediately after an AI model or agent has been invoked to process the results. /// - /// Contains the event context. + /// Contains the invocation context including request messages, response messages, and any exception that occurred. /// The to monitor for cancellation requests. The default is . - /// A task that completes when the context has been rendered and returned. + /// A task that represents the asynchronous operation. /// + /// + /// Implementers can use the request and response messages in the provided to: + /// + /// Update internal state based on conversation outcomes + /// Extract and store memories or preferences from user messages + /// Log or audit conversation details + /// Perform cleanup or finalization tasks + /// + /// + /// /// This method is called regardless of whether the invocation succeeded or failed. - /// To check if the invocation was successful, you can inspect the property. + /// To check if the invocation was successful, inspect the property. + /// /// public virtual ValueTask InvokedAsync(InvokedContext context, CancellationToken cancellationToken = default) => default; @@ -49,8 +80,12 @@ public abstract class AIContextProvider /// /// Serializes the current object's state to a using the specified serialization options. /// - /// The JSON serialization options to use. - /// A representation of the object's state. + /// The JSON serialization options to use for the serialization process. + /// A representation of the object's state, or a default if the provider has no serializable state. + /// + /// The default implementation returns a default . Override this method if the provider + /// maintains state that should be preserved across sessions or distributed scenarios. + /// public virtual JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) => default; @@ -61,7 +96,8 @@ public abstract class AIContextProvider /// is . /// /// The purpose of this method is to allow for the retrieval of strongly-typed services that might be provided by the , - /// including itself or any services it might be wrapping. + /// including itself or any services it might be wrapping. This enables advanced scenarios where consumers need access to + /// specific provider implementations or their internal services. /// public virtual object? GetService(Type serviceType, object? serviceKey = null) { @@ -78,60 +114,85 @@ public abstract class AIContextProvider /// The found object, otherwise . /// /// The purpose of this method is to allow for the retrieval of strongly typed services that may be provided by the , - /// including itself or any services it might be wrapping. + /// including itself or any services it might be wrapping. This is a convenience overload of . /// public TService? GetService(object? serviceKey = null) => this.GetService(typeof(TService), serviceKey) is TService service ? service : default; /// - /// Contains the event context provided to . + /// Contains the context information provided to . /// + /// + /// This class provides context about the upcoming AI model or agent invocation, including the messages + /// that will be sent. Context providers can use this information to determine what additional context + /// should be provided for the invocation. + /// public class InvokingContext { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the specified request messages. /// - /// The messages to be sent to the Model/Agent/etc. for this invocation. - /// Thrown if is . + /// The messages to be sent to the AI model or agent for this invocation. + /// is . public InvokingContext(IEnumerable requestMessages) { RequestMessages = requestMessages ?? throw new ArgumentNullException(nameof(requestMessages)); } /// - /// Gets the messages that will be sent to the Model/Agent/etc. for this invocation. + /// Gets the messages that will be sent to the AI model or agent for this invocation. /// + /// + /// A collection of instances representing the conversation history + /// and new messages that will be processed by the AI model or agent. + /// public IEnumerable RequestMessages { get; } } /// - /// Contains the event conext provided to . + /// Contains the context information provided to . /// + /// + /// This class provides context about a completed AI model or agent invocation, including both the + /// request messages that were sent and the response messages that were generated. It also indicates + /// whether the invocation succeeded or failed. + /// public class InvokedContext { /// /// Initializes a new instance of the class with the specified request messages. /// - /// The messages that were sent to the Model/Agent/etc. for this invocation. - /// Thrown if is . + /// The messages that were sent to the AI model or agent for this invocation. + /// is . public InvokedContext(IEnumerable requestMessages) { RequestMessages = requestMessages ?? throw new ArgumentNullException(nameof(requestMessages)); } /// - /// Gets the messages that were sent to the Model/Agent/etc. for this invocation. + /// Gets the messages that were sent to the AI model or agent for this invocation. /// + /// + /// A collection of instances representing the conversation history + /// and new messages that were processed by the AI model or agent. + /// public IEnumerable RequestMessages { get; } /// - /// Gets the collection of response messages generated by Model/Agent/etc. if the invocation succeeded. + /// Gets the collection of response messages generated by the AI model or agent if the invocation succeeded. /// + /// + /// A collection of instances representing the response from the AI model or agent, + /// or if the invocation failed or did not produce response messages. + /// public IEnumerable? ResponseMessages { get; init; } /// /// Gets the that was thrown during the invocation, if the invocation failed. /// + /// + /// The exception that caused the invocation to fail, or if the invocation succeeded. + /// public Exception? InvokeException { get; init; } } } diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs index 09edd9261c..f7bdea3f9a 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentAbstractionsJsonUtilities.cs @@ -7,11 +7,13 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; -/// Provides a collection of utility methods for working with JSON data in the context of agents. +/// +/// Provides utility methods and configurations for JSON serialization operations within the Microsoft Agent Framework. +/// public static partial class AgentAbstractionsJsonUtilities { /// - /// Gets the singleton used as the default in JSON serialization operations. + /// Gets the default instance used for JSON serialization operations of agent abstraction types. /// /// /// @@ -30,7 +32,7 @@ public static partial class AgentAbstractionsJsonUtilities public static JsonSerializerOptions DefaultOptions { get; } = CreateDefaultOptions(); /// - /// Creates default options to use for agents-related serialization. + /// Creates and configures the default JSON serialization options for agent abstraction types. /// /// The configured options. [UnconditionalSuppressMessage("ReflectionAnalysis", "IL3050:RequiresDynamicCode", Justification = "Converter is guarded by IsReflectionEnabledByDefault check.")] diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs index 4dfdc7c49d..aa7e4ce1d5 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunOptions.cs @@ -1,11 +1,12 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Optional parameters when running an agent. +/// Provides optional parameters and configuration settings for controlling agent run behavior. /// public class AgentRunOptions { @@ -17,9 +18,10 @@ public class AgentRunOptions } /// - /// Initializes a new instance of the class by cloning the provided options. + /// Initializes a new instance of the class by copying values from the specified options. /// - /// The options to clone. + /// The options instance from which to copy values. + /// is . public AgentRunOptions(AgentRunOptions options) { _ = Throw.IfNull(options); diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs index d668b398c1..fdae328956 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponse.cs @@ -22,7 +22,9 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; -/// Represents the response to an Agent run request. +/// +/// Represents the response to an run request, containing messages and metadata about the interaction. +/// /// /// provides one or more response messages and metadata about the response. /// A typical response will contain a single message, however a response may contain multiple messages @@ -41,7 +43,7 @@ public class AgentRunResponse } /// Initializes a new instance of the class. - /// The response message. + /// The response message to include in this response. /// is . public AgentRunResponse(ChatMessage message) { @@ -50,9 +52,16 @@ public class AgentRunResponse this.Messages.Add(message); } - /// Initializes a new instance of the class. - /// The from which to seed this . + /// + /// Initializes a new instance of the class from an existing . + /// + /// The from which to populate this . /// is . + /// + /// This constructor creates an agent response that wraps an existing , preserving all + /// metadata and storing the original response in for access to + /// the underlying implementation details. + /// public AgentRunResponse(ChatResponse response) { _ = Throw.IfNull(response); @@ -65,14 +74,33 @@ public class AgentRunResponse this.Usage = response.Usage; } - /// Initializes a new instance of the class. - /// The response messages. + /// + /// Initializes a new instance of the class with the specified collection of messages. + /// + /// The collection of response messages, or to create an empty response. public AgentRunResponse(IList? messages) { this._messages = messages; } - /// Gets or sets the agent response messages. + /// + /// Gets or sets the collection of messages to be represented by this response. + /// + /// + /// A collection of instances representing the agent's response. + /// If the backing collection is , accessing this property will create an empty list. + /// + /// + /// + /// This property provides access to all messages generated during the agent's execution. While most + /// responses contain a single assistant message, complex agent behaviors may produce multiple messages + /// showing intermediate steps, function calls, or different types of content. + /// + /// + /// The collection is mutable and can be modified after creation. Setting this property to + /// will cause subsequent access to return an empty list. + /// + /// [AllowNull] public IList Messages { @@ -80,35 +108,75 @@ public class AgentRunResponse set => this._messages = value; } - /// Gets the text of the response. + /// + /// Gets the concatenated text content of all messages in this response. + /// + /// + /// A string containing the combined text from all instances + /// across all messages in , or an empty string if no text content is present. + /// /// - /// This property concatenates the of all - /// instances in . + /// This property provides a convenient way to access the textual response without needing to + /// iterate through individual messages and content items. Non-text content is ignored. /// [JsonIgnore] public string Text => this._messages?.ConcatText() ?? string.Empty; - /// Gets the user input requests associated with the response. + /// + /// Gets all user input requests present in the response messages. + /// + /// + /// An enumerable collection of instances found + /// across all messages in the response. + /// /// - /// This property concatenates all instances in the response. + /// User input requests indicate that the agent is asking for additional information + /// from the user before it can continue processing. This property aggregates all such + /// requests across all messages in the response. /// [JsonIgnore] public IEnumerable UserInputRequests => this._messages?.SelectMany(x => x.Contents).OfType() ?? []; - /// Gets or sets the ID of the agent that produced the response. + /// + /// Gets or sets the identifier of the agent that generated this response. + /// + /// + /// A unique string identifier for the agent, or if not specified. + /// + /// + /// This identifier helps track which agent generated the response in multi-agent scenarios + /// or for debugging and telemetry purposes. + /// public string? AgentId { get; set; } - /// Gets or sets the ID of the agent response. + /// + /// Gets or sets the unique identifier for this specific response. + /// + /// + /// A unique string identifier for this response instance, or if not assigned. + /// public string? ResponseId { get; set; } - /// Gets or sets a timestamp for the run response. + /// + /// Gets or sets the timestamp indicating when this response was created. + /// + /// + /// A representing when the response was generated, + /// or if not specified. + /// + /// + /// The creation timestamp is useful for auditing, logging, and understanding + /// the chronology of agentic interactions. + /// public DateTimeOffset? CreatedAt { get; set; } - /// Gets or sets usage details for the run response. - /// - /// Where the agent run response is produced via many model invocations, this - /// usage is an aggregation of the usage for all these model invocations. - /// + /// + /// Gets or sets the resource usage information for generating this response. + /// + /// + /// A instance containing token counts and other usage metrics, + /// or if usage information is not available. + /// public UsageDetails? Usage { get; set; } /// Gets or sets the raw representation of the run response from an underlying implementation. @@ -120,14 +188,43 @@ public class AgentRunResponse [JsonIgnore] public object? RawRepresentation { get; set; } - /// Gets or sets any additional properties associated with the run response. + /// + /// Gets or sets additional properties associated with this response. + /// + /// + /// An containing custom properties, + /// or if no additional properties are present. + /// + /// + /// Additional properties provide a way to include custom metadata or provider-specific + /// information that doesn't fit into the standard response schema. This is useful for + /// preserving implementation-specific details or extending the response with custom data. + /// public AdditionalPropertiesDictionary? AdditionalProperties { get; set; } /// public override string ToString() => this.Text; - /// Creates an array of instances that represent this . - /// An array of instances that may be used to represent this . + /// + /// Converts this into a collection of instances + /// suitable for streaming scenarios. + /// + /// + /// An array of instances that collectively represent + /// the same information as this response. + /// + /// + /// + /// This method is useful for converting complete responses back into streaming format, + /// which may be needed for scenarios that require uniform handling of both streaming + /// and non-streaming agent responses. + /// + /// + /// Each message in becomes a separate update, and usage information + /// is included as an additional update if present. The order of updates preserves the + /// original message sequence. + /// + /// public AgentRunResponseUpdate[] ToAgentRunResponseUpdates() { AgentRunResponseUpdate? extra = null; diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseExtensions.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseExtensions.cs index adad80d890..a428fbc21d 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentRunResponseExtensions.cs @@ -19,15 +19,16 @@ namespace Microsoft.Agents.AI; public static class AgentRunResponseExtensions { /// - /// Creates a from an . + /// Creates a from an instance. /// - /// The . - /// A built from . + /// The to convert. + /// A built from the specified . + /// is . /// - /// If the 's is a - /// instance, that instance is returned directly. Otherwise, a new - /// is created and populated with the data from the . - /// The instance is a shallow copy; any reference-type members (e.g. ) + /// If the 's is already a + /// instance, that instance is returned directly. + /// Otherwise, a new is created and populated with the data from the . + /// The resulting instance is a shallow copy; any reference-type members (e.g. ) /// will be shared between the two instances. /// public static ChatResponse AsChatResponse(this AgentRunResponse response) @@ -48,15 +49,16 @@ public static class AgentRunResponseExtensions } /// - /// Creates a from an . + /// Creates a from an instance. /// - /// The . - /// A built from . + /// The to convert. + /// A built from the specified . + /// is . /// - /// If the 's is a - /// instance, that instance is returned directly. Otherwise, a new - /// is created and populated with the data from the . - /// The instance is a shallow copy; any reference-type members (e.g. ) + /// If the 's is already a + /// instance, that instance is returned directly. + /// Otherwise, a new is created and populated with the data from the . + /// The resulting instance is a shallow copy; any reference-type members (e.g. ) /// will be shared between the two instances. /// public static ChatResponseUpdate AsChatResponseUpdate(this AgentRunResponseUpdate responseUpdate) @@ -82,8 +84,9 @@ public static class AgentRunResponseExtensions /// Creates an asynchronous enumerable of instances from an asynchronous /// enumerable of instances. /// - /// The sequence . - /// A sequence of instances built from . + /// The sequence of instances to convert. + /// An asynchronous enumerable of instances built from . + /// is . /// /// Each is converted to a using /// . @@ -99,9 +102,11 @@ public static class AgentRunResponseExtensions } } - /// Combines instances into a single . - /// The updates to be combined. - /// The combined . + /// + /// Combines a sequence of instances into a single . + /// + /// The sequence of updates to be combined into a single response. + /// A single that represents the combined state of all the updates. /// is . /// /// As part of combining into a single , the method will attempt to reconstruct @@ -126,16 +131,24 @@ public static class AgentRunResponseExtensions return response; } - /// Combines instances into a single . - /// The updates to be combined. + /// + /// Asynchronously combines a sequence of instances into a single . + /// + /// The asynchronous sequence of updates to be combined into a single response. /// The to monitor for cancellation requests. The default is . - /// The combined . + /// A task that represents the asynchronous operation. The task result contains a single that represents the combined state of all the updates. /// is . /// + /// + /// This is the asynchronous version of . + /// It performs the same combining logic but operates on an asynchronous enumerable of updates. + /// + /// /// As part of combining into a single , the method will attempt to reconstruct /// instances. This includes using to determine /// message boundaries, as well as coalescing contiguous items where applicable, e.g. multiple /// instances in a row may be combined into a single . + /// /// public static Task ToAgentRunResponseAsync( this IAsyncEnumerable updates, diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThreadMetadata.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThreadMetadata.cs index cf88c48625..6c0d92d8e7 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThreadMetadata.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/AgentThreadMetadata.cs @@ -2,7 +2,9 @@ namespace Microsoft.Agents.AI; -/// Provides metadata about an . +/// +/// Provides metadata information about an instance. +/// public class AgentThreadMetadata { /// diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageStore.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageStore.cs index ae79eb5460..9f89031464 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageStore.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ChatMessageStore.cs @@ -11,40 +11,79 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Defines methods for storing and retrieving chat messages associated with a specific thread. +/// Provides an abstract base class for storing and managing chat messages associated with agent conversations. /// /// -/// Implementations of this interface are responsible for managing the storage of chat messages, -/// including handling large volumes of data by truncating or summarizing messages as necessary. +/// +/// defines the contract for persistent storage of chat messages in agent conversations. +/// Implementations are responsible for managing message persistence, retrieval, and any necessary optimization +/// strategies such as truncation, summarization, or archival. +/// +/// +/// Key responsibilities include: +/// +/// Storing chat messages with proper ordering and metadata preservation +/// Retrieving messages in chronological order for agent context +/// Managing storage limits through truncation, summarization, or other strategies +/// Supporting serialization for thread persistence and migration +/// +/// /// public abstract class ChatMessageStore { /// - /// Gets all the messages from the store that should be used for the next agent invocation. + /// Asynchronously retrieves all messages from the store that should be provided as context for the next agent invocation. /// /// The to monitor for cancellation requests. The default is . - /// A collection of chat messages. + /// + /// A task that represents the asynchronous operation. The task result contains a collection of + /// instances in ascending chronological order (oldest first). + /// /// /// - /// Messages are returned in ascending chronological order, with the oldest message first. + /// Messages are returned in chronological order to maintain proper conversation flow and context for the agent. + /// The oldest messages appear first in the collection, followed by more recent messages. /// /// - /// If the messages stored in the store become very large, it is up to the store to - /// truncate, summarize or otherwise limit the number of messages returned. + /// If the total message history becomes very large, implementations should apply appropriate strategies to manage + /// storage constraints, such as: + /// + /// Truncating older messages while preserving recent context + /// Summarizing message groups to maintain essential context + /// Implementing sliding window approaches for message retention + /// Archiving old messages while keeping active conversation context + /// /// /// - /// When using implementations of , a new one should be created for each thread - /// since they may contain state that is specific to a thread. + /// Each store instance should be associated with a single conversation thread to ensure proper message isolation + /// and context management. /// /// public abstract Task> GetMessagesAsync(CancellationToken cancellationToken = default); /// - /// Adds messages to the store. + /// Asynchronously adds new messages to the store. /// - /// The messages to add. + /// The collection of chat messages to add to the store. /// The to monitor for cancellation requests. The default is . - /// An async task. + /// A task that represents the asynchronous add operation. + /// is . + /// + /// + /// Messages should be added in the order they were generated to maintain proper chronological sequence. + /// The store is responsible for preserving message ordering and ensuring that subsequent calls to + /// return messages in the correct chronological order. + /// + /// + /// Implementations may perform additional processing during message addition, such as: + /// + /// Validating message content and metadata + /// Applying storage optimizations or compression + /// Triggering background maintenance operations + /// Updating indices or search capabilities + /// + /// + /// public abstract Task AddMessagesAsync(IEnumerable messages, CancellationToken cancellationToken = default); /// diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs index a83c523fce..353c82c996 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/DelegatingAIAgent.cs @@ -11,24 +11,46 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Provides an optional base class for an that passes through calls to another instance. +/// Provides an abstract base class for AI agents that delegate operations to an inner agent +/// instance while allowing for extensibility and customization. /// /// -/// This is recommended as a base type when building agents that can be chained around an underlying . -/// The default implementation simply passes each call to the inner agent instance. +/// +/// implements the decorator pattern for s, enabling the creation of agent pipelines +/// where each layer can add functionality while delegating core operations to an underlying agent. This pattern is +/// fundamental to building composable agent architectures. +/// +/// +/// The default implementation provides transparent pass-through behavior, forwarding all operations to the inner agent. +/// Derived classes can override specific methods to add custom behavior while maintaining compatibility with the agent interface. +/// /// public class DelegatingAIAgent : AIAgent { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class with the specified inner agent. /// - /// The wrapped agent instance. + /// The underlying agent instance that will handle the core operations. + /// is . + /// + /// The inner agent serves as the foundation of the delegation chain. All operations not overridden by + /// derived classes will be forwarded to this agent. + /// protected DelegatingAIAgent(AIAgent innerAgent) { this.InnerAgent = Throw.IfNull(innerAgent); } - /// Gets the inner . + /// + /// Gets the inner agent instance that receives delegated operations. + /// + /// + /// The underlying instance that handles core agent operations. + /// + /// + /// Derived classes can use this property to access the inner agent for custom delegation scenarios + /// or to forward operations with additional processing. + /// protected AIAgent InnerAgent { get; } /// diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentThread.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentThread.cs index 8affd74da6..096ad17c30 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryAgentThread.cs @@ -10,36 +10,66 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; /// -/// A base class for agent threads that operate entirely in memory without external storage. +/// Provides an abstract base class for agent threads that maintain all conversation state in local memory. /// +/// +/// +/// is designed for scenarios where conversation state should be stored locally +/// rather than in external services or databases. This approach provides high performance and simplicity while +/// maintaining full control over the conversation data. +/// +/// +/// In-memory threads do not persist conversation data across application restarts +/// unless explicitly serialized and restored. +/// +/// public abstract class InMemoryAgentThread : AgentThread { /// /// Initializes a new instance of the class. /// - /// An optional to use for storing chat messages. If null, a new instance will be created. + /// + /// An optional instance to use for storing chat messages. + /// If , a new empty message store will be created. + /// + /// + /// This constructor allows sharing of message stores between threads or providing pre-configured + /// message stores with specific reduction or processing logic. + /// protected InMemoryAgentThread(InMemoryChatMessageStore? messageStore = null) { this.MessageStore = messageStore ?? []; } /// - /// Initializes a new instance of the class with the specified initial messages. + /// Initializes a new instance of the class. /// - /// The messages to initialize the thread with. + /// The initial messages to populate the conversation history. + /// is . + /// + /// This constructor is useful for initializing threads with existing conversation history or + /// for migrating conversations from other storage systems. + /// protected InMemoryAgentThread(IEnumerable messages) { this.MessageStore = [.. messages]; } /// - /// Initializes a new instance of the class from serialized state. + /// Initializes a new instance of the class from previously serialized state. /// /// A representing the serialized state of the thread. /// Optional settings for customizing the JSON deserialization process. - /// A factory function to create the from its serialized state. + /// + /// Optional factory function to create the from its serialized state. + /// If not provided, a default factory will be used that creates a basic in-memory store. + /// /// The is not a JSON object. /// The is invalid or cannot be deserialized to the expected type. + /// + /// This constructor enables restoration of in-memory threads from previously saved state, allowing + /// conversations to be resumed across application restarts or migrated between different instances. + /// protected InMemoryAgentThread( JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null, diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatMessageStore.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatMessageStore.cs index f8cf5e8754..b14573ada1 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatMessageStore.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/InMemoryChatMessageStore.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.Collections; using System.Collections.Generic; using System.Linq; @@ -12,8 +13,19 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Represents an in-memory store for chat messages associated with a specific thread. +/// Provides an in-memory implementation of with support for message reduction and collection semantics. /// +/// +/// +/// stores chat messages entirely in local memory, providing fast access and manipulation +/// capabilities. It implements both for agent integration and +/// for direct collection manipulation. +/// +/// +/// This store maintains all messages in memory. For long-running conversations or high-volume scenarios, consider using +/// message reduction strategies or alternative storage implementations. +/// +/// public sealed class InMemoryChatMessageStore : ChatMessageStore, IList { private List _messages; @@ -21,16 +33,26 @@ public sealed class InMemoryChatMessageStore : ChatMessageStore, IList /// Initializes a new instance of the class. /// + /// + /// This constructor creates a basic in-memory store without message reduction capabilities. + /// Messages will be stored exactly as added without any automatic processing or reduction. + /// public InMemoryChatMessageStore() { this._messages = []; } /// - /// Initializes a new instance of the class, with an existing state from a serialized JSON element. + /// Initializes a new instance of the class from previously serialized state. /// - /// A representing the serialized state of the store. + /// A representing the serialized state of the message store. /// Optional settings for customizing the JSON deserialization process. + /// The is not a valid JSON object or cannot be deserialized. + /// + /// This constructor enables restoration of message stores from previously saved state, allowing + /// conversation history to be preserved across application restarts or migrated between instances. + /// The store will be configured with default settings and message reduction before retrieval. + /// public InMemoryChatMessageStore(JsonElement serializedStoreState, JsonSerializerOptions? jsonSerializerOptions = null) : this(null, serializedStoreState, jsonSerializerOptions, ChatReducerTriggerEvent.BeforeMessagesRetrieval) { @@ -39,8 +61,19 @@ public sealed class InMemoryChatMessageStore : ChatMessageStore, IList /// Initializes a new instance of the class. /// - /// An optional instance used to process or reduce chat messages. If null, no reduction logic will be applied. - /// The event that should trigger the reducer invocation. + /// + /// A instance used to process, reduce, or optimize chat messages. + /// This can be used to implement strategies like message summarization, truncation, or cleanup. + /// + /// + /// Specifies when the message reducer should be invoked. The default is , + /// which applies reduction logic when messages are retrieved for agent consumption. + /// + /// is . + /// + /// Message reducers enable automatic management of message storage by implementing strategies to + /// keep memory usage under control while preserving important conversation context. + /// public InMemoryChatMessageStore(IChatReducer chatReducer, ChatReducerTriggerEvent reducerTriggerEvent = ChatReducerTriggerEvent.BeforeMessagesRetrieval) : this(chatReducer, default, null, reducerTriggerEvent) { diff --git a/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentThread.cs b/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentThread.cs index 4d322ec33f..030d9eb600 100644 --- a/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI.Abstractions/ServiceIdAgentThread.cs @@ -7,33 +7,47 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// A base class for agent threads that always store conversation state in the service, and only keep an ID reference in the . +/// Provides a base class for agent threads that store conversation state remotely in a service and maintain only an identifier reference locally. /// +/// +/// This class is designed for scenarios where conversation state is managed by an external service (such as a cloud-based AI service) +/// rather than being stored locally. The thread maintains only the service identifier needed to reference the remote conversation state. +/// public abstract class ServiceIdAgentThread : AgentThread { /// - /// Initializes a new instance of the class. + /// Initializes a new instance of the class without a service thread identifier. /// + /// + /// When using this constructor, the will be initially + /// and should be set by derived classes when the remote conversation is created. + /// protected ServiceIdAgentThread() { } /// - /// Initializes a new instance of the class with the specified service thread ID. + /// Initializes a new instance of the class with the specified service thread identifier. /// - /// The ID that the conversation state is stored under in the service. + /// The unique identifier that references the conversation state stored in the remote service. + /// is . + /// is empty or contains only whitespace. protected ServiceIdAgentThread(string serviceThreadId) { this.ServiceThreadId = Throw.IfNullOrEmpty(serviceThreadId); } /// - /// Initializes a new instance of the class from serialized state. + /// Initializes a new instance of the class from previously serialized state. /// /// A representing the serialized state of the thread. /// Optional settings for customizing the JSON deserialization process. /// The is not a JSON object. /// The is invalid or cannot be deserialized to the expected type. + /// + /// This constructor enables restoration of a service-backed thread from serialized state, typically used + /// when deserializing thread information that was previously saved or transmitted across application boundaries. + /// protected ServiceIdAgentThread( JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null) @@ -53,15 +67,28 @@ public abstract class ServiceIdAgentThread : AgentThread } /// - /// Gets the ID that the conversation state is stored under in the service. + /// Gets or sets the unique identifier that references the conversation state stored in the remote service. /// + /// + /// A string identifier that uniquely identifies the conversation within the remote service, + /// or if no remote conversation has been established yet. + /// + /// + /// This identifier is used by derived classes to reference the remote conversation state when making + /// API calls to the backing service. The exact format and meaning of this identifier depends on the + /// specific service implementation. + /// protected string? ServiceThreadId { get; set; } /// /// Serializes the current object's state to a using the specified serialization options. /// - /// The JSON serialization options to use. - /// A representation of the object's state. + /// The JSON serialization options to use for the serialization process. + /// A representation of the object's state, containing the service thread identifier. + /// + /// The serialized state contains only the service thread identifier, as all other conversation state + /// is maintained remotely by the backing service. This makes the serialized representation very lightweight. + /// public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { var state = new ServiceIdAgentThreadState diff --git a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs b/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs index fe1656b92a..a74da3ed20 100644 --- a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs +++ b/dotnet/src/Microsoft.Agents.AI/AIAgentBuilder.cs @@ -10,7 +10,9 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; -/// A builder for creating pipelines of . +/// +/// Provides a builder for creating pipelines of s. +/// public sealed class AIAgentBuilder { private readonly Func _innerAgentFactory; @@ -29,12 +31,21 @@ public sealed class AIAgentBuilder /// Initializes a new instance of the class. /// A callback that produces the inner that represents the underlying backend. + /// is . public AIAgentBuilder(Func innerAgentFactory) { this._innerAgentFactory = Throw.IfNull(innerAgentFactory); } - /// + /// Builds an that represents the entire pipeline. + /// + /// The that should provide services to the instances. + /// If , an empty will be used. + /// + /// An instance of that represents the entire pipeline. + /// + /// Calls to the resulting instance will pass through each of the pipeline stages in turn. + /// public AIAgent Build(IServiceProvider? services = null) { services ??= EmptyServiceProvider.Instance; @@ -58,7 +69,10 @@ public sealed class AIAgentBuilder return agent; } - /// + /// Adds a factory for an intermediate agent to the agent pipeline. + /// The agent factory function. + /// The updated instance. + /// is . public AIAgentBuilder Use(Func agentFactory) { _ = Throw.IfNull(agentFactory); @@ -66,7 +80,10 @@ public sealed class AIAgentBuilder return this.Use((innerAgent, _) => agentFactory(innerAgent)); } - /// + /// Adds a factory for an intermediate agent to the agent pipeline. + /// The agent factory function. + /// The updated instance. + /// is . public AIAgentBuilder Use(Func agentFactory) { _ = Throw.IfNull(agentFactory); diff --git a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilderAIAgentExtensions.cs b/dotnet/src/Microsoft.Agents.AI/AIAgentBuilderAIAgentExtensions.cs deleted file mode 100644 index fa758926f1..0000000000 --- a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilderAIAgentExtensions.cs +++ /dev/null @@ -1,25 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Agents.AI; - -/// Provides extension methods for working with in the context of . -public static class AIAgentBuilderAIAgentExtensions -{ - /// Creates a new using as its inner agent. - /// The agent to use as the inner agent. - /// The new instance. - /// - /// This method is equivalent to using the constructor directly, - /// specifying as the inner agent. - /// - /// is . - public static AIAgentBuilder AsBuilder(this AIAgent innerAgent) - { - _ = Throw.IfNull(innerAgent); - - return new AIAgentBuilder(innerAgent); - } -} diff --git a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI/AIAgentBuilderExtensions.cs index f837c5d4f7..a5a280d039 100644 --- a/dotnet/src/Microsoft.Agents.AI/AIAgentBuilderExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI/AIAgentBuilderExtensions.cs @@ -9,20 +9,31 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Provides extension methods for configuring an instance. +/// Provides extension methods for configuring and customizing instances. /// -/// This class contains methods that extend the functionality of the to -/// allow additional customization and behavior injection. public static class AIAgentBuilderExtensions { /// - /// Adds a middleware to the AI agent pipeline that intercepts and processes invocations. + /// Adds function invocation callbacks to the pipeline that intercepts and processes calls. /// - /// The to which the middleware is added. - /// A delegate that processes function invocations. The delegate receives the invocation context, the next - /// middleware in the pipeline, and a cancellation token, and returns a task representing the result of the - /// invocation. - /// The instance with the middleware added. + /// The to which the function invocation callback is added. + /// + /// A delegate that processes function invocations. The delegate receives the instance, + /// the function invocation context, and a continuation delegate representing the next callback in the pipeline. + /// It returns a task representing the result of the function invocation. + /// + /// The instance with the function invocation callback added, enabling method chaining. + /// or is . + /// + /// + /// The callback must call the provided continuation delegate to proceed with the function invocation, + /// unless it intends to completely replace the function's behavior. + /// + /// + /// The inner agent or the pipeline wrapping it must include a . If one does not exist, + /// the added to the pipline by this method will throw an exception when it is invoked. + /// + /// public static AIAgentBuilder Use(this AIAgentBuilder builder, Func>, CancellationToken, ValueTask> callback) { _ = Throw.IfNull(builder); @@ -38,4 +49,50 @@ public static class AIAgentBuilderExtensions return new FunctionInvocationDelegatingAgent(innerAgent, callback); }); } + + /// + /// Adds OpenTelemetry instrumentation to the agent pipeline, enabling comprehensive observability for agent operations. + /// + /// The to which OpenTelemetry support will be added. + /// + /// An optional source name that will be used to identify telemetry data from this agent. + /// If not specified, a default source name will be used. + /// + /// + /// An optional callback that provides additional configuration of the instance. + /// This allows for fine-tuning telemetry behavior such as enabling sensitive data collection. + /// + /// The with OpenTelemetry instrumentation added, enabling method chaining. + /// is . + /// + /// + /// This extension adds comprehensive telemetry capabilities to AI agents, including: + /// + /// Distributed tracing of agent invocations + /// Performance metrics and timing information + /// Request and response payload logging (when enabled) + /// Error tracking and exception details + /// Usage statistics and token consumption metrics + /// + /// + /// + /// The implementation follows the OpenTelemetry Semantic Conventions for Generative AI systems as defined at + /// . + /// + /// + /// Note: The OpenTelemetry specification for Generative AI is still experimental and subject to change. + /// As the specification evolves, the telemetry output from this agent may also change to maintain compliance. + /// + /// + public static AIAgentBuilder UseOpenTelemetry( + this AIAgentBuilder builder, + string? sourceName = null, + Action? configure = null) => + Throw.IfNull(builder).Use((innerAgent, services) => + { + var agent = new OpenTelemetryAgent(innerAgent, sourceName); + configure?.Invoke(agent); + + return agent; + }); } diff --git a/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs b/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs index bc8c788495..e58fbf4920 100644 --- a/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs +++ b/dotnet/src/Microsoft.Agents.AI/AgentExtensions.cs @@ -1,5 +1,6 @@ // Copyright (c) Microsoft. All rights reserved. +using System; using System.ComponentModel; using System.Threading; using System.Threading.Tasks; @@ -9,17 +10,59 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Extension methods for . +/// Provides extensions for . /// -public static class AgentExtensions +public static class AIAgentExtensions { /// - /// Creates a that will invoke the provided Agent. + /// Creates a new using the specified agent as the foundation for the builder pipeline. /// - /// The to be represented via the created . - /// Metadata to use to override defaults inferred from . - /// The to use for the function. - /// The created for invoking the . + /// The instance to use as the inner agent. + /// A new instance configured with the specified inner agent. + /// is . + /// + /// This method provides a convenient way to convert an existing instance into + /// a builder pattern, enabling easily wrapping the agent in layers of additional functionality. + /// It is functionally equivalent to using the constructor directly, + /// but provides a more fluent API when working with existing agent instances. + /// + public static AIAgentBuilder AsBuilder(this AIAgent innerAgent) + { + _ = Throw.IfNull(innerAgent); + + return new AIAgentBuilder(innerAgent); + } + + /// + /// Creates an that runs the provided . + /// + /// The to be represented as an invocable function. + /// + /// Optional metadata to customize the function representation, such as name and description. + /// If not provided, defaults will be inferred from the agent's properties. + /// + /// + /// Optional to use for function invocations. If not provided, a new thread + /// will be created for each function call, which may not preserve conversation context. + /// + /// + /// An that can be used as a tool by other agents or AI models to invoke this agent. + /// + /// is . + /// + /// + /// This extension method enables agents to participate in function calling scenarios, where they can be + /// invoked as tools by other agents or AI models. The resulting function accepts a query string as input and + /// returns the agent's response as a string, making it compatible with standard function calling interfaces + /// used by AI models. + /// + /// + /// The resulting is stateful, referencing both the and the optional + /// . Especially if a specific thread is provided, avoid using the resulting function concurrently + /// in multiple conversations or in requests where the parallel function calls may result in concurrent usage of the thread, + /// as that could lead to undefined and unpredictable behavior. + /// + /// public static AIFunction AsAIFunction(this AIAgent agent, AIFunctionFactoryOptions? options = null, AgentThread? thread = null) { Throw.IfNull(agent); diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs index 4f297f6eca..226cf18d2c 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgent.cs @@ -17,7 +17,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Represents an agent that can be invoked using a chat client. +/// Provides an that delegates to an implementation. /// public sealed class ChatClientAgent : AIAgent { @@ -29,13 +29,33 @@ public sealed class ChatClientAgent : AIAgent /// /// Initializes a new instance of the class. /// - /// The chat client to use for invoking the agent. - /// Optional instructions for the agent. - /// Optional name for the agent. - /// Optional description for the agent. - /// Optional list of tools that the agent can use during invocation. - /// Optional logger factory to use for logging. - /// An optional to use for resolving services required by the instances being invoked. + /// The chat client to use when running the agent. + /// + /// Optional system instructions that guide the agent's behavior. These instructions are provided to the + /// with each invocation to establish the agent's role and behavior. + /// + /// + /// Optional name for the agent. This name is used for identification and logging purposes. + /// + /// + /// Optional human-readable description of the agent's purpose and capabilities. + /// This description can be useful for documentation and agent discovery scenarios. + /// + /// + /// Optional collection of tools that the agent can invoke during conversations. + /// These tools augment any tools that may be provided to the agent via when + /// the agent is run. + /// + /// + /// Optional logger factory for creating loggers used by the agent and its components. + /// + /// + /// Optional service provider for resolving dependencies required by AI functions and other agent components. + /// This is particularly important when using custom tools that require dependency injection. + /// This is only relevant when the doesn't already contain a + /// and the needs to insert one. + /// + /// is . public ChatClientAgent(IChatClient chatClient, string? instructions = null, string? name = null, string? description = null, IList? tools = null, ILoggerFactory? loggerFactory = null, IServiceProvider? services = null) : this( chatClient, @@ -57,10 +77,21 @@ public sealed class ChatClientAgent : AIAgent /// /// Initializes a new instance of the class. /// - /// The chat client to use for invoking the agent. - /// Full set of options to configure the agent. - /// Optional logger factory to use for logging. - /// An optional to use for resolving services required by the instances being invoked. + /// The chat client to use when running the agent. + /// + /// Configuration options that control all aspects of the agent's behavior, including chat settings, + /// message store factories, context provider factories, and other advanced configurations. + /// + /// + /// Optional logger factory for creating loggers used by the agent and its components. + /// + /// + /// Optional service provider for resolving dependencies required by AI functions and other agent components. + /// This is particularly important when using custom tools that require dependency injection. + /// This is only relevant when the doesn't already contain a + /// and the needs to insert one. + /// + /// is . public ChatClientAgent(IChatClient chatClient, ChatClientAgentOptions? options, ILoggerFactory? loggerFactory = null, IServiceProvider? services = null) { _ = Throw.IfNull(chatClient); @@ -82,6 +113,13 @@ public sealed class ChatClientAgent : AIAgent /// /// Gets the underlying chat client used by the agent to invoke chat completions. /// + /// + /// The instance that backs this agent. + /// + /// + /// This may return the original client provided when the was constructed, or it may + /// return a pipeline of decorating instances applied around that inner client. + /// public IChatClient ChatClient { get; } /// @@ -94,8 +132,17 @@ public sealed class ChatClientAgent : AIAgent public override string? Description => this._agentOptions?.Description; /// - /// Gets the instructions for the agent (optional). + /// Gets the system instructions that guide the agent's behavior during conversations. /// + /// + /// A string containing the system instructions that are provided to the underlying chat client + /// to establish the agent's role, personality, and behavioral guidelines. May be + /// if no specific instructions were configured. + /// + /// + /// These instructions are typically provided to the AI model as system messages to establish + /// the context and expected behavior for the agent's responses. + /// public string? Instructions => this._agentOptions?.Instructions; /// @@ -282,13 +329,21 @@ public sealed class ChatClientAgent : AIAgent }; /// - /// Get a new instance using an existing conversation id, to continue that conversation. + /// Creates a new agent thread instance using an existing conversation identifier to continue that conversation. /// - /// The conversation id to continue. - /// A new instance. + /// The identifier of an existing conversation to continue. + /// + /// A new instance configured to work with the specified conversation. + /// /// - /// Note that any created with this method will only work with instances that support storing - /// chat history in the underlying service provided by the . + /// + /// This method creates threads that rely on server-side conversation storage, where the chat history + /// is maintained by the underlying AI service rather than in local message stores. + /// + /// + /// Agent threads created with this method will only work with + /// instances that support server-side conversation storage through their underlying . + /// /// public AgentThread GetNewThread(string conversationId) => new ChatClientAgentThread() diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunOptions.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunOptions.cs index 30e69f5b76..0f2c9da485 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunOptions.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentRunOptions.cs @@ -6,24 +6,48 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; /// -/// Chat client agent run options. +/// Provides specialized run options for instances, extending the base agent run options with chat-specific configuration. /// +/// +/// This class extends to provide additional configuration options that are specific to +/// chat client agents, in particular . +/// public sealed class ChatClientAgentRunOptions : AgentRunOptions { /// /// Initializes a new instance of the class. /// - /// Optional chat options to pass to the agent's invocation. + /// + /// Optional chat options to customize the behavior of the chat client during this specific agent invocation. + /// These options will be merged with the default chat options configured for the agent. + /// public ChatClientAgentRunOptions(ChatOptions? chatOptions = null) { this.ChatOptions = chatOptions; } - /// Gets or sets optional chat options to pass to the agent's invocation. + /// + /// Gets or sets the chat options to apply to the agent invocation. + /// + /// + /// Chat options that control various aspects of the chat client's behavior, such as temperature, max tokens, + /// tools, instructions, and other model-specific parameters. If , the agent's default + /// chat options will be used. + /// + /// + /// These options are specific to this invocation and will be combined with the agent's default chat options. + /// If both the agent and this run options specify the same option, the run options value typically takes precedence. + /// In the case of collections, like , the collections will be unioned. + /// public ChatOptions? ChatOptions { get; set; } /// - /// Gets or sets the factory method used to modify instances of per-request. + /// Gets or sets a factory function that can replace (typically via decorators) the chat client on a per-request basis. /// + /// + /// A function that receives the agent's configured chat client and returns a potentially modified or entirely + /// different chat client to use for this specific invocation. If , the agent's default + /// chat client will be used without modification. + /// public Func? ChatClientFactory { get; set; } } diff --git a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentThread.cs b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentThread.cs index 4a44862497..3152c28ea0 100644 --- a/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentThread.cs +++ b/dotnet/src/Microsoft.Agents.AI/ChatClient/ChatClientAgentThread.cs @@ -12,7 +12,7 @@ using Microsoft.Shared.Diagnostics; namespace Microsoft.Agents.AI; /// -/// Thread for ChatClient based agents. +/// Provides a thread implementation for use with . /// public class ChatClientAgentThread : AgentThread { @@ -27,12 +27,18 @@ public class ChatClientAgentThread : AgentThread } /// - /// Initializes a new instance of the class from serialized state. + /// Initializes a new instance of the class from previously serialized state. /// /// A representing the serialized state of the thread. /// Optional settings for customizing the JSON deserialization process. - /// An optional factory function to create a custom . - /// An optional factory function to create a custom . + /// + /// An optional factory function to create a custom from its serialized state. + /// If not provided, the default in-memory message store will be used. + /// + /// + /// An optional factory function to create a custom from its serialized state. + /// If not provided, no context provider will be configured. + /// internal ChatClientAgentThread( JsonElement serializedThreadState, JsonSerializerOptions? jsonSerializerOptions = null, @@ -152,13 +158,9 @@ public class ChatClientAgentThread : AgentThread /// public override JsonElement Serialize(JsonSerializerOptions? jsonSerializerOptions = null) { - JsonElement? storeState = this._messageStore is null ? - null : - this._messageStore.Serialize(jsonSerializerOptions); + JsonElement? storeState = this._messageStore?.Serialize(jsonSerializerOptions); - JsonElement? aiContextProviderState = this.AIContextProvider is null ? - null : - this.AIContextProvider.Serialize(jsonSerializerOptions); + JsonElement? aiContextProviderState = this.AIContextProvider?.Serialize(jsonSerializerOptions); var state = new ThreadState { diff --git a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAIAgentBuilderExtensions.cs b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAIAgentBuilderExtensions.cs deleted file mode 100644 index 8cf0dbfcd6..0000000000 --- a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAIAgentBuilderExtensions.cs +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) Microsoft. All rights reserved. - -using System; -using Microsoft.Shared.Diagnostics; - -namespace Microsoft.Agents.AI; - -/// Provides extensions for configuring instances. -public static class OpenTelemetryAIAgentBuilderExtensions -{ - /// - /// Adds OpenTelemetry support to the agent pipeline for agent runs, following the OpenTelemetry Semantic Conventions for Generative AI systems. - /// - /// - /// The draft specification this follows is available at . - /// The specification is still experimental and subject to change; as such, the telemetry output by this agent is also subject to change. - /// - /// The . - /// An optional source name that will be used on the telemetry data. - /// An optional callback that can be used to configure the instance. - /// The . - public static AIAgentBuilder UseOpenTelemetry( - this AIAgentBuilder builder, - string? sourceName = null, - Action? configure = null) => - Throw.IfNull(builder).Use((innerAgent, services) => - { - var agent = new OpenTelemetryAgent(innerAgent, sourceName); - configure?.Invoke(agent); - - return agent; - }); -} diff --git a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs index a155feb3c9..715e6dbd26 100644 --- a/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs +++ b/dotnet/src/Microsoft.Agents.AI/OpenTelemetryAgent.cs @@ -10,9 +10,11 @@ using Microsoft.Extensions.AI; namespace Microsoft.Agents.AI; -/// Represents a delegating that implements the OpenTelemetry Semantic Conventions for Generative AI systems. +/// +/// Provides a delegating implementation that implements the OpenTelemetry Semantic Conventions for Generative AI systems. +/// /// -/// This class provides an implementation of the Semantic Conventions for Generative AI systems, defined at . +/// This class provides an implementation of the Semantic Conventions for Generative AI systems v1.37, defined at . /// The specification is still experimental and subject to change; as such, the telemetry output by this client is also subject to change. /// public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable @@ -32,9 +34,16 @@ public sealed class OpenTelemetryAgent : DelegatingAIAgent, IDisposable private readonly string? _providerName; /// Initializes a new instance of the class. - /// The underlying . - /// An optional source name that will be used on the telemetry data. + /// The underlying to be augmented with telemetry capabilities. + /// + /// An optional source name that will be used to identify telemetry data from this agent. + /// If not provided, a default source name will be used for telemetry identification. + /// /// is . + /// + /// The constructor automatically extracts provider metadata from the inner agent and configures + /// telemetry collection according to OpenTelemetry semantic conventions for AI systems. + /// public OpenTelemetryAgent(AIAgent innerAgent, string? sourceName = null) : base(innerAgent) { this._providerName = innerAgent.GetService()?.ProviderName; diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs index b6559d30d9..f7ad1ebcdc 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/AgentExtensionsTests.cs @@ -12,7 +12,7 @@ using Moq; namespace Microsoft.Agents.AI.UnitTests; /// -/// Unit tests for the method. +/// Unit tests for the method. /// public class AgentExtensionsTests { @@ -21,7 +21,7 @@ public class AgentExtensionsTests { // Act & Assert var exception = Assert.Throws(() => - AgentExtensions.AsAIFunction(null!)); + AIAgentExtensions.AsAIFunction(null!)); Assert.Equal("agent", exception.ParamName); } diff --git a/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAIAgentBuilderExtensionsTests.cs b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAIAgentBuilderExtensionsTests.cs index 5d895f9553..309d940b9d 100644 --- a/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAIAgentBuilderExtensionsTests.cs +++ b/dotnet/tests/Microsoft.Agents.AI.UnitTests/OpenTelemetryAIAgentBuilderExtensionsTests.cs @@ -7,7 +7,7 @@ using Moq; namespace Microsoft.Agents.AI.UnitTests; /// -/// Unit tests for the class. +/// Unit tests for the class. /// public class OpenTelemetryAIAgentBuilderExtensionsTests { @@ -18,8 +18,7 @@ public class OpenTelemetryAIAgentBuilderExtensionsTests public void UseOpenTelemetry_WithNullBuilder_ThrowsArgumentNullException() { // Act & Assert - Assert.Throws("builder", () => - OpenTelemetryAIAgentBuilderExtensions.UseOpenTelemetry(null!)); + Assert.Throws("builder", () => ((AIAgentBuilder)null!).UseOpenTelemetry()); } ///