@@ -232,6 +232,34 @@ def __init__(self, datafile: str | bytes, logger: Logger, error_handler: Any):
232232 self .experiment_feature_map [exp_id ] = [feature .id ]
233233 rules .append (self .experiment_id_map [exp_id ])
234234
235+ # Feature Rollout support: inject the "everyone else" variation
236+ # into any experiment with type == "feature_rollout"
237+ rollout_for_flag = (
238+ None if len (feature .rolloutId ) == 0
239+ else self .rollout_id_map .get (feature .rolloutId )
240+ )
241+ if rollout_for_flag :
242+ everyone_else_variation = self ._get_everyone_else_variation (rollout_for_flag )
243+ if everyone_else_variation is not None :
244+ for experiment in rules :
245+ if getattr (experiment , 'type' , None ) == 'feature_rollout' :
246+ # Append the everyone else variation to the experiment
247+ experiment .variations .append (everyone_else_variation )
248+ # Add traffic allocation entry with endOfRange=10000
249+ experiment .trafficAllocation .append ({
250+ 'entityId' : everyone_else_variation ['id' ],
251+ 'endOfRange' : 10000 ,
252+ })
253+ # Update variation maps for this experiment
254+ var_entity = entities .Variation (** everyone_else_variation )
255+ self .variation_key_map [experiment .key ][var_entity .key ] = var_entity
256+ self .variation_id_map [experiment .key ][var_entity .id ] = var_entity
257+ self .variation_id_map_by_experiment_id [experiment .id ][var_entity .id ] = var_entity
258+ self .variation_key_map_by_experiment_id [experiment .id ][var_entity .key ] = var_entity
259+ self .variation_variable_usage_map [var_entity .id ] = self ._generate_key_map (
260+ var_entity .variables , 'id' , entities .Variation .VariableUsage
261+ )
262+
235263 flag_id = feature .id
236264 applicable_holdouts : list [entities .Holdout ] = []
237265
@@ -304,6 +332,32 @@ def _generate_key_map(
304332
305333 return key_map
306334
335+ @staticmethod
336+ def _get_everyone_else_variation (rollout : entities .Layer ) -> Optional [types .VariationDict ]:
337+ """ Get the "everyone else" variation from a rollout.
338+
339+ The "everyone else" rule is the last experiment in the rollout,
340+ and its first variation is the "everyone else" variation.
341+
342+ Args:
343+ rollout: The rollout (Layer) entity to get the variation from.
344+
345+ Returns:
346+ The "everyone else" variation dict, or None if not available.
347+ """
348+ if not rollout .experiments :
349+ return None
350+
351+ everyone_else_rule = rollout .experiments [- 1 ]
352+ variations = everyone_else_rule .get ('variations' , []) if isinstance (
353+ everyone_else_rule , dict
354+ ) else getattr (everyone_else_rule , 'variations' , [])
355+
356+ if not variations :
357+ return None
358+
359+ return variations [0 ]
360+
307361 @staticmethod
308362 def _deserialize_audience (audience_map : dict [str , entities .Audience ]) -> dict [str , entities .Audience ]:
309363 """ Helper method to de-serialize and populate audience map with the condition list and structure.
0 commit comments