22using Microsoft . Extensions . DependencyInjection ;
33using Microsoft . Extensions . Logging ;
44using OllamaSharp ;
5+ using OpenTelemetry ;
56
67namespace Microsoft . Extensions . Hosting ;
78
@@ -10,6 +11,8 @@ namespace Microsoft.Extensions.Hosting;
1011/// </summary>
1112public static class AspireOllamaChatClientExtensions
1213{
14+ private const string MeaiTelemetrySourceName = "Experimental.Microsoft.Extensions.AI" ;
15+
1316 /// <summary>
1417 /// Registers a singleton <see cref="IChatClient"/> in the services provided by the <paramref name="builder"/>.
1518 /// </summary>
@@ -19,8 +22,25 @@ public static ChatClientBuilder AddChatClient(this AspireOllamaApiClientBuilder
1922 {
2023 ArgumentNullException . ThrowIfNull ( builder , nameof ( builder ) ) ;
2124
25+ return builder . AddChatClient ( configureOpenTelemetry : null ) ;
26+ }
27+
28+ /// <summary>
29+ /// Registers a singleton <see cref="IChatClient"/> in the services provided by the <paramref name="builder"/>.
30+ /// </summary>
31+ /// <param name="builder">An <see cref="AspireOllamaApiClientBuilder" />.</param>
32+ /// <param name="configureOpenTelemetry">An optional delegate that can be used for customizing the OpenTelemetry chat client.</param>
33+ /// <returns>A <see cref="ChatClientBuilder"/> that can be used to build a pipeline around the inner <see cref="IChatClient"/>.</returns>
34+ public static ChatClientBuilder AddChatClient (
35+ this AspireOllamaApiClientBuilder builder ,
36+ Action < OpenTelemetryChatClient > ? configureOpenTelemetry )
37+ {
38+ ArgumentNullException . ThrowIfNull ( builder , nameof ( builder ) ) ;
39+
40+ AddTelemetrySource ( builder . HostBuilder ) ;
41+
2242 return builder . HostBuilder . Services . AddChatClient (
23- services => CreateInnerChatClient ( services , builder ) ) ;
43+ services => CreateInnerChatClient ( services , builder , configureOpenTelemetry ) ) ;
2444 }
2545
2646 /// <summary>
@@ -33,7 +53,22 @@ public static ChatClientBuilder AddKeyedChatClient(
3353 {
3454 ArgumentNullException . ThrowIfNull ( builder , nameof ( builder ) ) ;
3555
36- return builder . AddKeyedChatClient ( builder . ServiceKey ) ;
56+ return builder . AddKeyedChatClient ( builder . ServiceKey , configureOpenTelemetry : null ) ;
57+ }
58+
59+ /// <summary>
60+ /// Registers a keyed singleton <see cref="IChatClient"/> in the services provided by the <paramref name="builder"/>.
61+ /// </summary>
62+ /// <param name="builder">An <see cref="AspireOllamaApiClientBuilder" />.</param>
63+ /// <param name="configureOpenTelemetry">An optional delegate that can be used for customizing the OpenTelemetry chat client.</param>
64+ /// <returns>A <see cref="ChatClientBuilder"/> that can be used to build a pipeline around the inner <see cref="IChatClient"/>.</returns>
65+ public static ChatClientBuilder AddKeyedChatClient (
66+ this AspireOllamaApiClientBuilder builder ,
67+ Action < OpenTelemetryChatClient > ? configureOpenTelemetry )
68+ {
69+ ArgumentNullException . ThrowIfNull ( builder , nameof ( builder ) ) ;
70+
71+ return builder . AddKeyedChatClient ( builder . ServiceKey , configureOpenTelemetry ) ;
3772 }
3873
3974 /// <summary>
@@ -49,9 +84,29 @@ public static ChatClientBuilder AddKeyedChatClient(
4984 ArgumentNullException . ThrowIfNull ( builder , nameof ( builder ) ) ;
5085 ArgumentNullException . ThrowIfNull ( serviceKey , nameof ( serviceKey ) ) ;
5186
87+ return builder . AddKeyedChatClient ( serviceKey , configureOpenTelemetry : null ) ;
88+ }
89+
90+ /// <summary>
91+ /// Registers a keyed singleton <see cref="IChatClient"/> in the services provided by the <paramref name="builder"/> using the specified service key.
92+ /// </summary>
93+ /// <param name="builder">An <see cref="AspireOllamaApiClientBuilder" />.</param>
94+ /// <param name="serviceKey">The service key to use for registering the <see cref="IChatClient"/>.</param>
95+ /// <param name="configureOpenTelemetry">An optional delegate that can be used for customizing the OpenTelemetry chat client.</param>
96+ /// <returns>A <see cref="ChatClientBuilder"/> that can be used to build a pipeline around the inner <see cref="IChatClient"/>.</returns>
97+ public static ChatClientBuilder AddKeyedChatClient (
98+ this AspireOllamaApiClientBuilder builder ,
99+ object serviceKey ,
100+ Action < OpenTelemetryChatClient > ? configureOpenTelemetry )
101+ {
102+ ArgumentNullException . ThrowIfNull ( builder , nameof ( builder ) ) ;
103+ ArgumentNullException . ThrowIfNull ( serviceKey , nameof ( serviceKey ) ) ;
104+
105+ AddTelemetrySource ( builder . HostBuilder ) ;
106+
52107 return builder . HostBuilder . Services . AddKeyedChatClient (
53108 serviceKey ,
54- services => CreateInnerChatClient ( services , builder ) ) ;
109+ services => CreateInnerChatClient ( services , builder , configureOpenTelemetry ) ) ;
55110 }
56111
57112 /// <summary>
@@ -61,7 +116,8 @@ public static ChatClientBuilder AddKeyedChatClient(
61116 /// </summary>
62117 private static IChatClient CreateInnerChatClient (
63118 IServiceProvider services ,
64- AspireOllamaApiClientBuilder builder )
119+ AspireOllamaApiClientBuilder builder ,
120+ Action < OpenTelemetryChatClient > ? configureOpenTelemetry )
65121 {
66122 var ollamaApiClient = services . GetRequiredKeyedService < IOllamaApiClient > ( builder . ServiceKey ) ;
67123
@@ -73,6 +129,22 @@ private static IChatClient CreateInnerChatClient(
73129 }
74130
75131 var loggerFactory = services . GetService < ILoggerFactory > ( ) ;
76- return new OpenTelemetryChatClient ( result , loggerFactory ? . CreateLogger ( typeof ( OpenTelemetryChatClient ) ) ) ;
132+ var otelChatClient = new OpenTelemetryChatClient ( result , loggerFactory ? . CreateLogger ( typeof ( OpenTelemetryChatClient ) ) , MeaiTelemetrySourceName ) ;
133+
134+ configureOpenTelemetry ? . Invoke ( otelChatClient ) ;
135+
136+ return otelChatClient ;
137+ }
138+
139+ /// <summary>
140+ /// Add the MEAI telemetry source to OpenTelemetry tracing.
141+ /// </summary>
142+ private static void AddTelemetrySource ( IHostApplicationBuilder hostBuilder )
143+ {
144+ hostBuilder . Services . AddOpenTelemetry ( )
145+ . WithTracing ( tracing =>
146+ {
147+ tracing . AddSource ( MeaiTelemetrySourceName ) ;
148+ } ) ;
77149 }
78150}
0 commit comments