@@ -357,6 +357,187 @@ def hook(_):
357357 assert captured ["kwargs" ]["frames_per_batch" ] == 4
358358
359359
360+ def test_make_collector_async_env_uses_async_batched_collector (monkeypatch ):
361+ captured = {}
362+
363+ class _FakeAsyncCollector :
364+ def __init__ (self , * args , ** kwargs ):
365+ captured ["args" ] = args
366+ captured ["kwargs" ] = kwargs
367+
368+ def __iter__ (self ):
369+ return self
370+
371+ def __next__ (self ):
372+ raise StopIteration
373+
374+ def server_stats (self , * , reset = False ):
375+ return {"requests" : 0 }
376+
377+ def shutdown (self ):
378+ captured ["shutdown" ] = True
379+
380+ class _FakeEnv :
381+ batch_size = torch .Size ([1 ])
382+ device = torch .device ("cpu" )
383+
384+ cfg = SimpleNamespace (
385+ collector = SimpleNamespace (
386+ groups_per_iter = 4 ,
387+ group_size = 2 ,
388+ async_env = True ,
389+ async_policy = True ,
390+ server_min_batch_size = 2 ,
391+ ),
392+ env = SimpleNamespace (
393+ backend = "toy" ,
394+ action_dim = 2 ,
395+ state_dim = 4 ,
396+ image_shape = (3 , 8 , 8 ),
397+ render_size = 16 ,
398+ success_steps = 2 ,
399+ success_tol = 0.25 ,
400+ max_outer_steps = 3 ,
401+ num_envs = 4 ,
402+ seed = 0 ,
403+ ),
404+ )
405+ monkeypatch .setattr (utils , "AsyncBatchedCollector" , _FakeAsyncCollector )
406+
407+ collector = utils .make_collector (
408+ cfg ,
409+ _FakeEnv (),
410+ object (),
411+ torch .device ("cpu" ),
412+ tokenizer = object (),
413+ replay_buffer = object (),
414+ )
415+ collector ._ensure_collector ()
416+
417+ assert len (captured ["kwargs" ]["create_env_fn" ]) == 4
418+ assert captured ["kwargs" ]["yield_completed_trajectories" ]
419+ server_config = captured ["kwargs" ]["server_config" ]
420+ assert server_config .max_batch_size == 4
421+ assert server_config .min_batch_size == 2
422+
423+
424+ def test_make_collector_async_env_without_policy_batching (monkeypatch ):
425+ captured = {}
426+
427+ class _FakeAsyncCollector :
428+ def __init__ (self , * args , ** kwargs ):
429+ captured ["kwargs" ] = kwargs
430+
431+ def __iter__ (self ):
432+ return self
433+
434+ def __next__ (self ):
435+ raise StopIteration
436+
437+ def server_stats (self , * , reset = False ):
438+ return {}
439+
440+ def shutdown (self ):
441+ pass
442+
443+ class _FakeEnv :
444+ batch_size = torch .Size ([1 ])
445+ device = torch .device ("cpu" )
446+
447+ cfg = SimpleNamespace (
448+ collector = SimpleNamespace (
449+ groups_per_iter = 2 ,
450+ group_size = 2 ,
451+ async_env = True ,
452+ async_policy = False ,
453+ ),
454+ env = SimpleNamespace (
455+ backend = "toy" ,
456+ action_dim = 2 ,
457+ state_dim = 4 ,
458+ image_shape = (3 , 8 , 8 ),
459+ render_size = 16 ,
460+ success_steps = 2 ,
461+ success_tol = 0.25 ,
462+ max_outer_steps = 3 ,
463+ num_envs = 2 ,
464+ seed = 0 ,
465+ ),
466+ )
467+ monkeypatch .setattr (utils , "AsyncBatchedCollector" , _FakeAsyncCollector )
468+
469+ collector = utils .make_collector (
470+ cfg ,
471+ _FakeEnv (),
472+ object (),
473+ torch .device ("cpu" ),
474+ tokenizer = object (),
475+ )
476+ collector ._ensure_collector ()
477+
478+ server_config = captured ["kwargs" ]["server_config" ]
479+ assert server_config .max_batch_size == 1
480+ assert server_config .timeout == 0.0
481+
482+
483+ def test_make_collector_sync_env_can_use_policy_server (monkeypatch ):
484+ captured = {}
485+
486+ class _FakeCollector :
487+ def __init__ (self , * args , ** kwargs ):
488+ captured ["collector_args" ] = args
489+ captured ["collector_kwargs" ] = kwargs
490+ self .requested_frames_per_batch = kwargs ["frames_per_batch" ]
491+
492+ def shutdown (self , * args , ** kwargs ):
493+ captured ["collector_shutdown" ] = True
494+
495+ def reset (self , * args , ** kwargs ):
496+ captured ["collector_reset" ] = True
497+
498+ class _FakeServer :
499+ def __init__ (self , * args , ** kwargs ):
500+ captured ["server_args" ] = args
501+ captured ["server_kwargs" ] = kwargs
502+
503+ def start (self ):
504+ return self
505+
506+ def shutdown (self ):
507+ captured ["server_shutdown" ] = True
508+
509+ def stats (self , * , reset = False ):
510+ return {"requests" : 0 }
511+
512+ class _FakeEnv :
513+ batch_size = torch .Size ([2 ])
514+ device = None
515+
516+ policy = SimpleNamespace (
517+ in_keys = ["observation" ], out_keys = [("vla_action" , "tokens" )]
518+ )
519+ cfg = SimpleNamespace (
520+ collector = SimpleNamespace (
521+ groups_per_iter = 2 ,
522+ group_size = 1 ,
523+ async_policy = True ,
524+ ),
525+ env = SimpleNamespace (max_outer_steps = 3 ),
526+ )
527+ monkeypatch .setattr (utils , "Collector" , _FakeCollector )
528+ monkeypatch .setattr (utils , "InferenceServer" , _FakeServer )
529+
530+ collector = utils .make_collector (cfg , _FakeEnv (), policy , torch .device ("cpu" ))
531+
532+ assert isinstance (collector , utils ._ServerBackedCollector )
533+ assert isinstance (captured ["collector_args" ][1 ], utils .PolicyClientModule )
534+ assert captured ["server_kwargs" ]["server_config" ].max_batch_size == 2
535+ assert captured ["collector_kwargs" ]["policy_device" ] == torch .device ("cpu" )
536+ assert captured ["collector_kwargs" ]["trust_policy" ] is True
537+ collector .shutdown ()
538+ assert captured ["server_shutdown" ]
539+
540+
360541def test_make_replay_buffer_scales_capacity_with_overcollection ():
361542 cfg = SimpleNamespace (
362543 collector = SimpleNamespace (
0 commit comments