openai/openai-dotnet

Public

mirrored from https://github.com/openai/openai-dotnetAvailable

CodeCommitsIssuesPull requestsActionsInsightsSecurity
OpenAI_2.6.0

Branches

Tags

  • No tags available.
0Branches0Tags
Go to file
Add file
Code

Clone

HTTPS

Download ZIP

tests/Chat/ChatStoreTests.cs

1005lines · modecode

1using Microsoft.ClientModel.TestFramework;
2using NUnit.Framework;
3using OpenAI.Chat;
4using OpenAI.Tests.Utility;
5using System;
6using System.ClientModel;
7using System.Collections.Generic;
8using System.Threading;
9using System.Threading.Tasks;
10using static OpenAI.Tests.TestHelpers;
11
12namespace OpenAI.Tests.Chat;
13
14[Category("Chat")]
15[Category("ChatStore")]
16public class ChatStoreTests : OpenAIRecordedTestBase
17{
18 public ChatStoreTests(bool isAsync) : base(isAsync)
19 {
20 TestTimeoutInSeconds = 30;
21 }
22
23 [RecordedTest]
24 public async Task ChatMetadata()
25 {
26 ChatClient client = GetTestClient();
27
28 ChatCompletionOptions options = new()
29 {
30 StoredOutputEnabled = true,
31 Metadata =
32 {
33 ["my_metadata_key"] = "my_metadata_value",
34 },
35 };
36
37 ChatCompletion completion = await client.CompleteChatAsync(
38 ["Hello, world!"],
39 options);
40
41 int count = 0;
42 await foreach (var fetchedCompletion in client.GetChatCompletionsAsync())
43 {
44 count++;
45 }
46 Assert.That(count, Is.GreaterThan(0));
47 }
48
49 [RecordedTest]
50 public async Task GetChatCompletionsWithPagination()
51 {
52 ChatClient client = GetTestClient();
53
54 // Create multiple completions with stored output enabled
55 var completionIds = new List<string>();
56 for (int i = 0; i < 3; i++)
57 {
58 ChatCompletionOptions options = new()
59 {
60 StoredOutputEnabled = true,
61 Metadata = { ["test_key"] = $"test_value_{i}" }
62 };
63
64 ChatCompletion completion = await client.CompleteChatAsync(
65 [$"Test message {i}: Say 'Hello World {i}'"],
66 options);
67
68 completionIds.Add(completion.Id);
69 }
70
71 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
72
73 // Test pagination with limit
74 ChatCompletionCollectionOptions paginationOptions = new()
75 {
76 PageSizeLimit = 2
77 };
78
79 int totalCount = 0;
80 string lastId = null;
81
82 await foreach (var fetchedCompletion in client.GetChatCompletionsAsync(paginationOptions))
83 {
84 totalCount++;
85 lastId = fetchedCompletion.Id;
86 Assert.That(fetchedCompletion.Id, Is.Not.Null.And.Not.Empty);
87 Assert.That(fetchedCompletion.Content, Is.Not.Null);
88
89 if (totalCount >= 2) break; // Stop after getting 2 items
90 }
91
92 Assert.That(totalCount, Is.EqualTo(2));
93 Assert.That(lastId, Is.Not.Null);
94
95 // Clean up
96 foreach (var id in completionIds)
97 {
98 try
99 {
100 await client.DeleteChatCompletionAsync(id);
101 }
102 catch { /* Ignore cleanup errors */ }
103 }
104 }
105
106 [RecordedTest]
107 public async Task GetChatCompletionsWithAfterIdPagination()
108 {
109 ChatClient client = GetTestClient();
110
111 // Create multiple completions
112 var completionIds = new List<string>();
113 for (int i = 0; i < 3; i++)
114 {
115 ChatCompletionOptions createOptions = new()
116 {
117 StoredOutputEnabled = true
118 };
119
120 ChatCompletion completion = await client.CompleteChatAsync(
121 [$"Pagination test {i}: Say 'Test {i}'"],
122 createOptions);
123
124 completionIds.Add(completion.Id);
125 }
126
127 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
128
129 // Get first completion to use as afterId
130 string afterId = null;
131 await foreach (var firstCompletion in client.GetChatCompletionsAsync())
132 {
133 afterId = firstCompletion.Id;
134 break;
135 }
136
137 Assert.That(afterId, Is.Not.Null);
138
139 // Test pagination starting after the first ID
140 ChatCompletionCollectionOptions paginationOptions = new()
141 {
142 AfterId = afterId,
143 PageSizeLimit = 2
144 };
145
146 int count = 0;
147 await foreach (var completion in client.GetChatCompletionsAsync(paginationOptions))
148 {
149 count++;
150 // Ensure we don't get the afterId completion
151 Assert.That(completion.Id, Is.Not.EqualTo(afterId));
152 if (count >= 2) break;
153 }
154
155 // Clean up
156 foreach (var id in completionIds)
157 {
158 try
159 {
160 await client.DeleteChatCompletionAsync(id);
161 }
162 catch { /* Ignore cleanup errors */ }
163 }
164 }
165
166 [RecordedTest]
167 public async Task GetChatCompletionsWithOrderFiltering()
168 {
169 ChatClient client = GetTestClient();
170
171 // Create completions with timestamps
172 var completionIds = new List<string>();
173 for (int i = 0; i < 2; i++)
174 {
175 ChatCompletionOptions createOptions = new()
176 {
177 StoredOutputEnabled = true,
178 Metadata = { ["sequence"] = i.ToString() }
179 };
180
181 ChatCompletion completion = await client.CompleteChatAsync(
182 [$"Order test {i}: Say 'Order {i}'"],
183 createOptions);
184
185 completionIds.Add(completion.Id);
186 await Task.Delay(1000); // Ensure different timestamps
187 }
188
189 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
190
191 // Test ascending order
192 ChatCompletionCollectionOptions ascOptions = new()
193 {
194 Order = ChatCompletionCollectionOrder.Ascending,
195 PageSizeLimit = 5
196 };
197
198 var ascResults = new List<ChatCompletion>();
199 await foreach (var completion in client.GetChatCompletionsAsync(ascOptions))
200 {
201 ascResults.Add(completion);
202 if (ascResults.Count >= 2) break;
203 }
204
205 // Test descending order
206 ChatCompletionCollectionOptions descOptions = new()
207 {
208 Order = ChatCompletionCollectionOrder.Descending,
209 PageSizeLimit = 5
210 };
211
212 var descResults = new List<ChatCompletion>();
213 await foreach (var completion in client.GetChatCompletionsAsync(descOptions))
214 {
215 descResults.Add(completion);
216 if (descResults.Count >= 2) break;
217 }
218
219 // Verify we get results in both cases
220 Assert.That(ascResults, Has.Count.GreaterThan(0));
221 Assert.That(descResults, Has.Count.GreaterThan(0));
222
223 // The first result in descending order should be the most recent
224 // (though exact ordering validation is tricky due to timing)
225 Assert.That(descResults[0].Id, Is.Not.Null.And.Not.Empty);
226
227 // Clean up
228 foreach (var id in completionIds)
229 {
230 try
231 {
232 await client.DeleteChatCompletionAsync(id);
233 }
234 catch { /* Ignore cleanup errors */ }
235 }
236 }
237
238 [LiveOnly(Reason = "Temp while sorting out flakiness in playback")]
239 [RecordedTest]
240 public async Task GetChatCompletionsWithMetadataFiltering()
241 {
242 ChatClient client = GetTestClient();
243
244 // Create completions with different metadata
245 var testMetadataKey = $"test_scenario_{Guid.NewGuid():N}";
246 var completionIds = new List<string>();
247
248 // Create completion with specific metadata
249 ChatCompletionOptions options1 = new()
250 {
251 StoredOutputEnabled = true,
252 Metadata = { [testMetadataKey] = "target_value" }
253 };
254
255 ChatCompletion targetCompletion = await client.CompleteChatAsync(
256 ["Metadata test: Say 'Target completion'"],
257 options1);
258 completionIds.Add(targetCompletion.Id);
259
260 // Create completion with different metadata
261 ChatCompletionOptions options2 = new()
262 {
263 StoredOutputEnabled = true,
264 Metadata = { [testMetadataKey] = "other_value" }
265 };
266
267 ChatCompletion otherCompletion = await client.CompleteChatAsync(
268 ["Metadata test: Say 'Other completion'"],
269 options2);
270 completionIds.Add(otherCompletion.Id);
271
272 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
273
274 // Filter by specific metadata
275 ChatCompletionCollectionOptions filterOptions = new()
276 {
277 Metadata = { [testMetadataKey] = "target_value" },
278 PageSizeLimit = 10
279 };
280
281 int totalFound = 0;
282
283 await foreach (var completion in client.GetChatCompletionsAsync(filterOptions))
284 {
285 totalFound++;
286 Assert.That(completion.Id, Is.Not.Null.And.Not.Empty);
287 if (totalFound >= 20) break; // Prevent infinite loop
288 }
289
290 // Should find completions (filtering behavior may vary by implementation)
291 Assert.That(totalFound, Is.GreaterThan(0));
292
293 // Clean up
294 foreach (var id in completionIds)
295 {
296 try
297 {
298 await client.DeleteChatCompletionAsync(id);
299 }
300 catch { /* Ignore cleanup errors */ }
301 }
302 }
303
304 [RecordedTest]
305 public async Task GetChatCompletionsWithModelFiltering()
306 {
307 ChatClient client = GetTestClient();
308
309 // Create completion with default model
310 ChatCompletionOptions createOptions = new()
311 {
312 StoredOutputEnabled = true,
313 Metadata = { ["model_test"] = "true" }
314 };
315
316 ChatCompletion completion = await client.CompleteChatAsync(
317 ["Model filter test: Say 'Hello'"],
318 createOptions);
319
320 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
321
322 // Filter by the model used by the test client
323 ChatCompletionCollectionOptions filterOptions = new()
324 {
325 Model = "gpt-4o-mini-2024-07-18", // Common test model
326 PageSizeLimit = 10
327 };
328
329 int count = 0;
330 await foreach (var fetchedCompletion in client.GetChatCompletionsAsync(filterOptions))
331 {
332 count++;
333 Assert.That(fetchedCompletion.Id, Is.Not.Null.And.Not.Empty);
334 Assert.That(fetchedCompletion.Model, Is.EqualTo(filterOptions.Model));
335 if (count >= 5) break; // Limit results for test performance
336 }
337
338 Assert.That(count, Is.GreaterThan(0));
339
340 // Clean up
341 try
342 {
343 await client.DeleteChatCompletionAsync(completion.Id);
344 }
345 catch { /* Ignore cleanup errors */ }
346 }
347
348 [RecordedTest]
349 public async Task GetChatCompletionsWithEmptyOptions()
350 {
351 ChatClient client = GetTestClient();
352
353 // Create a completion to ensure we have something to fetch
354 ChatCompletionOptions createOptions = new()
355 {
356 StoredOutputEnabled = true
357 };
358
359 ChatCompletion completion = await client.CompleteChatAsync(
360 ["Empty options test: Say 'Hello'"],
361 createOptions);
362
363 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
364
365 // Test with default/empty options
366 int count = 0;
367 await foreach (var fetchedCompletion in client.GetChatCompletionsAsync())
368 {
369 count++;
370 Assert.That(fetchedCompletion.Id, Is.Not.Null.And.Not.Empty);
371 Assert.That(fetchedCompletion.Content, Is.Not.Null);
372 if (count >= 3) break; // Limit for test performance
373 }
374
375 Assert.That(count, Is.GreaterThan(0));
376
377 // Clean up
378 try
379 {
380 await client.DeleteChatCompletionAsync(completion.Id);
381 }
382 catch { /* Ignore cleanup errors */ }
383 }
384
385 [LiveOnly(Reason ="Temp while sorting out flakiness in playback")]
386 [RecordedTest]
387 public async Task GetChatCompletionsWithCombinedFilters()
388 {
389 ChatClient client = GetTestClient();
390
391 // Create completion with combined metadata for filtering
392 var testKey = $"combined_test_{Guid.NewGuid():N}";
393 ChatCompletionOptions createOptions = new()
394 {
395 StoredOutputEnabled = true,
396 Metadata =
397 {
398 [testKey] = "combined_value",
399 ["test_type"] = "integration"
400 }
401 };
402
403 ChatCompletion completion = await client.CompleteChatAsync(
404 ["Combined filters test: Say 'Combined test'"],
405 createOptions);
406
407 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
408
409 // Test with combined filters
410 ChatCompletionCollectionOptions combinedOptions = new()
411 {
412 PageSizeLimit = 5,
413 Order = ChatCompletionCollectionOrder.Descending,
414 Metadata = { [testKey] = "combined_value" }
415 };
416
417 int count = 0;
418
419 await foreach (var fetchedCompletion in client.GetChatCompletionsAsync(combinedOptions))
420 {
421 count++;
422 Assert.That(fetchedCompletion.Id, Is.Not.Null.And.Not.Empty);
423
424 if (count >= 10) break; // Prevent excessive iterations
425 }
426
427 Assert.That(count, Is.GreaterThan(0));
428
429 // Clean up
430 try
431 {
432 await client.DeleteChatCompletionAsync(completion.Id);
433 }
434 catch { /* Ignore cleanup errors */ }
435 }
436
437 [RecordedTest]
438 public async Task StoredChatCompletionsWork()
439 {
440 ChatClient client = GetTestClient();
441
442 ChatCompletionOptions options = new()
443 {
444 StoredOutputEnabled = true
445 };
446
447 ChatCompletion completion = await client.CompleteChatAsync(
448 [new UserChatMessage("Say `this is a test`.")],
449 options);
450
451 await TestHelpers.RetryWithExponentialBackoffAsync(async () =>
452 {
453
454 ChatCompletion storedCompletion = await client.GetChatCompletionAsync(completion.Id);
455
456 Assert.That(storedCompletion.Id, Is.EqualTo(completion.Id));
457 Assert.That(storedCompletion.Content[0].Text, Is.EqualTo(completion.Content[0].Text));
458
459 ChatCompletionDeletionResult deletionResult = await client.DeleteChatCompletionAsync(completion.Id);
460
461 Assert.That(deletionResult.Deleted, Is.True);
462 });
463
464 await Task.Delay(s_delayInMilliseconds);
465
466 Assert.ThrowsAsync<ClientResultException>(async () =>
467 {
468 ChatCompletion deletedCompletion = await client.GetChatCompletionAsync(completion.Id);
469 });
470 }
471
472 [LiveOnly(Reason = "Temp while sorting out flakiness in playback")]
473 [RecordedTest]
474 public async Task UpdateChatCompletionWorks()
475 {
476 ChatClient client = GetTestClient();
477
478 var testMetadataKey = $"test_key_{Guid.NewGuid():N}";
479 var initialOptions = new ChatCompletionOptions
480 {
481 StoredOutputEnabled = true,
482 Metadata = { [testMetadataKey] = "initial_value" }
483 };
484
485 ChatCompletion chatCompletion = await client.CompleteChatAsync(
486 [new UserChatMessage("Say `this is a test`.")],
487 initialOptions);
488
489 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
490
491 var newMetadata = new Dictionary<string, string>
492 {
493 [testMetadataKey] = "updated_value",
494 ["updated_by"] = "unit_test"
495 };
496
497 ChatCompletion updated = await client.UpdateChatCompletionAsync(chatCompletion.Id, newMetadata);
498
499 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be updated
500
501 Assert.That(updated, Is.Not.Null);
502 Assert.That(updated.Id, Is.EqualTo(chatCompletion.Id));
503
504 ChatCompletionDeletionResult deletionResult = await client.DeleteChatCompletionAsync(chatCompletion.Id);
505 Assert.That(deletionResult.Deleted, Is.True);
506
507 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be deleted
508
509 Assert.ThrowsAsync<ClientResultException>(async () =>
510 {
511 _ = await client.GetChatCompletionAsync(chatCompletion.Id);
512 });
513 }
514
515 [RecordedTest]
516 public async Task GetChatCompletionsValidatesCollectionEnumeration()
517 {
518 ChatClient client = GetTestClient();
519
520 // Create a completion to ensure we have data
521 ChatCompletionOptions createOptions = new()
522 {
523 StoredOutputEnabled = true,
524 Metadata = { ["enumeration_test"] = "true" }
525 };
526
527 ChatCompletion completion = await client.CompleteChatAsync(
528 ["Enumeration test: Say 'Test enumeration'"],
529 createOptions);
530
531 await Task.Delay(5000); // Wait for completion to be stored
532
533 // Test that we can enumerate multiple times
534 ChatCompletionCollectionOptions collectionOptions = new()
535 {
536 PageSizeLimit = 2
537 };
538
539 var collection = client.GetChatCompletionsAsync(collectionOptions);
540
541 // First enumeration
542 int firstCount = 0;
543 await foreach (var item in collection)
544 {
545 firstCount++;
546 Assert.That(item.Id, Is.Not.Null.And.Not.Empty);
547 if (firstCount >= 2) break;
548 }
549
550 // Second enumeration (should work independently)
551 int secondCount = 0;
552 await foreach (var item in collection)
553 {
554 secondCount++;
555 Assert.That(item.Id, Is.Not.Null.And.Not.Empty);
556 if (secondCount >= 2) break;
557 }
558
559 Assert.That(firstCount, Is.GreaterThan(0));
560 Assert.That(secondCount, Is.GreaterThan(0));
561
562 // Clean up
563 try
564 {
565 await client.DeleteChatCompletionAsync(completion.Id);
566 }
567 catch { /* Ignore cleanup errors */ }
568 }
569
570 [RecordedTest]
571 public async Task GetChatCompletionsHandlesLargeLimits()
572 {
573 ChatClient client = GetTestClient();
574
575 // Create a completion for testing
576 ChatCompletionOptions createOptions = new()
577 {
578 StoredOutputEnabled = true
579 };
580
581 ChatCompletion completion = await client.CompleteChatAsync(
582 ["Large limit test: Say 'Testing large limits'"],
583 createOptions);
584
585 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
586
587 // Test with a large page size limit
588 ChatCompletionCollectionOptions largeOptions = new()
589 {
590 PageSizeLimit = 100
591 };
592
593 int count = 0;
594 await foreach (var fetchedCompletion in client.GetChatCompletionsAsync(largeOptions))
595 {
596 count++;
597 Assert.That(fetchedCompletion.Id, Is.Not.Null.And.Not.Empty);
598 if (count >= 20) break; // Prevent excessive test time
599 }
600
601 Assert.That(count, Is.GreaterThan(0));
602
603 // Clean up
604 try
605 {
606 await client.DeleteChatCompletionAsync(completion.Id);
607 }
608 catch { /* Ignore cleanup errors */ }
609 }
610
611 [RecordedTest]
612 public async Task GetChatCompletionsWithMinimalLimits()
613 {
614 ChatClient client = GetTestClient();
615
616 // Create a completion for testing
617 ChatCompletionOptions createOptions = new()
618 {
619 StoredOutputEnabled = true
620 };
621
622 ChatCompletion completion = await client.CompleteChatAsync(
623 ["Minimal limit test: Say 'Testing minimal limits'"],
624 createOptions);
625
626 await Task.Delay(s_delayInMilliseconds); // Wait for completions to be stored
627
628 // Test with minimal page size
629 ChatCompletionCollectionOptions minimalOptions = new()
630 {
631 PageSizeLimit = 1
632 };
633
634 int count = 0;
635 await foreach (var fetchedCompletion in client.GetChatCompletionsAsync(minimalOptions))
636 {
637 count++;
638 Assert.That(fetchedCompletion.Id, Is.Not.Null.And.Not.Empty);
639 if (count >= 3) break; // Get a few items to verify pagination works
640 }
641
642 Assert.That(count, Is.GreaterThan(0));
643
644 // Clean up
645 try
646 {
647 await client.DeleteChatCompletionAsync(completion.Id);
648 }
649 catch { /* Ignore cleanup errors */ }
650 }
651
652 [RecordedTest]
653 public async Task GetChatCompletionMessagesWithBasicUsage()
654 {
655 ChatClient client = GetTestClient();
656
657 // Create a completion with stored output enabled to have messages
658 ChatCompletionOptions createOptions = new()
659 {
660 StoredOutputEnabled = true,
661 Metadata = { ["test_scenario"] = "basic_messages" }
662 };
663
664 ChatCompletion completion = await client.CompleteChatAsync(
665 ["Basic messages test: Say 'Hello, this is a test message.'"],
666 createOptions);
667
668 await RetryWithExponentialBackoffAsync(async () =>
669 {
670 // Test basic enumeration of messages
671 int messageCount = 0;
672 await foreach (var message in client.GetChatCompletionMessagesAsync(completion.Id))
673 {
674 messageCount++;
675 Assert.That(message.Id, Is.Not.Null.And.Not.Empty);
676 Assert.That(message.Content, Is.EqualTo("Basic messages test: Say 'Hello, this is a test message.'"));
677
678 if (messageCount >= 5) break; // Prevent infinite loop
679 }
680
681 Assert.That(messageCount, Is.GreaterThan(0));
682 });
683
684 // Clean up
685 try
686 {
687 await client.DeleteChatCompletionAsync(completion.Id);
688 }
689 catch { /* Ignore cleanup errors */ }
690 }
691
692 [RecordedTest]
693 public async Task GetChatCompletionMessagesWithPagination()
694 {
695 ChatClient client = GetTestClient();
696
697 // Create completion with multiple messages (conversation with tool calls)
698 // and one with multiple content parts
699 List<ChatMessage> conversationMessages = new()
700 {
701 new UserChatMessage("What's the weather like today? Use the weather tool."),
702 new UserChatMessage("Name something I could do outside in this weather."),
703 new UserChatMessage("Name something else I could do outside in this weather."),
704 new UserChatMessage([
705 ChatMessageContentPart.CreateTextPart("Whose logo is this?: "),
706 ChatMessageContentPart.CreateImagePart(new Uri("https://upload.wikimedia.org/wikipedia/commons/c/c3/Openai.png"))]),
707 };
708
709 // Add function definition to trigger more back-and-forth
710 ChatTool weatherTool = ChatTool.CreateFunctionTool(
711 "get_weather",
712 "Get current weather information",
713 BinaryData.FromString("""
714 {
715 "type": "object",
716 "properties": {
717 "location": {
718 "type": "string",
719 "description": "The city and state, e.g. San Francisco, CA"
720 }
721 },
722 "required": ["location"]
723 }
724 """));
725
726 ChatCompletionOptions createOptions = new()
727 {
728 StoredOutputEnabled = true,
729 Tools = { weatherTool },
730 Metadata = { ["test_scenario"] = "pagination_messages" }
731 };
732
733 ChatCompletion completion = await client.CompleteChatAsync(
734 conversationMessages,
735 createOptions);
736
737 await RetryWithExponentialBackoffAsync(async () =>
738 {
739 // Test pagination with limit
740 int totalMessages = 0;
741 string lastMessageId = null;
742
743 var options = new ChatCompletionMessageCollectionOptions()
744 {
745 PageSizeLimit = 2
746 };
747
748 bool foundContentParts = false;
749
750 await foreach (var message in client.GetChatCompletionMessagesAsync(completion.Id, options))
751 {
752 totalMessages++;
753 lastMessageId = message.Id;
754
755 // Check if the message contains any content parts
756 if (message.ContentParts.Count > 0)
757 {
758 foundContentParts = true;
759 Assert.That(message.ContentParts[0].Text, Is.EqualTo("Whose logo is this?: "));
760 Assert.That(message.ContentParts[1].ImageBytes, Is.Not.Null);
761 }
762 Assert.That(message.Id, Is.Not.Null.And.Not.Empty);
763
764 if (totalMessages >= 4) break; // Get a few pages worth
765 }
766
767 Assert.That(foundContentParts, Is.True);
768 Assert.That(totalMessages, Is.GreaterThan(3));
769 Assert.That(lastMessageId, Is.Not.Null);
770 });
771
772 // Clean up
773 try
774 {
775 await client.DeleteChatCompletionAsync(completion.Id);
776 }
777 catch { /* Ignore cleanup errors */ }
778 }
779
780 [RecordedTest]
781 public async Task GetChatCompletionMessagesWithAfterIdPagination()
782 {
783 ChatClient client = GetTestClient();
784
785 // Create completion
786 ChatCompletionOptions createOptions = new()
787 {
788 StoredOutputEnabled = true,
789 Metadata = { ["test_scenario"] = "after_id_pagination" }
790 };
791
792 ChatCompletion completion = await client.CompleteChatAsync(
793 ["After ID pagination test: Please provide a detailed response with multiple sentences."],
794 createOptions);
795
796 await RetryWithExponentialBackoffAsync(async () =>
797 {
798 // Get first message to use as afterId
799 string afterId = null;
800 await foreach (var firstMessage in client.GetChatCompletionMessagesAsync(completion.Id))
801 {
802 afterId = firstMessage.Id;
803 break;
804 }
805
806 if (afterId != null)
807 {
808 // Test pagination starting after the first message
809 int count = 0;
810 var options = new ChatCompletionMessageCollectionOptions()
811 {
812 AfterId = afterId,
813 PageSizeLimit = 3
814 };
815
816 await foreach (var message in client.GetChatCompletionMessagesAsync(completion.Id, options))
817 {
818 count++;
819 // Ensure we don't get the afterId message
820 Assert.That(message.Id, Is.Not.EqualTo(afterId));
821
822 if (count >= 3) break;
823 }
824
825 // We might not have messages after the first one, so just verify the method works
826 Assert.That(count, Is.GreaterThanOrEqualTo(0));
827 }
828 });
829 // Clean up
830 try
831 {
832 await client.DeleteChatCompletionAsync(completion.Id);
833 }
834 catch { /* Ignore cleanup errors */ }
835 }
836
837 [RecordedTest]
838 public async Task GetChatCompletionMessagesWithOrderFiltering()
839 {
840 ChatClient client = GetTestClient();
841
842 // Create completion with detailed conversation
843 ChatCompletionOptions createOptions = new()
844 {
845 StoredOutputEnabled = true,
846 Metadata = { ["test_scenario"] = "order_filtering" }
847 };
848
849 ChatCompletion completion = await client.CompleteChatAsync(
850 ["Order filtering test: Please provide a comprehensive response about machine learning."],
851 createOptions);
852
853 await RetryWithExponentialBackoffAsync(async () =>
854 {
855 // Test ascending order
856 List<ChatCompletionMessageListDatum> ascMessages = new();
857 var ascOptions = new ChatCompletionMessageCollectionOptions()
858 {
859 Order = ChatCompletionMessageCollectionOrder.Ascending,
860 PageSizeLimit = 5
861 };
862
863 await foreach (var message in client.GetChatCompletionMessagesAsync(completion.Id, ascOptions))
864 {
865 ascMessages.Add(message);
866 if (ascMessages.Count >= 3) break;
867 }
868
869 // Test descending order
870 List<ChatCompletionMessageListDatum> descMessages = new();
871 var descOptions = new ChatCompletionMessageCollectionOptions()
872 {
873 Order = ChatCompletionMessageCollectionOrder.Descending,
874 PageSizeLimit = 5
875 };
876
877 await foreach (var message in client.GetChatCompletionMessagesAsync(completion.Id, descOptions))
878 {
879 descMessages.Add(message);
880 if (descMessages.Count >= 3) break;
881 }
882
883 // Verify we get results in both cases
884 Assert.That(ascMessages, Has.Count.GreaterThan(0));
885 Assert.That(descMessages, Has.Count.GreaterThan(0));
886 });
887
888 // Clean up
889 try
890 {
891 await client.DeleteChatCompletionAsync(completion.Id);
892 }
893 catch { /* Ignore cleanup errors */ }
894 }
895
896 [RecordedTest]
897 public async Task GetChatCompletionMessagesWithCancellationToken()
898 {
899 ChatClient client = GetTestClient();
900
901 // Create completion
902 ChatCompletionOptions createOptions = new()
903 {
904 StoredOutputEnabled = true,
905 Metadata = { ["test_scenario"] = "cancellation_token" }
906 };
907
908 ChatCompletion completion = await client.CompleteChatAsync(
909 ["Cancellation test: Say 'Hello World'"],
910 createOptions);
911
912 // Test with cancellation token
913 using var cts = new CancellationTokenSource();
914
915 await RetryWithExponentialBackoffAsync(async () =>
916 {
917 try
918 {
919 int count = 0;
920 await foreach (var message in client.GetChatCompletionMessagesAsync(completion.Id, cancellationToken: cts.Token))
921 {
922 count++;
923 Assert.That(message.Id, Is.Not.Null.And.Not.Empty);
924
925 if (count >= 2)
926 {
927 cts.Cancel(); // Cancel after getting some messages
928 break;
929 }
930 }
931
932 Assert.That(count, Is.GreaterThanOrEqualTo(1));
933 }
934 catch (OperationCanceledException)
935 {
936 // This is expected if cancellation happens during enumeration
937 }
938 });
939
940 // Clean up
941 try
942 {
943 await client.DeleteChatCompletionAsync(completion.Id);
944 }
945 catch { /* Ignore cleanup errors */ }
946 }
947
948 [RecordedTest]
949 public async Task GetChatCompletionMessagesWithCombinedOptions()
950 {
951 ChatClient client = GetTestClient();
952
953 // Create completion with comprehensive options
954 ChatCompletionOptions createOptions = new()
955 {
956 StoredOutputEnabled = true,
957 Metadata = { ["test_scenario"] = "combined_options" }
958 };
959
960 ChatCompletion completion = await client.CompleteChatAsync(
961 ["Combined options test: Provide a detailed explanation of artificial intelligence."],
962 createOptions);
963
964 await RetryWithExponentialBackoffAsync(async () =>
965 {
966 // Test combined options: limit + order + cancellation token
967 using var cts = new CancellationTokenSource(TimeSpan.FromSeconds(30));
968
969 List<ChatCompletionMessageListDatum> messages = new();
970
971 await foreach (var message in client.GetChatCompletionMessagesAsync(
972 completion.Id,
973 new ChatCompletionMessageCollectionOptions()
974 {
975 PageSizeLimit = 3,
976 Order = ChatCompletionMessageCollectionOrder.Descending
977 },
978 cancellationToken: cts.Token))
979 {
980 messages.Add(message);
981
982 // Validate message structure
983 Assert.That(message.Id, Is.Not.Null.And.Not.Empty);
984
985 if (messages.Count >= 3) break;
986 }
987
988 Assert.That(messages, Has.Count.GreaterThan(0));
989 });
990
991 // Clean up
992 try
993 {
994 await client.DeleteChatCompletionAsync(completion.Id);
995 }
996 catch { /* Ignore cleanup errors */ }
997 }
998
999 private const int s_delayInMilliseconds = 5000;
1000
1001 private ChatClient GetTestClient(string overrideModel = null)
1002 => GetProxiedOpenAIClient<ChatClient>(
1003 scenario: TestScenario.Chat,
1004 overrideModel: overrideModel);
1005}
1006