66// We disable this warning because it is a false positive arising from the analyzer's lack of support for C#'s primary
77// constructor syntax.
88
9+ using System ;
910using System . Collections . Generic ;
1011using System . Diagnostics ;
1112using System . Linq ;
1516
1617namespace Microsoft . Extensions . AI . Evaluation . Safety ;
1718
18- internal sealed partial class ContentSafetyChatClient (
19- ContentSafetyServiceConfiguration contentSafetyServiceConfiguration ,
20- IChatClient ? originalChatClient = null ) : DelegatingChatClient ( originalChatClient ?? NoOpChatClient . Instance )
19+ internal sealed class ContentSafetyChatClient : IChatClient
2120{
22- private readonly ContentSafetyService _service = new ContentSafetyService ( contentSafetyServiceConfiguration ) ;
21+ private const string Moniker = "Azure AI Content Safety" ;
2322
24- public override async Task < ChatResponse > GetResponseAsync (
23+ private readonly ContentSafetyService _service ;
24+ private readonly IChatClient ? _originalChatClient ;
25+ private readonly ChatClientMetadata _metadata ;
26+
27+ public ContentSafetyChatClient (
28+ ContentSafetyServiceConfiguration contentSafetyServiceConfiguration ,
29+ IChatClient ? originalChatClient = null )
30+ {
31+ _service = new ContentSafetyService ( contentSafetyServiceConfiguration ) ;
32+ _originalChatClient = originalChatClient ;
33+
34+ ChatClientMetadata ? originalMetadata = _originalChatClient ? . GetService < ChatClientMetadata > ( ) ;
35+
36+ string providerName =
37+ $ "{ Moniker } (" +
38+ $ "Subscription: { contentSafetyServiceConfiguration . SubscriptionId } , " +
39+ $ "Resource Group: { contentSafetyServiceConfiguration . ResourceGroupName } , " +
40+ $ "Project: { contentSafetyServiceConfiguration . ProjectName } )";
41+
42+ if ( originalMetadata ? . ProviderName is string originalProviderName &&
43+ ! string . IsNullOrWhiteSpace ( originalProviderName ) )
44+ {
45+ providerName = $ "{ originalProviderName } ; { providerName } ";
46+ }
47+
48+ string modelId = Moniker ;
49+
50+ if ( originalMetadata ? . DefaultModelId is string originalModelId &&
51+ ! string . IsNullOrWhiteSpace ( originalModelId ) )
52+ {
53+ modelId = $ "{ originalModelId } ; { modelId } ";
54+ }
55+
56+ _metadata = new ChatClientMetadata ( providerName , originalMetadata ? . ProviderUri , modelId ) ;
57+ }
58+
59+ public async Task < ChatResponse > GetResponseAsync (
2560 IEnumerable < ChatMessage > messages ,
2661 ChatOptions ? options = null ,
2762 CancellationToken cancellationToken = default )
@@ -38,15 +73,25 @@ await _service.AnnotateAsync(
3873 contentSafetyChatOptions . EvaluatorName ,
3974 cancellationToken ) . ConfigureAwait ( false ) ;
4075
41- return new ChatResponse ( new ChatMessage ( ChatRole . Assistant , annotationResult ) ) ;
76+ return new ChatResponse ( new ChatMessage ( ChatRole . Assistant , annotationResult ) )
77+ {
78+ ModelId = Moniker
79+ } ;
80+ }
81+ else if ( _originalChatClient is not null )
82+ {
83+ return await _originalChatClient . GetResponseAsync (
84+ messages ,
85+ options ,
86+ cancellationToken ) . ConfigureAwait ( false ) ;
4287 }
4388 else
4489 {
45- return await base . GetResponseAsync ( messages , options , cancellationToken ) . ConfigureAwait ( false ) ;
90+ throw new NotSupportedException ( ) ;
4691 }
4792 }
4893
49- public override async IAsyncEnumerable < ChatResponseUpdate > GetStreamingResponseAsync (
94+ public async IAsyncEnumerable < ChatResponseUpdate > GetStreamingResponseAsync (
5095 IEnumerable < ChatMessage > messages ,
5196 ChatOptions ? options = null ,
5297 [ EnumeratorCancellation ] CancellationToken cancellationToken = default )
@@ -63,15 +108,45 @@ await _service.AnnotateAsync(
63108 contentSafetyChatOptions . EvaluatorName ,
64109 cancellationToken ) . ConfigureAwait ( false ) ;
65110
66- yield return new ChatResponseUpdate ( ChatRole . Assistant , annotationResult ) ;
111+ yield return new ChatResponseUpdate ( ChatRole . Assistant , annotationResult )
112+ {
113+ ModelId = Moniker
114+ } ;
67115 }
68- else
116+ else if ( _originalChatClient is not null )
69117 {
70118 await foreach ( var update in
71- base . GetStreamingResponseAsync ( messages , options , cancellationToken ) . ConfigureAwait ( false ) )
119+ _originalChatClient . GetStreamingResponseAsync (
120+ messages ,
121+ options ,
122+ cancellationToken ) . ConfigureAwait ( false ) )
72123 {
73124 yield return update ;
74125 }
75126 }
127+ else
128+ {
129+ throw new NotSupportedException ( ) ;
130+ }
131+ }
132+
133+ public object ? GetService ( Type serviceType , object ? serviceKey = null )
134+ {
135+ if ( serviceKey is null )
136+ {
137+ if ( serviceType == typeof ( ChatClientMetadata ) )
138+ {
139+ return _metadata ;
140+ }
141+ else if ( serviceType == typeof ( ContentSafetyChatClient ) )
142+ {
143+ return this ;
144+ }
145+ }
146+
147+ return _originalChatClient ? . GetService ( serviceType , serviceKey ) ;
76148 }
149+
150+ public void Dispose ( )
151+ => _originalChatClient ? . Dispose ( ) ;
77152}
0 commit comments