11using System . Text ;
2+ using Cnblogs . DashScope . Core ;
23using Cnblogs . DashScope . Sdk . UnitTests . Utils ;
34using FluentAssertions ;
45using Microsoft . Extensions . AI ;
56using NSubstitute ;
7+ using NSubstitute . Extensions ;
68
79namespace Cnblogs . DashScope . Sdk . UnitTests ;
810
@@ -12,9 +14,13 @@ public class ChatClientTests
1214 public async Task ChatClient_TextCompletion_SuccessAsync ( )
1315 {
1416 // Arrange
15- const bool sse = false ;
16- var testCase = Snapshots . TextGeneration . MessageFormat . SingleMessage ;
17- var ( dashScopeClient , handler ) = await Sut . GetTestClientAsync ( sse , testCase ) ;
17+ var testCase = Snapshots . TextGeneration . MessageFormat . SingleChatClientMessage ;
18+ var dashScopeClient = Substitute . For < IDashScopeClient > ( ) ;
19+ dashScopeClient . Configure ( )
20+ . GetTextCompletionAsync (
21+ Arg . Any < ModelRequest < TextGenerationInput , ITextGenerationParameters > > ( ) ,
22+ Arg . Any < CancellationToken > ( ) )
23+ . Returns ( Task . FromResult ( testCase . ResponseModel ) ) ;
1824 var client = dashScopeClient . AsChatClient ( testCase . RequestModel . Model ) ;
1925 var content = testCase . RequestModel . Input . Messages ! . First ( ) . Content ;
2026 var parameter = testCase . RequestModel . Parameters ;
@@ -25,15 +31,20 @@ public async Task ChatClient_TextCompletion_SuccessAsync()
2531 new ChatOptions ( )
2632 {
2733 FrequencyPenalty = parameter ? . RepetitionPenalty ,
34+ PresencePenalty = parameter ? . PresencePenalty ,
2835 ModelId = testCase . RequestModel . Model ,
2936 MaxOutputTokens = parameter ? . MaxTokens ,
3037 Seed = ( long ? ) parameter ? . Seed ,
3138 Temperature = parameter ? . Temperature ,
39+ TopK = parameter ? . TopK ,
40+ TopP = parameter ? . TopP ,
41+ ToolMode = ChatToolMode . Auto
3242 } ) ;
3343
3444 // Assert
35- handler . Received ( ) . MockSend (
36- Arg . Any < HttpRequestMessage > ( ) ,
45+ _ = dashScopeClient . Received ( ) . GetTextCompletionAsync (
46+ Arg . Is < ModelRequest < TextGenerationInput , ITextGenerationParameters > > (
47+ m => IsEquivalent ( m , testCase . RequestModel ) ) ,
3748 Arg . Any < CancellationToken > ( ) ) ;
3849 response . Message . Text . Should ( ) . Be ( testCase . ResponseModel . Output . Choices ? . First ( ) . Message . Content ) ;
3950 }
@@ -43,8 +54,15 @@ public async Task ChatClient_TextCompletionStream_SuccessAsync()
4354 {
4455 // Arrange
4556 const bool sse = true ;
46- var testCase = Snapshots . TextGeneration . MessageFormat . SingleMessageIncremental ;
47- var ( dashScopeClient , handler ) = await Sut . GetTestClientAsync ( sse , testCase ) ;
57+ var testCase = Snapshots . TextGeneration . MessageFormat . SingleMessageChatClientIncremental ;
58+ var dashScopeClient = Substitute . For < IDashScopeClient > ( ) ;
59+ var returnThis = new [ ] { testCase . ResponseModel } . ToAsyncEnumerable ( ) ;
60+ dashScopeClient
61+ . Configure ( )
62+ . GetTextCompletionStreamAsync (
63+ Arg . Any < ModelRequest < TextGenerationInput , ITextGenerationParameters > > ( ) ,
64+ Arg . Any < CancellationToken > ( ) )
65+ . Returns ( returnThis ) ;
4866 var client = dashScopeClient . AsChatClient ( testCase . RequestModel . Model ) ;
4967 var content = testCase . RequestModel . Input . Messages ! . First ( ) . Content ;
5068 var parameter = testCase . RequestModel . Parameters ;
@@ -55,10 +73,15 @@ public async Task ChatClient_TextCompletionStream_SuccessAsync()
5573 new ChatOptions ( )
5674 {
5775 FrequencyPenalty = parameter ? . RepetitionPenalty ,
76+ PresencePenalty = parameter ? . PresencePenalty ,
5877 ModelId = testCase . RequestModel . Model ,
5978 MaxOutputTokens = parameter ? . MaxTokens ,
6079 Seed = ( long ? ) parameter ? . Seed ,
6180 Temperature = parameter ? . Temperature ,
81+ TopK = parameter ? . TopK ,
82+ TopP = parameter ? . TopP ,
83+ StopSequences = [ "你好" ] ,
84+ ToolMode = ChatToolMode . Auto
6285 } ) ;
6386 var text = new StringBuilder ( ) ;
6487 await foreach ( var update in response )
@@ -67,9 +90,118 @@ public async Task ChatClient_TextCompletionStream_SuccessAsync()
6790 }
6891
6992 // Assert
70- handler . Received ( ) . MockSend (
71- Arg . Any < HttpRequestMessage > ( ) ,
93+ _ = dashScopeClient . Received ( ) . GetTextCompletionStreamAsync (
94+ Arg . Is < ModelRequest < TextGenerationInput , ITextGenerationParameters > > (
95+ m => IsEquivalent ( m , testCase . RequestModel ) ) ,
7296 Arg . Any < CancellationToken > ( ) ) ;
7397 text . ToString ( ) . Should ( ) . Be ( testCase . ResponseModel . Output . Choices ? . First ( ) . Message . Content ) ;
7498 }
99+
100+ [ Fact ]
101+ public async Task ChatClient_ImageRecognition_SuccessAsync ( )
102+ {
103+ // Arrange
104+ const bool sse = false ;
105+ var testCase = Snapshots . MultimodalGeneration . VlChatClientNoSse ;
106+ var dashScopeClient = Substitute . For < IDashScopeClient > ( ) ;
107+ dashScopeClient . Configure ( )
108+ . GetMultimodalGenerationAsync (
109+ Arg . Any < ModelRequest < MultimodalInput , IMultimodalParameters > > ( ) ,
110+ Arg . Any < CancellationToken > ( ) )
111+ . Returns ( Task . FromResult ( testCase . ResponseModel ) ) ;
112+ var client = dashScopeClient . AsChatClient ( testCase . RequestModel . Model ) ;
113+ var contents = testCase . RequestModel . Input . Messages . Last ( ) . Content ;
114+ var messages = new List < ChatMessage >
115+ {
116+ new (
117+ ChatRole . User ,
118+ [ new ImageContent ( contents [ 0 ] . Image ! ) , new TextContent ( contents [ 1 ] . Text ) ] )
119+ } ;
120+ var parameter = testCase . RequestModel . Parameters ;
121+
122+ // Act
123+ var response = await client . CompleteAsync (
124+ messages ,
125+ new ChatOptions
126+ {
127+ FrequencyPenalty = parameter ? . RepetitionPenalty ,
128+ PresencePenalty = parameter ? . PresencePenalty ,
129+ ModelId = testCase . RequestModel . Model ,
130+ MaxOutputTokens = parameter ? . MaxTokens ,
131+ Seed = ( long ? ) parameter ? . Seed ,
132+ Temperature = parameter ? . Temperature ,
133+ TopK = parameter ? . TopK ,
134+ TopP = parameter ? . TopP ,
135+ } ) ;
136+
137+ // Assert
138+ await dashScopeClient . Received ( ) . GetMultimodalGenerationAsync (
139+ Arg . Is < ModelRequest < MultimodalInput , IMultimodalParameters > > ( m => IsEquivalent ( m , testCase . RequestModel ) ) ,
140+ Arg . Any < CancellationToken > ( ) ) ;
141+ response . Choices [ 0 ] . Text . Should ( )
142+ . BeEquivalentTo ( testCase . ResponseModel . Output . Choices [ 0 ] . Message . Content [ 0 ] . Text ) ;
143+ }
144+
145+ [ Fact ]
146+ public async Task ChatClient_ImageRecognitionStream_SuccessAsync ( )
147+ {
148+ // Arrange
149+ const bool sse = true ;
150+ var testCase = Snapshots . MultimodalGeneration . VlChatClientSse ;
151+ var dashScopeClient = Substitute . For < IDashScopeClient > ( ) ;
152+ dashScopeClient . Configure ( )
153+ . GetMultimodalGenerationStreamAsync (
154+ Arg . Any < ModelRequest < MultimodalInput , IMultimodalParameters > > ( ) ,
155+ Arg . Any < CancellationToken > ( ) )
156+ . Returns ( new [ ] { testCase . ResponseModel } . ToAsyncEnumerable ( ) ) ;
157+ var client = dashScopeClient . AsChatClient ( testCase . RequestModel . Model ) ;
158+ var contents = testCase . RequestModel . Input . Messages . Last ( ) . Content ;
159+ var messages = new List < ChatMessage >
160+ {
161+ new (
162+ ChatRole . User ,
163+ [ new ImageContent ( contents [ 0 ] . Image ! ) , new TextContent ( contents [ 1 ] . Text ) ] )
164+ } ;
165+ var parameter = testCase . RequestModel . Parameters ;
166+
167+ // Act
168+ var response = client . CompleteStreamingAsync (
169+ messages ,
170+ new ChatOptions ( )
171+ {
172+ FrequencyPenalty = parameter ? . RepetitionPenalty ,
173+ PresencePenalty = parameter ? . PresencePenalty ,
174+ ModelId = testCase . RequestModel . Model ,
175+ MaxOutputTokens = parameter ? . MaxTokens ,
176+ Seed = ( long ? ) parameter ? . Seed ,
177+ Temperature = parameter ? . Temperature ,
178+ TopK = parameter ? . TopK ,
179+ TopP = parameter ? . TopP ,
180+ } ) ;
181+ var text = new StringBuilder ( ) ;
182+ await foreach ( var update in response )
183+ {
184+ text . Append ( update . Text ) ;
185+ }
186+
187+ // Assert
188+ _ = dashScopeClient . Received ( ) . GetMultimodalGenerationStreamAsync (
189+ Arg . Is < ModelRequest < MultimodalInput , IMultimodalParameters > > ( m => IsEquivalent ( m , testCase . RequestModel ) ) ,
190+ Arg . Any < CancellationToken > ( ) ) ;
191+ text . ToString ( ) . Should ( ) . Be ( testCase . ResponseModel . Output . Choices . First ( ) . Message . Content [ 0 ] . Text ) ;
192+ }
193+
194+ private bool IsEquivalent < T > ( T left , T right )
195+ {
196+ try
197+ {
198+ left . Should ( ) . BeEquivalentTo ( right ) ;
199+ }
200+ catch ( Exception e )
201+ {
202+ return false ;
203+ }
204+
205+ return true ;
206+ }
75207}
0 commit comments