From 5982f6abc369fee9654fdff92d1a939d20e87f60 Mon Sep 17 00:00:00 2001 From: Stephen Toub Date: Mon, 18 Nov 2024 10:52:08 -0500 Subject: [PATCH] Reduce a bit of LINQ in M.E.AI (#5663) --- .../AdditionalPropertiesDictionary.cs | 3 +- .../ChatCompletion/ChatMessage.cs | 8 +-- .../StreamingChatCompletionUpdate.cs | 8 +-- .../Contents/AIContentExtensions.cs | 64 +++++++++++++++++++ .../ChatCompletion/OpenTelemetryChatClient.cs | 2 +- 5 files changed, 73 insertions(+), 12 deletions(-) create mode 100644 src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContentExtensions.cs diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/AdditionalPropertiesDictionary.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/AdditionalPropertiesDictionary.cs index 8b8d69896b..c780c1ccaf 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/AdditionalPropertiesDictionary.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/AdditionalPropertiesDictionary.cs @@ -111,7 +111,8 @@ public sealed class AdditionalPropertiesDictionary : IDictionary _dictionary.Clear(); /// - bool ICollection>.Contains(KeyValuePair item) => _dictionary.Contains(item); + bool ICollection>.Contains(KeyValuePair item) => + ((ICollection>)_dictionary).Contains(item); /// public bool ContainsKey(string key) => _dictionary.ContainsKey(key); diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatMessage.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatMessage.cs index 6370319704..d52cc36cdb 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatMessage.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/ChatMessage.cs @@ -3,7 +3,6 @@ using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Text.Json.Serialization; using Microsoft.Shared.Diagnostics; @@ -60,10 +59,10 @@ public class ChatMessage [JsonIgnore] public string? Text { - get => Contents.OfType().FirstOrDefault()?.Text; + get => Contents.FindFirst()?.Text; set { - if (Contents.OfType().FirstOrDefault() is { } textContent) + if (Contents.FindFirst() is { } textContent) { textContent.Text = value; } @@ -95,6 +94,5 @@ public class ChatMessage public AdditionalPropertiesDictionary? AdditionalProperties { get; set; } /// - public override string ToString() => - string.Concat(Contents.OfType()); + public override string ToString() => Contents.ConcatText(); } diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/StreamingChatCompletionUpdate.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/StreamingChatCompletionUpdate.cs index 9978e0f29b..36ae500e13 100644 --- a/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/StreamingChatCompletionUpdate.cs +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/ChatCompletion/StreamingChatCompletionUpdate.cs @@ -4,7 +4,6 @@ using System; using System.Collections.Generic; using System.Diagnostics.CodeAnalysis; -using System.Linq; using System.Text.Json.Serialization; namespace Microsoft.Extensions.AI; @@ -66,10 +65,10 @@ public class StreamingChatCompletionUpdate [JsonIgnore] public string? Text { - get => Contents.OfType().FirstOrDefault()?.Text; + get => Contents.FindFirst()?.Text; set { - if (Contents.OfType().FirstOrDefault() is { } textContent) + if (Contents.FindFirst() is { } textContent) { textContent.Text = value; } @@ -116,6 +115,5 @@ public class StreamingChatCompletionUpdate public string? ModelId { get; set; } /// - public override string ToString() => - string.Concat(Contents.OfType()); + public override string ToString() => Contents.ConcatText(); } diff --git a/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContentExtensions.cs b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContentExtensions.cs new file mode 100644 index 0000000000..eb516e2a7c --- /dev/null +++ b/src/Libraries/Microsoft.Extensions.AI.Abstractions/Contents/AIContentExtensions.cs @@ -0,0 +1,64 @@ +// 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; +#if !NET +using System.Linq; +#else +using System.Runtime.CompilerServices; +#endif + +namespace Microsoft.Extensions.AI; + +/// Internal extensions for working with . +internal static class AIContentExtensions +{ + /// Finds the first occurrence of a in the list. + public static T? FindFirst(this IList contents) + where T : AIContent + { + int count = contents.Count; + for (int i = 0; i < count; i++) + { + if (contents[i] is T t) + { + return t; + } + } + + return null; + } + + /// Concatenates the text of all instances in the list. + public static string ConcatText(this IList contents) + { + int count = contents.Count; + switch (count) + { + case 0: + break; + + case 1: + return contents[0] is TextContent tc ? tc.Text : string.Empty; + + default: +#if NET + DefaultInterpolatedStringHandler builder = new(0, 0, null, stackalloc char[512]); + for (int i = 0; i < count; i++) + { + if (contents[i] is TextContent text) + { + builder.AppendLiteral(text.Text); + } + } + + return builder.ToStringAndClear(); +#else + return string.Concat(contents.OfType()); +#endif + } + + return string.Empty; + } +} diff --git a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs index 7cf26e5944..193006780a 100644 --- a/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs +++ b/src/Libraries/Microsoft.Extensions.AI/ChatCompletion/OpenTelemetryChatClient.cs @@ -502,7 +502,7 @@ public sealed partial class OpenTelemetryChatClient : DelegatingChatClient { if (EnableSensitiveData) { - string content = string.Concat(message.Contents.OfType().Select(c => c.Text)); + string content = string.Concat(message.Contents.OfType()); if (content.Length > 0) { return content;