From 4148f0abff0a9209453e446ad3a94b9d29ef0637 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Fri, 11 Oct 2024 16:17:55 -0400 Subject: [PATCH] Add thread-safety comments about M.E.AI middleware components --- .../ChatCompletion/IChatClient.cs | 14 +++++++++ .../Embeddings/IEmbeddingGenerator.cs | 13 ++++++++ .../ConfigureOptionsChatClient.cs | 7 +++++ .../DistributedCachingChatClient.cs | 4 +++ .../FunctionInvokingChatClient.cs | 31 +++++++++++++++++++ .../ChatCompletion/LoggingChatClient.cs | 4 +++ .../DistributedCachingEmbeddingGenerator.cs | 4 +++ .../Embeddings/LoggingEmbeddingGenerator.cs | 4 +++ 8 files changed, 81 insertions(+) diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/IChatClient.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/IChatClient.cs index e9839cab2ae..8cbfa1314f4 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/IChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/IChatClient.cs @@ -9,6 +9,20 @@ namespace Microsoft.Extensions.AI; /// Represents a chat completion client. +/// +/// +/// Unless otherwise specified, all members of are thread-safe for concurrent use. +/// It is expected that all implementations of support being used by multiple requests concurrently. +/// +/// +/// However, implementations of may mutate the arguments supplied to and +/// , such as by adding additional messages to the messages list or configuring the options +/// instance. Thus, consumers of the interface either should avoid using shared instances of these arguments for concurrent +/// invocations or should otherwise ensure by construction that no instances are used which might employ +/// such mutation. For example, the WithChatOptions method be provided with a callback that could mutate the supplied options +/// argument, and that should be avoided if using a singleton options instance. +/// +/// public interface IChatClient : IDisposable { /// Sends chat messages to the model and returns the response messages. diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Embeddings/IEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Embeddings/IEmbeddingGenerator.cs index 6c791ee2bf4..5cc289fbb5e 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Embeddings/IEmbeddingGenerator.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Embeddings/IEmbeddingGenerator.cs @@ -11,6 +11,19 @@ namespace Microsoft.Extensions.AI; /// Represents a generator of embeddings. /// The type from which embeddings will be generated. /// The type of embeddings to generate. +/// +/// +/// Unless otherwise specified, all members of are thread-safe for concurrent use. +/// It is expected that all implementations of support being used by multiple requests concurrently. +/// +/// +/// However, implementations of may mutate the arguments supplied to +/// , such as by adding additional values to the values list or configuring the options +/// instance. Thus, consumers of the interface either should avoid using shared instances of these arguments for concurrent +/// invocations or should otherwise ensure by construction that no instances +/// are used which might employ such mutation. +/// +/// public interface IEmbeddingGenerator : IDisposable where TEmbedding : Embedding { diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ConfigureOptionsChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ConfigureOptionsChatClient.cs index a8a4b9269e2..895bf8873df 100644 --- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ConfigureOptionsChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/ConfigureOptionsChatClient.cs @@ -14,6 +14,7 @@ namespace Microsoft.Extensions.AI; /// A delegating chat client that updates or replaces the used by the remainder of the pipeline. /// +/// /// The configuration callback is invoked with the caller-supplied instance. To override the caller-supplied options /// with a new instance, the callback may simply return that new instance, for example _ => new ChatOptions() { MaxTokens = 1000 }. To provide /// a new instance only if the caller-supplied instance is `null`, the callback may conditionally return a new instance, for example @@ -28,6 +29,12 @@ namespace Microsoft.Extensions.AI; /// return newOptions; /// } /// +/// +/// +/// The provided implementation of is thread-safe for concurrent use so long as the employed configuration +/// callback is also thread-safe for concurrent requests. If callers employ a shared options instance, care should be taken in the +/// configuration callback, as multiple calls to it may end up running in parallel with the same options instance. +/// /// public sealed class ConfigureOptionsChatClient : DelegatingChatClient { diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/DistributedCachingChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/DistributedCachingChatClient.cs index 65c50c090bd..8c247d73fb3 100644 --- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/DistributedCachingChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/DistributedCachingChatClient.cs @@ -13,6 +13,10 @@ namespace Microsoft.Extensions.AI; /// /// A delegating chat client that caches the results of completion calls, storing them as JSON in an . /// +/// +/// The provided implementation of is thread-safe for concurrent use so long as the employed +/// is similarly thread-safe for concurrent use. +/// public class DistributedCachingChatClient : CachingChatClient { private readonly IDistributedCache _storage; diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs index 16e9d62f25b..94b87c9a7b1 100644 --- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/FunctionInvokingChatClient.cs @@ -17,9 +17,22 @@ namespace Microsoft.Extensions.AI; /// Include this in a chat pipeline to resolve function calls automatically. /// /// +/// /// When this client receives a in a chat completion, it responds /// by calling the corresponding defined in , /// producing a . +/// +/// +/// The provided implementation of is thread-safe for concurrent use so long as the +/// instances employed as part of the supplied are also safe. +/// The property may be used to control whether multiple function invocation +/// requests as part of the same request are invocable concurrently, but even with that set to +/// (the default), multiple concurrent requests to this same instance and using the same tools could result in those +/// tools being used concurrently (one per request). For example, a function that accesses the HttpContext of a specific +/// ASP.NET web request should only be used as part of a single at a time, and only with +/// set to , in case the inner client decided to issue multiple +/// invocation requests to that same function. +/// /// public class FunctionInvokingChatClient : DelegatingChatClient { @@ -49,6 +62,10 @@ public FunctionInvokingChatClient(IChatClient innerClient) /// to continue attempting function calls until is reached. /// /// + /// Changing the value of this property while the client is in use may result in inconsistencies + /// as to whether errors are retried during an in-flight request. + /// + /// /// The default value is . /// /// @@ -73,6 +90,10 @@ public FunctionInvokingChatClient(IChatClient innerClient) /// result in disclosing the raw exception information to external users, which may be a security /// concern depending on the application scenario. /// + /// + /// Changing the value of this property while the client is in use may result in inconsistencies + /// as to whether detailed errors are provided during an in-flight request. + /// /// public bool DetailedErrors { get; set; } @@ -95,6 +116,7 @@ public FunctionInvokingChatClient(IChatClient innerClient) /// Gets or sets a value indicating whether to keep intermediate messages in the chat history. /// /// + /// /// When the inner returns to the /// , the adds /// those messages to the list of messages, along with instances @@ -104,6 +126,11 @@ public FunctionInvokingChatClient(IChatClient innerClient) /// messages will persist in the list provided to /// and by the caller. Set /// to to remove those messages prior to completing the operation. + /// + /// + /// Changing the value of this property while the client is in use may result in inconsistencies + /// as to whether function calling messages are kept during an in-flight request. + /// /// public bool KeepFunctionCallingMessages { get; set; } = true; @@ -120,6 +147,10 @@ public FunctionInvokingChatClient(IChatClient innerClient) /// must be at least one, as it includes the initial request. /// /// + /// Changing the value of this property while the client is in use may result in inconsistencies + /// as to how many iterations are allowed for an in-flight request. + /// + /// /// The default value is . /// /// diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs index 25a936eb646..1c268aa08a9 100644 --- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/LoggingChatClient.cs @@ -13,6 +13,10 @@ namespace Microsoft.Extensions.AI; /// A delegating chat client that logs chat operations to an . +/// +/// The provided implementation of is thread-safe for concurrent use so long as the +/// employed is also thread-safe for concurrent use. +/// public partial class LoggingChatClient : DelegatingChatClient { /// An instance used for all logging. diff --git a/src/Libraries/Microsoft.Extensions.AI/Embeddings/DistributedCachingEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/Embeddings/DistributedCachingEmbeddingGenerator.cs index 932bb2f91b8..eda857462a2 100644 --- a/src/Libraries/Microsoft.Extensions.AI/Embeddings/DistributedCachingEmbeddingGenerator.cs +++ b/src/Libraries/Microsoft.Extensions.AI/Embeddings/DistributedCachingEmbeddingGenerator.cs @@ -16,6 +16,10 @@ namespace Microsoft.Extensions.AI; /// /// The type from which embeddings will be generated. /// The type of embeddings to generate. +/// +/// The provided implementation of is thread-safe for concurrent +/// use so long as the employed is similarly thread-safe for concurrent use. +/// public class DistributedCachingEmbeddingGenerator : CachingEmbeddingGenerator where TEmbedding : Embedding { diff --git a/src/Libraries/Microsoft.Extensions.AI/Embeddings/LoggingEmbeddingGenerator.cs b/src/Libraries/Microsoft.Extensions.AI/Embeddings/LoggingEmbeddingGenerator.cs index e42f51f602f..cef4c203020 100644 --- a/src/Libraries/Microsoft.Extensions.AI/Embeddings/LoggingEmbeddingGenerator.cs +++ b/src/Libraries/Microsoft.Extensions.AI/Embeddings/LoggingEmbeddingGenerator.cs @@ -15,6 +15,10 @@ namespace Microsoft.Extensions.AI; /// A delegating embedding generator that logs embedding generation operations to an . /// Specifies the type of the input passed to the generator. /// Specifies the type of the embedding instance produced by the generator. +/// +/// The provided implementation of is thread-safe for concurrent use +/// so long as the employed is also thread-safe for concurrent use. +/// public partial class LoggingEmbeddingGenerator : DelegatingEmbeddingGenerator where TEmbedding : Embedding {