2121
2222s3_client = boto3 .client ("s3" )
2323
24-
2524def fetch_content_from_s3 (s3_uri : str ) -> Union [Dict [str , Any ], str ]:
2625 """
2726 Fetches content from S3 URI and parses as JSON or YAML if possible
@@ -70,6 +69,23 @@ def resolve_content(content: Union[str, Dict[str, Any]]) -> Union[Dict[str, Any]
7069 return fetch_content_from_s3 (content )
7170 return content
7271
72+ # Model mapping between regions
73+ MODEL_MAPPINGS = {
74+ "us.amazon.nova-lite-v1:0" : "eu.amazon.nova-lite-v1:0" ,
75+ "us.amazon.nova-pro-v1:0" : "eu.amazon.nova-pro-v1:0" ,
76+ "us.amazon.nova-premier-v1:0" : "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" ,
77+ "us.anthropic.claude-3-haiku-20240307-v1:0" : "eu.anthropic.claude-3-haiku-20240307-v1:0" ,
78+ "us.anthropic.claude-3-5-haiku-20241022-v1:0" : "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" ,
79+ "us.anthropic.claude-haiku-4-5-20251001-v1:0" : "eu.anthropic.claude-haiku-4-5-20251001-v1:0" ,
80+ "us.anthropic.claude-3-5-sonnet-20241022-v2:0" : "eu.anthropic.claude-3-5-sonnet-20241022-v2:0" ,
81+ "us.anthropic.claude-3-7-sonnet-20250219-v1:0" : "eu.anthropic.claude-3-7-sonnet-20250219-v1:0" ,
82+ "us.anthropic.claude-sonnet-4-20250514-v1:0" : "eu.anthropic.claude-sonnet-4-20250514-v1:0" ,
83+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0:1m" : "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" ,
84+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0" : "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" ,
85+ "us.anthropic.claude-sonnet-4-5-20250929-v1:0:1m" : "eu.anthropic.claude-sonnet-4-5-20250929-v1:0:1m" ,
86+ "us.anthropic.claude-opus-4-20250514-v1:0" : "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" ,
87+ "us.anthropic.claude-opus-4-1-20250805-v1:0" : "eu.anthropic.claude-sonnet-4-5-20250929-v1:0" ,
88+ }
7389
7490def get_current_region () -> str :
7591 """Get the current AWS region"""
@@ -86,51 +102,83 @@ def is_us_region(region: str) -> bool:
86102 return region .startswith ("us-" )
87103
88104
89- def _should_include_model (model_id : str , region_type : str ) -> bool :
90- """Check if a model should be included for the given region type"""
91- # Non-string items are always included
92- if not isinstance (model_id , str ):
93- return True
94-
95- # For other regions or non-prefixed models, include everything
96- if region_type not in ["us" , "eu" ] or not (
97- model_id .startswith ("us." ) or model_id .startswith ("eu." )
98- ):
99- return True
100-
101- # For US regions: exclude EU models
102- if region_type == "us" and model_id .startswith ("eu." ):
103- return False
104-
105- # For EU regions: exclude US models
106- if region_type == "eu" and model_id .startswith ("us." ):
107- return False
108-
109- return True
105+ def get_model_mapping (model_id : str , target_region_type : str ) -> str :
106+ """Get the equivalent model for the target region type"""
107+ if target_region_type == "eu" :
108+ return MODEL_MAPPINGS .get (model_id , model_id )
109+ elif target_region_type == "us" :
110+ # Reverse mapping for US
111+ for us_model , eu_model in MODEL_MAPPINGS .items ():
112+ if model_id == eu_model :
113+ return us_model
114+ return model_id
115+ return model_id
110116
111117
112118def filter_models_by_region (data : Any , region_type : str ) -> Any :
113119 """Filter out models that don't match the region type"""
114120 if isinstance (data , dict ):
115- return {
116- key : (
117- [item for item in value if _should_include_model (item , region_type )]
118- if isinstance (value , list )
119- and any (
120- isinstance (item , str ) and ("us." in item or "eu." in item )
121- for item in value
122- )
123- else filter_models_by_region (value , region_type )
124- )
125- for key , value in data .items ()
126- }
127-
128- if isinstance (data , list ):
121+ filtered_data = {}
122+ for key , value in data .items ():
123+ if isinstance (value , list ) and any (
124+ isinstance (item , str ) and ("us." in item or "eu." in item )
125+ for item in value
126+ ):
127+ # This is a model list - filter it
128+ filtered_list = []
129+ for item in value :
130+ if isinstance (item , str ):
131+ # Include models that match the region type or are region-agnostic
132+ if region_type == "us" :
133+ # Include US models and non-region-specific models, exclude EU models
134+ if item .startswith ("us." ) or (not item .startswith ("eu." ) and not item .startswith ("us." )):
135+ filtered_list .append (item )
136+ elif region_type == "eu" :
137+ # Include EU models and non-region-specific models, exclude US models
138+ if item .startswith ("eu." ) or (not item .startswith ("eu." ) and not item .startswith ("us." )):
139+ filtered_list .append (item )
140+ else :
141+ # For other regions, include all models
142+ filtered_list .append (item )
143+ else :
144+ filtered_list .append (item )
145+ filtered_data [key ] = filtered_list
146+ else :
147+ filtered_data [key ] = filter_models_by_region (value , region_type )
148+ return filtered_data
149+ elif isinstance (data , list ):
129150 return [filter_models_by_region (item , region_type ) for item in data ]
151+ return data
130152
153+
154+ def swap_model_ids (data : Any , region_type : str ) -> Any :
155+ """Swap model IDs to match the region type"""
156+ if isinstance (data , dict ):
157+ swapped_data = {}
158+ for key , value in data .items ():
159+ if isinstance (value , str ) and ("us." in value or "eu." in value ):
160+ # This is a model ID - check if it needs swapping
161+ if region_type == "us" and value .startswith ("eu." ):
162+ new_model = get_model_mapping (value , "us" )
163+ if new_model != value :
164+ logger .info (f"Swapped EU model { value } to US model { new_model } " )
165+ swapped_data [key ] = new_model
166+ elif region_type == "eu" and value .startswith ("us." ):
167+ new_model = get_model_mapping (value , "eu" )
168+ if new_model != value :
169+ logger .info (f"Swapped US model { value } to EU model { new_model } " )
170+ swapped_data [key ] = new_model
171+ else :
172+ swapped_data [key ] = value
173+ else :
174+ swapped_data [key ] = swap_model_ids (value , region_type )
175+ return swapped_data
176+ elif isinstance (data , list ):
177+ return [swap_model_ids (item , region_type ) for item in data ]
131178 return data
132179
133180
181+
134182def generate_physical_id (stack_id : str , logical_id : str ) -> str :
135183 """
136184 Generates a consistent physical ID for the custom resource
0 commit comments