@eppo/js-client-sdk-common
Advanced tools
Comparing version 4.0.0 to 4.0.1
@@ -161,2 +161,3 @@ import { IAssignmentLogger } from '../assignment-logger'; | ||
getBanditActionDetails(flagKey: string, subjectKey: string, subjectAttributes: BanditSubjectAttributes, actions: BanditActions, defaultValue: string): IAssignmentDetails<string>; | ||
private evaluateBanditAction; | ||
private ensureNonContextualSubjectAttributes; | ||
@@ -163,0 +164,0 @@ private ensureContextualSubjectAttributes; |
@@ -257,64 +257,74 @@ "use strict"; | ||
getBanditActionDetails(flagKey, subjectKey, subjectAttributes, actions, defaultValue) { | ||
var _a, _b, _c; | ||
const flagEvaluationDetailsBuilder = this.flagEvaluationDetailsBuilder(flagKey); | ||
const defaultResult = { variation: defaultValue, action: null }; | ||
var _a, _b; | ||
let variation = defaultValue; | ||
let action = null; | ||
// Initialize with a generic evaluation details. This will mutate as the function progresses. | ||
let evaluationDetails = this.flagEvaluationDetailsBuilder(flagKey).buildForNoneResult('ASSIGNMENT_ERROR', 'Unexpected error getting assigned variation for bandit action'); | ||
try { | ||
const banditVariations = (_a = this.banditVariationConfigurationStore) === null || _a === void 0 ? void 0 : _a.get(flagKey); | ||
if (banditVariations && !Object.keys(actions).length) { | ||
// No actions passed for a flag known to have an active bandit, so we just return the default values so that | ||
// we don't log a variation or bandit assignment | ||
return Object.assign(Object.assign({}, defaultResult), { evaluationDetails: flagEvaluationDetailsBuilder.buildForNoneResult('NO_ACTIONS_SUPPLIED_FOR_BANDIT', 'No bandit actions passed for a flag known to have an active bandit') }); | ||
} | ||
// Get the assigned variation for the flag with a possible bandit | ||
// Note for getting assignments, we don't care about context | ||
const nonContextualSubjectAttributes = this.ensureNonContextualSubjectAttributes(subjectAttributes); | ||
const { variation: _variation, evaluationDetails } = this.getStringAssignmentDetails(flagKey, subjectKey, nonContextualSubjectAttributes, defaultValue); | ||
variation = _variation; | ||
const { variation: assignedVariation, evaluationDetails: assignmentEvaluationDetails } = this.getStringAssignmentDetails(flagKey, subjectKey, nonContextualSubjectAttributes, defaultValue); | ||
variation = assignedVariation; | ||
evaluationDetails = assignmentEvaluationDetails; | ||
// Check if the assigned variation is an active bandit | ||
// Note: the reason for non-bandit assignments include the subject being bucketed into a non-bandit variation or | ||
// a rollout having been done. | ||
const banditVariations = (_a = this.banditVariationConfigurationStore) === null || _a === void 0 ? void 0 : _a.get(flagKey); | ||
const banditKey = (_b = banditVariations === null || banditVariations === void 0 ? void 0 : banditVariations.find((banditVariation) => banditVariation.variationValue === variation)) === null || _b === void 0 ? void 0 : _b.key; | ||
if (banditKey) { | ||
// Retrieve the model parameters for the bandit | ||
const banditParameters = (_c = this.banditModelConfigurationStore) === null || _c === void 0 ? void 0 : _c.get(banditKey); | ||
if (!banditParameters) { | ||
throw new Error('No model parameters for bandit ' + banditKey); | ||
} | ||
const banditModelData = banditParameters.modelData; | ||
const contextualSubjectAttributes = this.ensureContextualSubjectAttributes(subjectAttributes); | ||
const actionsWithContextualAttributes = this.ensureActionsWithContextualAttributes(actions); | ||
const banditEvaluation = this.banditEvaluator.evaluateBandit(flagKey, subjectKey, contextualSubjectAttributes, actionsWithContextualAttributes, banditModelData); | ||
action = banditEvaluation.actionKey; | ||
evaluationDetails.banditKey = banditKey; | ||
action = this.evaluateBanditAction(flagKey, subjectKey, subjectAttributes, actions, banditKey, evaluationDetails); | ||
evaluationDetails.banditAction = action; | ||
evaluationDetails.banditKey = banditKey; | ||
const banditEvent = { | ||
timestamp: new Date().toISOString(), | ||
featureFlag: flagKey, | ||
bandit: banditKey, | ||
subject: subjectKey, | ||
action, | ||
actionProbability: banditEvaluation.actionWeight, | ||
optimalityGap: banditEvaluation.optimalityGap, | ||
modelVersion: banditParameters.modelVersion, | ||
subjectNumericAttributes: contextualSubjectAttributes.numericAttributes, | ||
subjectCategoricalAttributes: contextualSubjectAttributes.categoricalAttributes, | ||
actionNumericAttributes: actionsWithContextualAttributes[action].numericAttributes, | ||
actionCategoricalAttributes: actionsWithContextualAttributes[action].categoricalAttributes, | ||
metaData: this.buildLoggerMetadata(), | ||
evaluationDetails, | ||
}; | ||
this.logBanditAction(banditEvent); | ||
} | ||
return { variation, action, evaluationDetails }; | ||
} | ||
catch (err) { | ||
application_logger_1.logger.error('Error evaluating bandit action', err); | ||
application_logger_1.logger.error('Error determining bandit action', err); | ||
if (!this.isGracefulFailureMode) { | ||
throw err; | ||
} | ||
return Object.assign(Object.assign({}, defaultResult), { evaluationDetails: flagEvaluationDetailsBuilder.buildForNoneResult('ASSIGNMENT_ERROR', `Error evaluating bandit action: ${err.message}`) }); | ||
if (variation) { | ||
// If we have a variation, the assignment succeeded and the error was with the bandit part. | ||
// Update the flag evaluation code to indicate that | ||
evaluationDetails.flagEvaluationCode = 'BANDIT_ERROR'; | ||
} | ||
evaluationDetails.flagEvaluationDescription = `Error evaluating bandit action: ${err.message}`; | ||
} | ||
return { variation, action, evaluationDetails }; | ||
} | ||
evaluateBanditAction(flagKey, subjectKey, subjectAttributes, actions, banditKey, evaluationDetails) { | ||
var _a; | ||
// If no actions, there is nothing to do | ||
if (!Object.keys(actions).length) { | ||
return null; | ||
} | ||
// Retrieve the model parameters for the bandit | ||
const banditParameters = (_a = this.banditModelConfigurationStore) === null || _a === void 0 ? void 0 : _a.get(banditKey); | ||
if (!banditParameters) { | ||
throw new Error('No model parameters for bandit ' + banditKey); | ||
} | ||
const banditModelData = banditParameters.modelData; | ||
const contextualSubjectAttributes = this.ensureContextualSubjectAttributes(subjectAttributes); | ||
const actionsWithContextualAttributes = this.ensureActionsWithContextualAttributes(actions); | ||
const banditEvaluation = this.banditEvaluator.evaluateBandit(flagKey, subjectKey, contextualSubjectAttributes, actionsWithContextualAttributes, banditModelData); | ||
const action = banditEvaluation.actionKey; | ||
const banditEvent = { | ||
timestamp: new Date().toISOString(), | ||
featureFlag: flagKey, | ||
bandit: banditKey, | ||
subject: subjectKey, | ||
action, | ||
actionProbability: banditEvaluation.actionWeight, | ||
optimalityGap: banditEvaluation.optimalityGap, | ||
modelVersion: banditParameters.modelVersion, | ||
subjectNumericAttributes: contextualSubjectAttributes.numericAttributes, | ||
subjectCategoricalAttributes: contextualSubjectAttributes.categoricalAttributes, | ||
actionNumericAttributes: actionsWithContextualAttributes[action].numericAttributes, | ||
actionCategoricalAttributes: actionsWithContextualAttributes[action].categoricalAttributes, | ||
metaData: this.buildLoggerMetadata(), | ||
evaluationDetails, | ||
}; | ||
this.logBanditAction(banditEvent); | ||
return action; | ||
} | ||
ensureNonContextualSubjectAttributes(subjectAttributes) { | ||
@@ -321,0 +331,0 @@ let result; |
import { Allocation, Variation, VariationType } from './interfaces'; | ||
import { Rule } from './rules'; | ||
export declare const flagEvaluationCodes: readonly ["MATCH", "FLAG_UNRECOGNIZED_OR_DISABLED", "TYPE_MISMATCH", "ASSIGNMENT_ERROR", "DEFAULT_ALLOCATION_NULL", "NO_ACTIONS_SUPPLIED_FOR_BANDIT"]; | ||
export declare const flagEvaluationCodes: readonly ["MATCH", "FLAG_UNRECOGNIZED_OR_DISABLED", "TYPE_MISMATCH", "ASSIGNMENT_ERROR", "DEFAULT_ALLOCATION_NULL", "NO_ACTIONS_SUPPLIED_FOR_BANDIT", "BANDIT_ERROR"]; | ||
export declare type FlagEvaluationCode = typeof flagEvaluationCodes[number]; | ||
@@ -5,0 +5,0 @@ export declare enum AllocationEvaluationCode { |
@@ -12,2 +12,3 @@ "use strict"; | ||
'NO_ACTIONS_SUPPLIED_FOR_BANDIT', | ||
'BANDIT_ERROR', | ||
]; | ||
@@ -14,0 +15,0 @@ var AllocationEvaluationCode; |
{ | ||
"name": "@eppo/js-client-sdk-common", | ||
"version": "4.0.0", | ||
"version": "4.0.1", | ||
"description": "Eppo SDK for client-side JavaScript applications (base for both web and react native)", | ||
@@ -5,0 +5,0 @@ "main": "dist/index.js", |
@@ -465,20 +465,13 @@ import ApiEndpoints from '../api-endpoints'; | ||
): IAssignmentDetails<string> { | ||
const flagEvaluationDetailsBuilder = this.flagEvaluationDetailsBuilder(flagKey); | ||
const defaultResult = { variation: defaultValue, action: null }; | ||
let variation = defaultValue; | ||
let action: string | null = null; | ||
// Initialize with a generic evaluation details. This will mutate as the function progresses. | ||
let evaluationDetails: IFlagEvaluationDetails = this.flagEvaluationDetailsBuilder( | ||
flagKey, | ||
).buildForNoneResult( | ||
'ASSIGNMENT_ERROR', | ||
'Unexpected error getting assigned variation for bandit action', | ||
); | ||
try { | ||
const banditVariations = this.banditVariationConfigurationStore?.get(flagKey); | ||
if (banditVariations && !Object.keys(actions).length) { | ||
// No actions passed for a flag known to have an active bandit, so we just return the default values so that | ||
// we don't log a variation or bandit assignment | ||
return { | ||
...defaultResult, | ||
evaluationDetails: flagEvaluationDetailsBuilder.buildForNoneResult( | ||
'NO_ACTIONS_SUPPLIED_FOR_BANDIT', | ||
'No bandit actions passed for a flag known to have an active bandit', | ||
), | ||
}; | ||
} | ||
// Get the assigned variation for the flag with a possible bandit | ||
@@ -488,9 +481,11 @@ // Note for getting assignments, we don't care about context | ||
this.ensureNonContextualSubjectAttributes(subjectAttributes); | ||
const { variation: _variation, evaluationDetails } = this.getStringAssignmentDetails( | ||
flagKey, | ||
subjectKey, | ||
nonContextualSubjectAttributes, | ||
defaultValue, | ||
); | ||
variation = _variation; | ||
const { variation: assignedVariation, evaluationDetails: assignmentEvaluationDetails } = | ||
this.getStringAssignmentDetails( | ||
flagKey, | ||
subjectKey, | ||
nonContextualSubjectAttributes, | ||
defaultValue, | ||
); | ||
variation = assignedVariation; | ||
evaluationDetails = assignmentEvaluationDetails; | ||
@@ -500,2 +495,3 @@ // Check if the assigned variation is an active bandit | ||
// a rollout having been done. | ||
const banditVariations = this.banditVariationConfigurationStore?.get(flagKey); | ||
const banditKey = banditVariations?.find( | ||
@@ -506,59 +502,80 @@ (banditVariation) => banditVariation.variationValue === variation, | ||
if (banditKey) { | ||
// Retrieve the model parameters for the bandit | ||
const banditParameters = this.banditModelConfigurationStore?.get(banditKey); | ||
if (!banditParameters) { | ||
throw new Error('No model parameters for bandit ' + banditKey); | ||
} | ||
const banditModelData = banditParameters.modelData; | ||
const contextualSubjectAttributes = | ||
this.ensureContextualSubjectAttributes(subjectAttributes); | ||
const actionsWithContextualAttributes = this.ensureActionsWithContextualAttributes(actions); | ||
const banditEvaluation = this.banditEvaluator.evaluateBandit( | ||
evaluationDetails.banditKey = banditKey; | ||
action = this.evaluateBanditAction( | ||
flagKey, | ||
subjectKey, | ||
contextualSubjectAttributes, | ||
actionsWithContextualAttributes, | ||
banditModelData, | ||
subjectAttributes, | ||
actions, | ||
banditKey, | ||
evaluationDetails, | ||
); | ||
action = banditEvaluation.actionKey; | ||
evaluationDetails.banditAction = action; | ||
evaluationDetails.banditKey = banditKey; | ||
const banditEvent: IBanditEvent = { | ||
timestamp: new Date().toISOString(), | ||
featureFlag: flagKey, | ||
bandit: banditKey, | ||
subject: subjectKey, | ||
action, | ||
actionProbability: banditEvaluation.actionWeight, | ||
optimalityGap: banditEvaluation.optimalityGap, | ||
modelVersion: banditParameters.modelVersion, | ||
subjectNumericAttributes: contextualSubjectAttributes.numericAttributes, | ||
subjectCategoricalAttributes: contextualSubjectAttributes.categoricalAttributes, | ||
actionNumericAttributes: actionsWithContextualAttributes[action].numericAttributes, | ||
actionCategoricalAttributes: | ||
actionsWithContextualAttributes[action].categoricalAttributes, | ||
metaData: this.buildLoggerMetadata(), | ||
evaluationDetails, | ||
}; | ||
this.logBanditAction(banditEvent); | ||
} | ||
return { variation, action, evaluationDetails }; | ||
} catch (err) { | ||
logger.error('Error evaluating bandit action', err); | ||
logger.error('Error determining bandit action', err); | ||
if (!this.isGracefulFailureMode) { | ||
throw err; | ||
} | ||
return { | ||
...defaultResult, | ||
evaluationDetails: flagEvaluationDetailsBuilder.buildForNoneResult( | ||
'ASSIGNMENT_ERROR', | ||
`Error evaluating bandit action: ${err.message}`, | ||
), | ||
}; | ||
if (variation) { | ||
// If we have a variation, the assignment succeeded and the error was with the bandit part. | ||
// Update the flag evaluation code to indicate that | ||
evaluationDetails.flagEvaluationCode = 'BANDIT_ERROR'; | ||
} | ||
evaluationDetails.flagEvaluationDescription = `Error evaluating bandit action: ${err.message}`; | ||
} | ||
return { variation, action, evaluationDetails }; | ||
} | ||
private evaluateBanditAction( | ||
flagKey: string, | ||
subjectKey: string, | ||
subjectAttributes: BanditSubjectAttributes, | ||
actions: BanditActions, | ||
banditKey: string, | ||
evaluationDetails: IFlagEvaluationDetails, | ||
): string | null { | ||
// If no actions, there is nothing to do | ||
if (!Object.keys(actions).length) { | ||
return null; | ||
} | ||
// Retrieve the model parameters for the bandit | ||
const banditParameters = this.banditModelConfigurationStore?.get(banditKey); | ||
if (!banditParameters) { | ||
throw new Error('No model parameters for bandit ' + banditKey); | ||
} | ||
const banditModelData = banditParameters.modelData; | ||
const contextualSubjectAttributes = this.ensureContextualSubjectAttributes(subjectAttributes); | ||
const actionsWithContextualAttributes = this.ensureActionsWithContextualAttributes(actions); | ||
const banditEvaluation = this.banditEvaluator.evaluateBandit( | ||
flagKey, | ||
subjectKey, | ||
contextualSubjectAttributes, | ||
actionsWithContextualAttributes, | ||
banditModelData, | ||
); | ||
const action = banditEvaluation.actionKey; | ||
const banditEvent: IBanditEvent = { | ||
timestamp: new Date().toISOString(), | ||
featureFlag: flagKey, | ||
bandit: banditKey, | ||
subject: subjectKey, | ||
action, | ||
actionProbability: banditEvaluation.actionWeight, | ||
optimalityGap: banditEvaluation.optimalityGap, | ||
modelVersion: banditParameters.modelVersion, | ||
subjectNumericAttributes: contextualSubjectAttributes.numericAttributes, | ||
subjectCategoricalAttributes: contextualSubjectAttributes.categoricalAttributes, | ||
actionNumericAttributes: actionsWithContextualAttributes[action].numericAttributes, | ||
actionCategoricalAttributes: actionsWithContextualAttributes[action].categoricalAttributes, | ||
metaData: this.buildLoggerMetadata(), | ||
evaluationDetails, | ||
}; | ||
this.logBanditAction(banditEvent); | ||
return action; | ||
} | ||
private ensureNonContextualSubjectAttributes( | ||
@@ -565,0 +582,0 @@ subjectAttributes: BanditSubjectAttributes, |
@@ -11,2 +11,3 @@ import { Allocation, Variation, VariationType } from './interfaces'; | ||
'NO_ACTIONS_SUPPLIED_FOR_BANDIT', | ||
'BANDIT_ERROR', | ||
] as const; | ||
@@ -13,0 +14,0 @@ |
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is too big to display
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
Sorry, the diff of this file is not supported yet
790796
6407