@@ -456,7 +456,10 @@ def _prepare_export_inputs(self):
456456 example_cache_position = torch .arange (seq_length , dtype = torch .long , device = self .model .device )
457457 max_seq_len = self .metadata .get ("get_max_seq_len" )
458458 sliding_window = self .metadata .get ("sliding_window" , float ("inf" ))
459- max_dim = min (max_seq_len , sliding_window ) - 1
459+ if self .use_custom_kv_cache and self .use_custom_sdpa :
460+ max_dim = max_seq_len - 1
461+ else :
462+ max_dim = min (max_seq_len , sliding_window ) - 1
460463 seq_len_dim = torch .export .Dim ("seq_length_dim" , max = max_dim )
461464 dynamic_shapes = {
462465 "input_ids" : {1 : seq_len_dim },
@@ -491,22 +494,47 @@ def export(
491494 f"Exporting using input_ids({ input_ids .shape } )={ input_ids } , cache_position({ cache_position .shape } )={ cache_position } , dynamic_shapes={ dynamic_shapes } , strict={ strict } "
492495 )
493496
494- exportable_module = TorchExportableModuleForDecoderOnlyLM (
495- self .model ,
496- )
497- self ._register_custom_attention (exportable_module )
498-
499- if self .use_custom_kv_cache :
500- from optimum .executorch .attentions .custom_kv_cache import (
501- replace_with_et_custom_kv_cache ,
497+ max_seq_len = self .metadata .get ("get_max_seq_len" )
498+ sliding_window = self .metadata .get ("sliding_window" )
499+ use_ring_cache = self .use_custom_kv_cache and self .use_custom_sdpa and sliding_window is not None
500+
501+ if use_ring_cache :
502+ from transformers .integrations .executorch import TorchExportableModuleWithHybridCache
503+ from optimum .executorch .attentions .custom_kv_cache import ETCustomHybridCache
504+
505+ # Bypass TorchExportableModuleWithHybridCache.__init__ — it calls StaticCache which
506+ # caps sliding layers to sliding_window via StaticSlidingWindowLayer, baking a
507+ # <= sliding_window guard into torch.export. Instead, directly install
508+ # ETCustomHybridCache sized to max_seq_len, then patch sliding layer max_cache_len
509+ # so get_mask_sizes() returns max_seq_len during tracing.
510+ exportable_module_inner = TorchExportableModuleWithHybridCache .__new__ (TorchExportableModuleWithHybridCache )
511+ torch .nn .Module .__init__ (exportable_module_inner )
512+ exportable_module_inner .model = self .model
513+ exportable_module_inner .cache = ETCustomHybridCache (
514+ config = self .model .config , max_batch_size = 1 , max_cache_len = max_seq_len ,
515+ device = self .model .device , dtype = self .model .dtype ,
502516 )
517+ for layer in exportable_module_inner .cache .layers :
518+ if layer .is_sliding :
519+ layer .max_cache_len = max_seq_len
520+ for i in range (len (exportable_module_inner .cache .kv_cache )):
521+ exportable_module_inner .register_buffer (
522+ f"key_cache_{ i } " , exportable_module_inner .cache .kv_cache [i ].k_cache , persistent = False )
523+ exportable_module_inner .register_buffer (
524+ f"value_cache_{ i } " , exportable_module_inner .cache .kv_cache [i ].v_cache , persistent = False )
525+ if exportable_module_inner .cache .layers [i ].is_sliding :
526+ exportable_module_inner .register_buffer (
527+ f"cache_positions_{ i } " ,
528+ exportable_module_inner .cache .kv_cache [i ].cache_positions_manager .cache_positions ,
529+ persistent = False ,
530+ )
531+ exportable_module = TorchExportableModuleForDecoderOnlyLM .__new__ (TorchExportableModuleForDecoderOnlyLM )
532+ torch .nn .Module .__init__ (exportable_module )
533+ exportable_module .model = exportable_module_inner
534+ else :
535+ exportable_module = TorchExportableModuleForDecoderOnlyLM (self .model )
503536
504- replace_with_et_custom_kv_cache (
505- exportable_module .model ,
506- self .model .config ,
507- self .model .generation_config ,
508- self .model .dtype ,
509- )
537+ self ._register_custom_attention (exportable_module )
510538
511539 with torch .no_grad ():
512540 exported_program = exportable_module .export (
0 commit comments