@@ -363,7 +363,6 @@ def get_variation(
363
363
b. If so, fetch the CMAB decision and assign the corresponding variation and cmab_uuid.
364
364
7. For non-CMAB experiments, bucket the user into a variation.
365
365
8. If a variation is assigned, optionally update the user profile.
366
- 9. Return the assigned variation, decision reasons, and cmab_uuid (if applicable).
367
366
368
367
Args:
369
368
project_config: Instance of ProjectConfig.
@@ -374,10 +373,11 @@ def get_variation(
374
373
options: Decide options.
375
374
376
375
Returns:
377
- A tuple of:
378
- - The assigned Variation (or None if not assigned).
379
- - A list of log messages representing decision making.
380
- - The cmab_uuid if the experiment is a CMAB experiment, otherwise None.
376
+ A VariationResult dictionary with:
377
+ - 'variation': The assigned Variation (or None if not assigned).
378
+ - 'reasons': A list of log messages representing decision making.
379
+ - 'cmab_uuid': The cmab_uuid if the experiment is a CMAB experiment, otherwise None.
380
+ - 'error': Boolean indicating if an error occurred during the decision process.
381
381
"""
382
382
user_id = user_context .user_id
383
383
if options :
@@ -657,7 +657,7 @@ def get_variation_for_feature(
657
657
feature : entities .FeatureFlag ,
658
658
user_context : OptimizelyUserContext ,
659
659
options : Optional [list [str ]] = None
660
- ) -> tuple [ Decision , list [ str ]] :
660
+ ) -> DecisionResult :
661
661
""" Returns the experiment/variation the user is bucketed in for the given feature.
662
662
663
663
Args:
@@ -667,8 +667,11 @@ def get_variation_for_feature(
667
667
options: Decide options.
668
668
669
669
Returns:
670
- Decision namedtuple consisting of experiment and variation for the user.
671
- """
670
+ A DecisionResult dictionary containing:
671
+ - 'decision': Decision namedtuple with experiment, variation, source, and cmab_uuid.
672
+ - 'error': Boolean indicating if an error occurred during the decision process.
673
+ - 'reasons': List of log messages representing decision making for the feature.
674
+ """
672
675
return self .get_variations_for_feature_list (project_config , [feature ], user_context , options )[0 ]
673
676
674
677
def validated_forced_decision (
@@ -740,17 +743,21 @@ def get_variations_for_feature_list(
740
743
features : list [entities .FeatureFlag ],
741
744
user_context : OptimizelyUserContext ,
742
745
options : Optional [Sequence [str ]] = None
743
- ) -> list [tuple [ Decision , list [ str ]] ]:
746
+ ) -> list [DecisionResult ]:
744
747
"""
745
748
Returns the list of experiment/variation the user is bucketed in for the given list of features.
749
+
746
750
Args:
747
- project_config: Instance of ProjectConfig.
748
- features: List of features for which we are determining if it is enabled or not for the given user.
749
- user_context: user context for user.
750
- options: Decide options.
751
+ project_config: Instance of ProjectConfig.
752
+ features: List of features for which we are determining if it is enabled or not for the given user.
753
+ user_context: user context for user.
754
+ options: Decide options.
751
755
752
756
Returns:
753
- List of Decision namedtuple consisting of experiment and variation for the user.
757
+ A list of DecisionResult dictionaries, each containing:
758
+ - 'decision': Decision namedtuple with experiment, variation, source, and cmab_uuid.
759
+ - 'error': Boolean indicating if an error occurred during the decision process.
760
+ - 'reasons': List of log messages representing decision making for each feature.
754
761
"""
755
762
decide_reasons : list [str ] = []
756
763
@@ -786,27 +793,45 @@ def get_variations_for_feature_list(
786
793
if forced_decision_variation :
787
794
decision_variation = forced_decision_variation
788
795
cmab_uuid = None
796
+ error = False
789
797
else :
790
798
variation_result = self .get_variation (
791
799
project_config , experiment , user_context , user_profile_tracker , feature_reasons , options
792
800
)
793
801
cmab_uuid = variation_result ['cmab_uuid' ]
794
802
variation_reasons = variation_result ['reasons' ]
795
803
decision_variation = variation_result ['variation' ]
804
+ error = variation_result ['error' ]
796
805
feature_reasons .extend (variation_reasons )
797
806
807
+ if error :
808
+ decision = Decision (experiment , None , enums .DecisionSources .FEATURE_TEST , cmab_uuid )
809
+ decision_result : DecisionResult = {
810
+ 'decision' : decision ,
811
+ 'error' : True ,
812
+ 'reasons' : feature_reasons
813
+ }
814
+ decisions .append (decision_result )
815
+ experiment_decision_found = True
816
+ break
817
+
798
818
if decision_variation :
799
819
self .logger .debug (
800
820
f'User "{ user_context .user_id } " '
801
821
f'bucketed into experiment "{ experiment .key } " of feature "{ feature .key } ".'
802
822
)
803
823
decision = Decision (experiment , decision_variation ,
804
824
enums .DecisionSources .FEATURE_TEST , cmab_uuid )
805
- decisions .append ((decision , feature_reasons ))
825
+ decision_result = {
826
+ 'decision' : decision ,
827
+ 'error' : False ,
828
+ 'reasons' : feature_reasons
829
+ }
830
+ decisions .append (decision_result )
806
831
experiment_decision_found = True # Mark that a decision was found
807
832
break # Stop after the first successful experiment decision
808
833
809
- # Only process rollout if no experiment decision was found
834
+ # Only process rollout if no experiment decision was found and no error
810
835
if not experiment_decision_found :
811
836
rollout_decision , rollout_reasons = self .get_variation_for_rollout (project_config ,
812
837
feature ,
@@ -820,7 +845,12 @@ def get_variations_for_feature_list(
820
845
self .logger .debug (f'User "{ user_context .user_id } " '
821
846
f'not bucketed into any rollout for feature "{ feature .key } ".' )
822
847
823
- decisions .append ((rollout_decision , feature_reasons ))
848
+ decision_result = {
849
+ 'decision' : rollout_decision ,
850
+ 'error' : False ,
851
+ 'reasons' : feature_reasons
852
+ }
853
+ decisions .append (decision_result )
824
854
825
855
if self .user_profile_service is not None and user_profile_tracker is not None and ignore_ups is False :
826
856
user_profile_tracker .save_user_profile ()
0 commit comments