diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/CachingChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/CachingChatClient.cs
index 770ffa60cfc..f2de7f92fc8 100644
--- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/CachingChatClient.cs
+++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/CachingChatClient.cs
@@ -1,6 +1,7 @@
// Licensed to the .NET Foundation under one or more agreements.
// The .NET Foundation licenses this file to you under the MIT license.
+using System;
using System.Collections.Generic;
using System.Runtime.CompilerServices;
using System.Threading;
@@ -16,6 +17,12 @@ namespace Microsoft.Extensions.AI;
///
public abstract class CachingChatClient : DelegatingChatClient
{
+ /// A boxed value.
+ private static readonly object _boxedTrue = true;
+
+ /// A boxed value.
+ private static readonly object _boxedFalse = false;
+
/// Initializes a new instance of the class.
/// The underlying .
protected CachingChatClient(IChatClient innerClient)
@@ -45,7 +52,7 @@ public override async Task CompleteAsync(IList chat
// We're only storing the final result, not the in-flight task, so that we can avoid caching failures
// or having problems when one of the callers cancels but others don't. This has the drawback that
// concurrent callers might trigger duplicate requests, but that's acceptable.
- var cacheKey = GetCacheKey(false, chatMessages, options);
+ var cacheKey = GetCacheKey(_boxedFalse, chatMessages, options);
if (await ReadCacheAsync(cacheKey, cancellationToken).ConfigureAwait(false) is not { } result)
{
@@ -68,7 +75,7 @@ public override async IAsyncEnumerable CompleteSt
// we make a streaming request, yielding those results, but then convert those into a non-streaming
// result and cache it. When we get a cache hit, we yield the non-streaming result as a streaming one.
- var cacheKey = GetCacheKey(true, chatMessages, options);
+ var cacheKey = GetCacheKey(_boxedTrue, chatMessages, options);
if (await ReadCacheAsync(cacheKey, cancellationToken).ConfigureAwait(false) is { } chatCompletion)
{
// Yield all of the cached items.
@@ -93,7 +100,7 @@ public override async IAsyncEnumerable CompleteSt
}
else
{
- var cacheKey = GetCacheKey(true, chatMessages, options);
+ var cacheKey = GetCacheKey(_boxedTrue, chatMessages, options);
if (await ReadCacheStreamingAsync(cacheKey, cancellationToken).ConfigureAwait(false) is { } existingChunks)
{
// Yield all of the cached items.
@@ -118,14 +125,10 @@ public override async IAsyncEnumerable CompleteSt
}
}
- ///
- /// Computes a cache key for the specified call parameters.
- ///
- /// A flag to indicate if this is a streaming call.
- /// The chat content.
- /// The chat options to configure the request.
- /// A string that will be used as a cache key.
- protected abstract string GetCacheKey(bool streaming, IList chatMessages, ChatOptions? options);
+ /// Computes a cache key for the specified values.
+ /// The values to inform the key.
+ /// The computed key.
+ protected abstract string GetCacheKey(params ReadOnlySpan