@@ -422,6 +422,249 @@ public void Pagination_CanRehydrateFileAssociationCollection()
422422 Assert . That ( pageCount , Is . GreaterThanOrEqualTo ( 1 ) ) ;
423423 }
424424
425+ [ Test ]
426+ public async Task CanPaginateGetFileAssociationsInBatchAsync ( )
427+ {
428+ AssertAsyncOnly ( ) ;
429+
430+ VectorStoreClient client = GetTestClient ( ) ;
431+ CreateVectorStoreOperation createOperation = await client . CreateVectorStoreAsync ( waitUntilCompleted : true ) ;
432+ VectorStore vectorStore = createOperation . Value ;
433+ Validate ( vectorStore ) ;
434+
435+ // Create enough files to ensure we get multiple pages
436+ IReadOnlyList < OpenAIFile > testFiles = GetNewTestFiles ( 10 ) ;
437+
438+ CreateBatchFileJobOperation batchFileJobOperation = client . CreateBatchFileJob ( vectorStore . Id , testFiles ? . Select ( file => file . Id ) , waitUntilCompleted : false ) ;
439+
440+ Assert . Multiple ( ( ) =>
441+ {
442+ Assert . That ( batchFileJobOperation . BatchId , Is . Not . Null ) ;
443+ Assert . That ( batchFileJobOperation . VectorStoreId , Is . EqualTo ( vectorStore . Id ) ) ;
444+ } ) ;
445+
446+ // Test basic pagination with PageSizeLimit
447+ var options = new VectorStoreFileAssociationCollectionOptions { PageSizeLimit = 3 } ;
448+ AsyncCollectionResult < VectorStoreFileAssociation > associations = client . GetFileAssociationsInBatchAsync (
449+ vectorStore . Id , batchFileJobOperation . BatchId , options ) ;
450+
451+ int totalItemsCount = 0 ;
452+ int pageCount = 0 ;
453+ List < string > seenFileIds = new List < string > ( ) ;
454+
455+ await foreach ( VectorStoreFileAssociation association in associations )
456+ {
457+ totalItemsCount ++ ;
458+ seenFileIds . Add ( association . FileId ) ;
459+
460+ Assert . Multiple ( ( ) =>
461+ {
462+ Assert . That ( association . FileId , Is . Not . Null ) ;
463+ Assert . That ( association . VectorStoreId , Is . EqualTo ( vectorStore . Id ) ) ;
464+ } ) ;
465+ }
466+
467+ // Verify we got all the files
468+ Assert . That ( totalItemsCount , Is . EqualTo ( 10 ) ) ;
469+ Assert . That ( seenFileIds . Distinct ( ) . Count ( ) , Is . EqualTo ( 10 ) ) ;
470+
471+ // Now test pagination by examining raw pages
472+ AsyncCollectionResult < VectorStoreFileAssociation > pagedAssociations = client . GetFileAssociationsInBatchAsync (
473+ vectorStore . Id , batchFileJobOperation . BatchId , new VectorStoreFileAssociationCollectionOptions { PageSizeLimit = 3 } ) ;
474+
475+ IAsyncEnumerable < ClientResult > pages = pagedAssociations . GetRawPagesAsync ( ) ;
476+ IAsyncEnumerator < ClientResult > pageEnumerator = pages . GetAsyncEnumerator ( ) ;
477+
478+ pageCount = 0 ;
479+ int itemsInPages = 0 ;
480+
481+ while ( await pageEnumerator . MoveNextAsync ( ) )
482+ {
483+ ClientResult page = pageEnumerator . Current ;
484+ pageCount ++ ;
485+
486+ IEnumerable < VectorStoreFileAssociation > itemsInPage = GetFileAssociationsFromPage ( page ) ;
487+ int pageItemCount = itemsInPage . Count ( ) ;
488+ itemsInPages += pageItemCount ;
489+
490+ // Each page should have at most 3 items (except possibly the last page)
491+ Assert . That ( pageItemCount , Is . LessThanOrEqualTo ( 3 ) ) ;
492+ Assert . That ( pageItemCount , Is . GreaterThan ( 0 ) ) ;
493+ }
494+
495+ // We should have at least 4 pages (10 items with page size 3)
496+ Assert . That ( pageCount , Is . GreaterThanOrEqualTo ( 4 ) ) ;
497+ Assert . That ( itemsInPages , Is . EqualTo ( 10 ) ) ;
498+ }
499+
500+ [ Test ]
501+ public async Task CanTestGetFileAssociationsInBatchAsyncCollectionOptions ( )
502+ {
503+ AssertAsyncOnly ( ) ;
504+
505+ VectorStoreClient client = GetTestClient ( ) ;
506+ CreateVectorStoreOperation createOperation = await client . CreateVectorStoreAsync ( waitUntilCompleted : true ) ;
507+ VectorStore vectorStore = createOperation . Value ;
508+ Validate ( vectorStore ) ;
509+
510+ // Create files for testing
511+ IReadOnlyList < OpenAIFile > testFiles = GetNewTestFiles ( 8 ) ;
512+
513+ CreateBatchFileJobOperation batchFileJobOperation = client . CreateBatchFileJob ( vectorStore . Id , testFiles ? . Select ( file => file . Id ) , waitUntilCompleted : false ) ;
514+ Validate ( batchFileJobOperation ) ;
515+
516+ // Test Order property - Ascending vs Descending
517+ var ascendingOptions = new VectorStoreFileAssociationCollectionOptions
518+ {
519+ Order = VectorStoreCollectionOrder . Ascending ,
520+ PageSizeLimit = 5
521+ } ;
522+ var descendingOptions = new VectorStoreFileAssociationCollectionOptions
523+ {
524+ Order = VectorStoreCollectionOrder . Descending ,
525+ PageSizeLimit = 5
526+ } ;
527+
528+ List < string > ascendingIds = new List < string > ( ) ;
529+ List < string > descendingIds = new List < string > ( ) ;
530+
531+ await foreach ( VectorStoreFileAssociation association in client . GetFileAssociationsInBatchAsync ( vectorStore . Id , batchFileJobOperation . BatchId , ascendingOptions ) )
532+ {
533+ ascendingIds . Add ( association . FileId ) ;
534+ }
535+
536+ await foreach ( VectorStoreFileAssociation association in client . GetFileAssociationsInBatchAsync ( vectorStore . Id , batchFileJobOperation . BatchId , descendingOptions ) )
537+ {
538+ descendingIds . Add ( association . FileId ) ;
539+ }
540+
541+ // The lists should be reverse of each other
542+ Assert . That ( ascendingIds . Count , Is . EqualTo ( descendingIds . Count ) ) ;
543+ Assert . That ( ascendingIds . SequenceEqual ( descendingIds . AsEnumerable ( ) . Reverse ( ) ) , Is . True ) ;
544+
545+ // Test Filter property - only get completed files (which should be all of them after batch completion)
546+ var filterOptions = new VectorStoreFileAssociationCollectionOptions
547+ {
548+ Filter = VectorStoreFileStatusFilter . Completed
549+ } ;
550+
551+ int completedCount = 0 ;
552+ await foreach ( VectorStoreFileAssociation association in client . GetFileAssociationsInBatchAsync ( vectorStore . Id , batchFileJobOperation . BatchId , filterOptions ) )
553+ {
554+ completedCount ++ ;
555+ Assert . That ( association . Status , Is . EqualTo ( VectorStoreFileAssociationStatus . Completed ) ) ;
556+ }
557+
558+ Assert . That ( completedCount , Is . EqualTo ( 8 ) ) ; // Should match the number of files we uploaded
559+
560+ // Test AfterId property - get associations after a specific ID
561+ var firstAssociation = ascendingIds . FirstOrDefault ( ) ;
562+ if ( ! string . IsNullOrEmpty ( firstAssociation ) )
563+ {
564+ var afterOptions = new VectorStoreFileAssociationCollectionOptions
565+ {
566+ AfterId = firstAssociation ,
567+ Order = VectorStoreCollectionOrder . Ascending
568+ } ;
569+
570+ List < string > afterIds = new List < string > ( ) ;
571+ await foreach ( VectorStoreFileAssociation association in client . GetFileAssociationsInBatchAsync ( vectorStore . Id , batchFileJobOperation . BatchId , afterOptions ) )
572+ {
573+ afterIds . Add ( association . FileId ) ;
574+ }
575+
576+ // Should have one less item (excluding the first one)
577+ Assert . That ( afterIds . Count , Is . EqualTo ( ascendingIds . Count - 1 ) ) ;
578+ // Should not contain the first ID
579+ Assert . That ( afterIds . Contains ( firstAssociation ) , Is . False ) ;
580+ }
581+
582+ // Test BeforeId property - get associations before a specific ID
583+ var lastAssociation = ascendingIds . LastOrDefault ( ) ;
584+ if ( ! string . IsNullOrEmpty ( lastAssociation ) )
585+ {
586+ var beforeOptions = new VectorStoreFileAssociationCollectionOptions
587+ {
588+ BeforeId = lastAssociation ,
589+ Order = VectorStoreCollectionOrder . Ascending
590+ } ;
591+
592+ List < string > beforeIds = new List < string > ( ) ;
593+ await foreach ( VectorStoreFileAssociation association in client . GetFileAssociationsInBatchAsync ( vectorStore . Id , batchFileJobOperation . BatchId , beforeOptions ) )
594+ {
595+ beforeIds . Add ( association . FileId ) ;
596+ }
597+
598+ // Should have one less item (excluding the last one)
599+ Assert . That ( beforeIds . Count , Is . EqualTo ( ascendingIds . Count - 1 ) ) ;
600+ // Should not contain the last ID
601+ Assert . That ( beforeIds . Contains ( lastAssociation ) , Is . False ) ;
602+ }
603+ }
604+
605+ [ Test ]
606+ public async Task CanRehydrateGetFileAssociationsInBatchAsyncPagination ( )
607+ {
608+ AssertAsyncOnly ( ) ;
609+
610+ VectorStoreClient client = GetTestClient ( ) ;
611+ CreateVectorStoreOperation createOperation = await client . CreateVectorStoreAsync ( waitUntilCompleted : true ) ;
612+ VectorStore vectorStore = createOperation . Value ;
613+ Validate ( vectorStore ) ;
614+
615+ IReadOnlyList < OpenAIFile > testFiles = GetNewTestFiles ( 6 ) ;
616+
617+ CreateBatchFileJobOperation batchFileJobOperation = client . CreateBatchFileJob ( vectorStore . Id , testFiles ? . Select ( file => file . Id ) , waitUntilCompleted : false ) ;
618+ Validate ( batchFileJobOperation ) ;
619+
620+ // We added 6 files and will get pages with 2 items, so expect three pages in the collection.
621+ AsyncCollectionResult < VectorStoreFileAssociation > fileAssociations = client . GetFileAssociationsInBatchAsync (
622+ vectorStore . Id , batchFileJobOperation . BatchId , new VectorStoreFileAssociationCollectionOptions { PageSizeLimit = 2 } ) ;
623+
624+ IAsyncEnumerable < ClientResult > pages = fileAssociations . GetRawPagesAsync ( ) ;
625+ IAsyncEnumerator < ClientResult > pageEnumerator = pages . GetAsyncEnumerator ( ) ;
626+ await pageEnumerator . MoveNextAsync ( ) ;
627+ ClientResult firstPage = pageEnumerator . Current ;
628+ ContinuationToken nextPageToken = fileAssociations . GetContinuationToken ( firstPage ) ;
629+
630+ // Simulate rehydration of the collection
631+ BinaryData rehydrationBytes = nextPageToken . ToBytes ( ) ;
632+ ContinuationToken rehydrationToken = ContinuationToken . FromBytes ( rehydrationBytes ) ;
633+
634+ AsyncCollectionResult < VectorStoreFileAssociation > rehydratedFileAssociations = client . GetFileAssociationsInBatchAsync (
635+ vectorStore . Id , batchFileJobOperation . BatchId , new VectorStoreFileAssociationCollectionOptions { AfterId = rehydrationToken . ToBytes ( ) . ToString ( ) , PageSizeLimit = 2 } ) ;
636+
637+ IAsyncEnumerable < ClientResult > rehydratedPages = rehydratedFileAssociations . GetRawPagesAsync ( ) ;
638+ IAsyncEnumerator < ClientResult > rehydratedPageEnumerator = rehydratedPages . GetAsyncEnumerator ( ) ;
639+
640+ int pageCount = 0 ;
641+
642+ while ( await pageEnumerator . MoveNextAsync ( ) && await rehydratedPageEnumerator . MoveNextAsync ( ) )
643+ {
644+ ClientResult page = pageEnumerator . Current ;
645+ ClientResult rehydratedPage = rehydratedPageEnumerator . Current ;
646+
647+ List < VectorStoreFileAssociation > itemsInPage = GetFileAssociationsFromPage ( page ) . ToList ( ) ;
648+ List < VectorStoreFileAssociation > itemsInRehydratedPage = GetFileAssociationsFromPage ( rehydratedPage ) . ToList ( ) ;
649+
650+ Assert . AreEqual ( itemsInPage . Count , itemsInRehydratedPage . Count ) ;
651+
652+ for ( int i = 0 ; i < itemsInPage . Count ; i ++ )
653+ {
654+ Assert . AreEqual ( itemsInPage [ i ] . FileId , itemsInRehydratedPage [ i ] . FileId ) ;
655+ Assert . AreEqual ( itemsInPage [ i ] . VectorStoreId , itemsInRehydratedPage [ i ] . VectorStoreId ) ;
656+ Assert . AreEqual ( itemsInPage [ i ] . CreatedAt , itemsInRehydratedPage [ i ] . CreatedAt ) ;
657+ Assert . AreEqual ( itemsInPage [ i ] . Status , itemsInRehydratedPage [ i ] . Status ) ;
658+ }
659+
660+ pageCount ++ ;
661+ }
662+
663+ // Since we rehydrated the collection at the second page, we expect to
664+ // see two of the remaining three pages in the collection.
665+ Assert . That ( pageCount , Is . EqualTo ( 2 ) ) ;
666+ }
667+
425668 private static IEnumerable < VectorStoreFileAssociation > GetFileAssociationsFromPage ( ClientResult page )
426669 {
427670 PipelineResponse response = page . GetRawResponse ( ) ;
0 commit comments