@@ -1734,8 +1734,10 @@ pub async fn sandbox_create(
17341734 }
17351735 None => None ,
17361736 } ;
1737+ let gpu_device_ids = gpu_device_ids_from_cli ( gpu_device) ;
1738+ let effective_gpu_count = gpu_count_from_cli ( gpu_count, & gpu_device_ids) ;
17371739 let requested_gpu =
1738- gpu || gpu_count . is_some ( ) || image. as_deref ( ) . is_some_and ( image_requests_gpu) ;
1740+ gpu || effective_gpu_count . is_some ( ) || image. as_deref ( ) . is_some_and ( image_requests_gpu) ;
17391741
17401742 let providers_v2_enabled = gateway_providers_v2_enabled ( & mut client) . await ?;
17411743 let inferred_types: Vec < String > = if providers_v2_enabled {
@@ -1753,11 +1755,13 @@ pub async fn sandbox_create(
17531755
17541756 let policy = load_sandbox_policy ( policy) ?;
17551757 let resource_limits = build_sandbox_resource_limits ( cpu, memory) ?;
1758+ let driver_config = gpu_driver_config_from_cli ( & gpu_device_ids) ;
17561759
1757- let template = if image. is_some ( ) || resource_limits. is_some ( ) {
1760+ let template = if image. is_some ( ) || resource_limits. is_some ( ) || driver_config . is_some ( ) {
17581761 Some ( SandboxTemplate {
17591762 image : image. unwrap_or_default ( ) ,
17601763 resources : resource_limits,
1764+ driver_config,
17611765 ..SandboxTemplate :: default ( )
17621766 } )
17631767 } else {
@@ -1768,8 +1772,7 @@ pub async fn sandbox_create(
17681772 spec : Some ( SandboxSpec {
17691773 resource_requirements : resource_requirements_from_cli (
17701774 requested_gpu,
1771- gpu_device,
1772- gpu_count,
1775+ effective_gpu_count,
17731776 ) ,
17741777 policy,
17751778 providers : configured_providers,
@@ -2197,17 +2200,69 @@ pub async fn sandbox_create(
21972200
21982201fn resource_requirements_from_cli (
21992202 requested_gpu : bool ,
2200- gpu_device : Option < & str > ,
22012203 gpu_count : Option < u32 > ,
22022204) -> Option < SandboxResourceRequirements > {
2203- requested_gpu. then ( || SandboxResourceRequirements {
2204- gpu : Some ( GpuResourceRequirement {
2205- device_ids : gpu_device
2206- . filter ( |device_id| !device_id. is_empty ( ) )
2207- . map ( |device_id| vec ! [ device_id. to_string( ) ] )
2208- . unwrap_or_default ( ) ,
2209- count : gpu_count,
2210- } ) ,
2205+ requested_gpu. then_some ( SandboxResourceRequirements {
2206+ gpu : Some ( GpuResourceRequirement { count : gpu_count } ) ,
2207+ } )
2208+ }
2209+
2210+ fn gpu_device_ids_from_cli ( gpu_device : Option < & str > ) -> Vec < String > {
2211+ gpu_device
2212+ . map ( str:: trim)
2213+ . filter ( |device_id| !device_id. is_empty ( ) )
2214+ . map ( |device_id| vec ! [ device_id. to_string( ) ] )
2215+ . unwrap_or_default ( )
2216+ }
2217+
2218+ fn gpu_count_from_cli ( gpu_count : Option < u32 > , gpu_device_ids : & [ String ] ) -> Option < u32 > {
2219+ if gpu_device_ids. is_empty ( ) {
2220+ gpu_count
2221+ } else {
2222+ u32:: try_from ( gpu_device_ids. len ( ) ) . ok ( )
2223+ }
2224+ }
2225+
2226+ fn gpu_driver_config_from_cli ( gpu_device_ids : & [ String ] ) -> Option < prost_types:: Struct > {
2227+ use prost_types:: { ListValue , Struct , Value , value:: Kind } ;
2228+
2229+ fn string_value ( value : & str ) -> Value {
2230+ Value {
2231+ kind : Some ( Kind :: StringValue ( value. to_string ( ) ) ) ,
2232+ }
2233+ }
2234+
2235+ fn driver_block ( gpu_device_ids : & [ String ] ) -> Value {
2236+ Value {
2237+ kind : Some ( Kind :: StructValue ( Struct {
2238+ fields : std:: iter:: once ( (
2239+ "gpu_device_ids" . to_string ( ) ,
2240+ Value {
2241+ kind : Some ( Kind :: ListValue ( ListValue {
2242+ values : gpu_device_ids
2243+ . iter ( )
2244+ . map ( |device_id| string_value ( device_id) )
2245+ . collect ( ) ,
2246+ } ) ) ,
2247+ } ,
2248+ ) )
2249+ . collect ( ) ,
2250+ } ) ) ,
2251+ }
2252+ }
2253+
2254+ if gpu_device_ids. is_empty ( ) {
2255+ return None ;
2256+ }
2257+
2258+ Some ( Struct {
2259+ fields : [
2260+ ( "docker" . to_string ( ) , driver_block ( gpu_device_ids) ) ,
2261+ ( "podman" . to_string ( ) , driver_block ( gpu_device_ids) ) ,
2262+ ( "vm" . to_string ( ) , driver_block ( gpu_device_ids) ) ,
2263+ ]
2264+ . into_iter ( )
2265+ . collect ( ) ,
22112266 } )
22122267}
22132268
@@ -7460,7 +7515,8 @@ mod tests {
74607515 dockerfile_sources_supported_for_gateway, format_endpoint, format_gateway_select_header,
74617516 format_gateway_select_items, format_provider_attachment_table, gateway_add,
74627517 gateway_auth_label, gateway_env_override_warning, gateway_select_with, gateway_type_label,
7463- git_sync_files, http_health_check, image_requests_gpu, import_local_package_mtls_bundle,
7518+ git_sync_files, gpu_count_from_cli, gpu_device_ids_from_cli, gpu_driver_config_from_cli,
7519+ http_health_check, image_requests_gpu, import_local_package_mtls_bundle,
74647520 inferred_provider_type, package_managed_tls_dirs, parse_cli_setting_value,
74657521 parse_credential_expiry_cli_value, parse_credential_expiry_pairs, parse_credential_pairs,
74667522 plaintext_gateway_is_remote, progress_step_from_metadata,
@@ -7946,49 +8002,65 @@ mod tests {
79468002 }
79478003 }
79488004
8005+ #[ test]
8006+ fn gpu_device_ids_from_cli_trims_gpu_device ( ) {
8007+ assert_eq ! (
8008+ gpu_device_ids_from_cli( Some ( " nvidia.com/gpu=0 " ) ) ,
8009+ vec![ "nvidia.com/gpu=0" . to_string( ) ]
8010+ ) ;
8011+ }
8012+
8013+ #[ test]
8014+ fn gpu_device_ids_from_cli_omits_empty_device ( ) {
8015+ assert ! ( gpu_device_ids_from_cli( Some ( " " ) ) . is_empty( ) ) ;
8016+ assert ! ( gpu_device_ids_from_cli( None ) . is_empty( ) ) ;
8017+ }
8018+
8019+ #[ test]
8020+ fn gpu_count_from_cli_uses_gpu_device_id_count ( ) {
8021+ let device_ids = gpu_device_ids_from_cli ( Some ( "nvidia.com/gpu=0" ) ) ;
8022+
8023+ assert_eq ! ( gpu_count_from_cli( None , & device_ids) , Some ( 1 ) ) ;
8024+ assert_eq ! ( gpu_count_from_cli( Some ( 2 ) , & device_ids) , Some ( 1 ) ) ;
8025+ }
8026+
79498027 #[ test]
79508028 fn resource_requirements_from_cli_uses_presence_for_default_gpu ( ) {
7951- let requirements = resource_requirements_from_cli ( true , None , None )
8029+ let requirements = resource_requirements_from_cli ( true , None )
79528030 . expect ( "resource requirements should be present" ) ;
79538031 let gpu = requirements. gpu . expect ( "GPU requirement should be present" ) ;
79548032
7955- assert ! ( gpu. device_ids. is_empty( ) ) ;
79568033 assert_eq ! ( gpu. count, None ) ;
79578034 }
79588035
79598036 #[ test]
7960- fn resource_requirements_from_cli_maps_gpu_device_to_one_device_id ( ) {
7961- let requirements = resource_requirements_from_cli ( true , Some ( "0000:2d:00. 0" ) , None )
7962- . expect ( "resource requirements should be present" ) ;
7963- let gpu = requirements . gpu . expect ( "GPU requirement should be present" ) ;
8037+ fn gpu_driver_config_from_cli_maps_gpu_device_to_driver_blocks ( ) {
8038+ let device_ids = gpu_device_ids_from_cli ( Some ( "nvidia.com/gpu= 0" ) ) ;
8039+ let config =
8040+ gpu_driver_config_from_cli ( & device_ids ) . expect ( "driver config should be present" ) ;
79648041
7965- assert_eq ! ( gpu. device_ids, vec![ "0000:2d:00.0" ] ) ;
7966- assert_eq ! ( gpu. count, None ) ;
8042+ assert ! ( config. fields. contains_key( "docker" ) ) ;
8043+ assert ! ( config. fields. contains_key( "podman" ) ) ;
8044+ assert ! ( config. fields. contains_key( "vm" ) ) ;
79678045 }
79688046
79698047 #[ test]
79708048 fn resource_requirements_from_cli_maps_gpu_count ( ) {
79718049 let requirements =
7972- resource_requirements_from_cli ( true , None , Some ( 2 ) ) . expect ( "requirements should exist" ) ;
8050+ resource_requirements_from_cli ( true , Some ( 2 ) ) . expect ( "requirements should exist" ) ;
79738051 let gpu = requirements. gpu . expect ( "GPU requirement should be present" ) ;
79748052
7975- assert ! ( gpu. device_ids. is_empty( ) ) ;
79768053 assert_eq ! ( gpu. count, Some ( 2 ) ) ;
79778054 }
79788055
79798056 #[ test]
7980- fn resource_requirements_from_cli_preserves_device_and_gpu_count_for_gateway_validation ( ) {
7981- let requirements = resource_requirements_from_cli ( true , Some ( "nvidia.com/gpu=0" ) , Some ( 2 ) )
7982- . expect ( "requirements should exist" ) ;
7983- let gpu = requirements. gpu . expect ( "GPU requirement should be present" ) ;
7984-
7985- assert_eq ! ( gpu. device_ids, vec![ "nvidia.com/gpu=0" ] ) ;
7986- assert_eq ! ( gpu. count, Some ( 2 ) ) ;
8057+ fn gpu_driver_config_from_cli_omits_empty_device ( ) {
8058+ assert ! ( gpu_driver_config_from_cli( & [ ] ) . is_none( ) ) ;
79878059 }
79888060
79898061 #[ test]
79908062 fn resource_requirements_from_cli_omits_gpu_request_when_not_requested ( ) {
7991- assert ! ( resource_requirements_from_cli( false , Some ( "0" ) , None ) . is_none( ) ) ;
8063+ assert ! ( resource_requirements_from_cli( false , None ) . is_none( ) ) ;
79928064 }
79938065
79948066 #[ test]
0 commit comments