@@ -462,99 +462,116 @@ export default class EppoClient {
462462 actions : BanditActions ,
463463 defaultValue : string ,
464464 ) : IAssignmentDetails < string > {
465- const flagEvaluationDetailsBuilder = this . flagEvaluationDetailsBuilder ( flagKey ) ;
466- const defaultResult = { variation : defaultValue , action : null } ;
467465 let variation = defaultValue ;
468466 let action : string | null = null ;
469- try {
470- const banditVariations = this . banditVariationConfigurationStore ?. get ( flagKey ) ;
471- if ( banditVariations && ! Object . keys ( actions ) . length ) {
472- // No actions passed for a flag known to have an active bandit, so we just return the default values so that
473- // we don't log a variation or bandit assignment
474- return {
475- ...defaultResult ,
476- evaluationDetails : flagEvaluationDetailsBuilder . buildForNoneResult (
477- 'NO_ACTIONS_SUPPLIED_FOR_BANDIT' ,
478- 'No bandit actions passed for a flag known to have an active bandit' ,
479- ) ,
480- } ;
481- }
482467
468+ // Initialize with a generic evaluation details. This will mutate as the function progresses.
469+ let evaluationDetails : IFlagEvaluationDetails = this . flagEvaluationDetailsBuilder (
470+ flagKey ,
471+ ) . buildForNoneResult (
472+ 'ASSIGNMENT_ERROR' ,
473+ 'Unexpected error getting assigned variation for bandit action' ,
474+ ) ;
475+ try {
483476 // Get the assigned variation for the flag with a possible bandit
484477 // Note for getting assignments, we don't care about context
485478 const nonContextualSubjectAttributes =
486479 this . ensureNonContextualSubjectAttributes ( subjectAttributes ) ;
487- const { variation : _variation , evaluationDetails } = this . getStringAssignmentDetails (
488- flagKey ,
489- subjectKey ,
490- nonContextualSubjectAttributes ,
491- defaultValue ,
492- ) ;
493- variation = _variation ;
480+ const { variation : assignedVariation , evaluationDetails : assignmentEvaluationDetails } =
481+ this . getStringAssignmentDetails (
482+ flagKey ,
483+ subjectKey ,
484+ nonContextualSubjectAttributes ,
485+ defaultValue ,
486+ ) ;
487+ variation = assignedVariation ;
488+ evaluationDetails = assignmentEvaluationDetails ;
494489
495490 // Check if the assigned variation is an active bandit
496491 // Note: the reason for non-bandit assignments include the subject being bucketed into a non-bandit variation or
497492 // a rollout having been done.
493+ const banditVariations = this . banditVariationConfigurationStore ?. get ( flagKey ) ;
498494 const banditKey = banditVariations ?. find (
499495 ( banditVariation ) => banditVariation . variationValue === variation ,
500496 ) ?. key ;
501497
502498 if ( banditKey ) {
503- // Retrieve the model parameters for the bandit
504- const banditParameters = this . banditModelConfigurationStore ?. get ( banditKey ) ;
505-
506- if ( ! banditParameters ) {
507- throw new Error ( 'No model parameters for bandit ' + banditKey ) ;
508- }
509-
510- const banditModelData = banditParameters . modelData ;
511- const contextualSubjectAttributes =
512- this . ensureContextualSubjectAttributes ( subjectAttributes ) ;
513- const actionsWithContextualAttributes = this . ensureActionsWithContextualAttributes ( actions ) ;
514- const banditEvaluation = this . banditEvaluator . evaluateBandit (
499+ evaluationDetails . banditKey = banditKey ;
500+ action = this . evaluateBanditAction (
515501 flagKey ,
516502 subjectKey ,
517- contextualSubjectAttributes ,
518- actionsWithContextualAttributes ,
519- banditModelData ,
503+ subjectAttributes ,
504+ actions ,
505+ banditKey ,
506+ evaluationDetails ,
520507 ) ;
521- action = banditEvaluation . actionKey ;
522508 evaluationDetails . banditAction = action ;
523- evaluationDetails . banditKey = banditKey ;
524-
525- const banditEvent : IBanditEvent = {
526- timestamp : new Date ( ) . toISOString ( ) ,
527- featureFlag : flagKey ,
528- bandit : banditKey ,
529- subject : subjectKey ,
530- action,
531- actionProbability : banditEvaluation . actionWeight ,
532- optimalityGap : banditEvaluation . optimalityGap ,
533- modelVersion : banditParameters . modelVersion ,
534- subjectNumericAttributes : contextualSubjectAttributes . numericAttributes ,
535- subjectCategoricalAttributes : contextualSubjectAttributes . categoricalAttributes ,
536- actionNumericAttributes : actionsWithContextualAttributes [ action ] . numericAttributes ,
537- actionCategoricalAttributes :
538- actionsWithContextualAttributes [ action ] . categoricalAttributes ,
539- metaData : this . buildLoggerMetadata ( ) ,
540- evaluationDetails,
541- } ;
542- this . logBanditAction ( banditEvent ) ;
543509 }
544- return { variation, action, evaluationDetails } ;
545510 } catch ( err ) {
546- logger . error ( 'Error evaluating bandit action' , err ) ;
511+ logger . error ( 'Error determining bandit action' , err ) ;
547512 if ( ! this . isGracefulFailureMode ) {
548513 throw err ;
549514 }
550- return {
551- ...defaultResult ,
552- evaluationDetails : flagEvaluationDetailsBuilder . buildForNoneResult (
553- 'ASSIGNMENT_ERROR' ,
554- `Error evaluating bandit action: ${ err . message } ` ,
555- ) ,
556- } ;
515+ if ( variation ) {
516+ // If we have a variation, the assignment succeeded and the error was with the bandit part.
517+ // Update the flag evaluation code to indicate that
518+ evaluationDetails . flagEvaluationCode = 'BANDIT_ERROR' ;
519+ }
520+ evaluationDetails . flagEvaluationDescription = `Error evaluating bandit action: ${ err . message } ` ;
521+ }
522+ return { variation, action, evaluationDetails } ;
523+ }
524+
525+ private evaluateBanditAction (
526+ flagKey : string ,
527+ subjectKey : string ,
528+ subjectAttributes : BanditSubjectAttributes ,
529+ actions : BanditActions ,
530+ banditKey : string ,
531+ evaluationDetails : IFlagEvaluationDetails ,
532+ ) : string | null {
533+ // If no actions, there is nothing to do
534+ if ( ! Object . keys ( actions ) . length ) {
535+ return null ;
536+ }
537+ // Retrieve the model parameters for the bandit
538+ const banditParameters = this . banditModelConfigurationStore ?. get ( banditKey ) ;
539+
540+ if ( ! banditParameters ) {
541+ throw new Error ( 'No model parameters for bandit ' + banditKey ) ;
557542 }
543+
544+ const banditModelData = banditParameters . modelData ;
545+ const contextualSubjectAttributes = this . ensureContextualSubjectAttributes ( subjectAttributes ) ;
546+ const actionsWithContextualAttributes = this . ensureActionsWithContextualAttributes ( actions ) ;
547+ const banditEvaluation = this . banditEvaluator . evaluateBandit (
548+ flagKey ,
549+ subjectKey ,
550+ contextualSubjectAttributes ,
551+ actionsWithContextualAttributes ,
552+ banditModelData ,
553+ ) ;
554+ const action = banditEvaluation . actionKey ;
555+
556+ const banditEvent : IBanditEvent = {
557+ timestamp : new Date ( ) . toISOString ( ) ,
558+ featureFlag : flagKey ,
559+ bandit : banditKey ,
560+ subject : subjectKey ,
561+ action,
562+ actionProbability : banditEvaluation . actionWeight ,
563+ optimalityGap : banditEvaluation . optimalityGap ,
564+ modelVersion : banditParameters . modelVersion ,
565+ subjectNumericAttributes : contextualSubjectAttributes . numericAttributes ,
566+ subjectCategoricalAttributes : contextualSubjectAttributes . categoricalAttributes ,
567+ actionNumericAttributes : actionsWithContextualAttributes [ action ] . numericAttributes ,
568+ actionCategoricalAttributes : actionsWithContextualAttributes [ action ] . categoricalAttributes ,
569+ metaData : this . buildLoggerMetadata ( ) ,
570+ evaluationDetails,
571+ } ;
572+ this . logBanditAction ( banditEvent ) ;
573+
574+ return action ;
558575 }
559576
560577 private ensureNonContextualSubjectAttributes (
0 commit comments