@higher.archi/boe 1.0.14 → 1.0.16
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/dist/abtesting/hash.d.ts +6 -0
- package/dist/abtesting/hash.d.ts.map +1 -0
- package/dist/abtesting/hash.js +16 -0
- package/dist/abtesting/hash.js.map +1 -0
- package/dist/abtesting/index.d.ts +4 -0
- package/dist/abtesting/index.d.ts.map +1 -0
- package/dist/abtesting/index.js +10 -0
- package/dist/abtesting/index.js.map +1 -0
- package/dist/abtesting/resolver.d.ts +28 -0
- package/dist/abtesting/resolver.d.ts.map +1 -0
- package/dist/abtesting/resolver.js +76 -0
- package/dist/abtesting/resolver.js.map +1 -0
- package/dist/abtesting/types.d.ts +57 -0
- package/dist/abtesting/types.d.ts.map +1 -0
- package/dist/abtesting/types.js +10 -0
- package/dist/abtesting/types.js.map +1 -0
- package/dist/compare/compare.d.ts +37 -0
- package/dist/compare/compare.d.ts.map +1 -0
- package/dist/compare/compare.js +97 -0
- package/dist/compare/compare.js.map +1 -0
- package/dist/compare/index.d.ts +3 -0
- package/dist/compare/index.d.ts.map +1 -0
- package/dist/compare/index.js +7 -0
- package/dist/compare/index.js.map +1 -0
- package/dist/compare/types.d.ts +38 -0
- package/dist/compare/types.d.ts.map +1 -0
- package/dist/compare/types.js +9 -0
- package/dist/compare/types.js.map +1 -0
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +8 -0
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/abtesting/hash.ts +12 -0
- package/src/abtesting/index.ts +8 -0
- package/src/abtesting/resolver.ts +86 -0
- package/src/abtesting/types.ts +66 -0
- package/src/compare/compare.ts +106 -0
- package/src/compare/index.ts +5 -0
- package/src/compare/types.ts +41 -0
- package/src/index.ts +12 -0
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.d.ts","sourceRoot":"","sources":["../../src/abtesting/hash.ts"],"names":[],"mappings":"AAAA;;;GAGG;AACH,wBAAgB,KAAK,CAAC,KAAK,EAAE,MAAM,GAAG,MAAM,CAO3C"}
|
|
@@ -0,0 +1,16 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fnv1a = fnv1a;
|
|
4
|
+
/**
|
|
5
|
+
* FNV-1a 32-bit hash. Pure JS, zero dependencies, browser-safe.
|
|
6
|
+
* Returns a float in [0.0, 1.0) for deterministic bucket resolution.
|
|
7
|
+
*/
|
|
8
|
+
function fnv1a(input) {
|
|
9
|
+
let hash = 0x811c9dc5; // FNV offset basis
|
|
10
|
+
for (let i = 0; i < input.length; i++) {
|
|
11
|
+
hash ^= input.charCodeAt(i);
|
|
12
|
+
hash = Math.imul(hash, 0x01000193); // FNV prime
|
|
13
|
+
}
|
|
14
|
+
return (hash >>> 0) / 0x100000000;
|
|
15
|
+
}
|
|
16
|
+
//# sourceMappingURL=hash.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"hash.js","sourceRoot":"","sources":["../../src/abtesting/hash.ts"],"names":[],"mappings":";;AAIA,sBAOC;AAXD;;;GAGG;AACH,SAAgB,KAAK,CAAC,KAAa;IACjC,IAAI,IAAI,GAAG,UAAU,CAAC,CAAC,mBAAmB;IAC1C,KAAK,IAAI,CAAC,GAAG,CAAC,EAAE,CAAC,GAAG,KAAK,CAAC,MAAM,EAAE,CAAC,EAAE,EAAE,CAAC;QACtC,IAAI,IAAI,KAAK,CAAC,UAAU,CAAC,CAAC,CAAC,CAAC;QAC5B,IAAI,GAAG,IAAI,CAAC,IAAI,CAAC,IAAI,EAAE,UAAU,CAAC,CAAC,CAAC,YAAY;IAClD,CAAC;IACD,OAAO,CAAC,IAAI,KAAK,CAAC,CAAC,GAAG,WAAW,CAAC;AACpC,CAAC"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/abtesting/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,SAAS,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AAGxE,OAAO,EAAE,cAAc,EAAE,MAAM,YAAY,CAAC;AAG5C,OAAO,EAAE,KAAK,EAAE,MAAM,QAAQ,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.fnv1a = exports.resolveVariant = void 0;
|
|
4
|
+
// Core resolver
|
|
5
|
+
var resolver_1 = require("./resolver");
|
|
6
|
+
Object.defineProperty(exports, "resolveVariant", { enumerable: true, get: function () { return resolver_1.resolveVariant; } });
|
|
7
|
+
// Hash (exported for advanced use / testing)
|
|
8
|
+
var hash_1 = require("./hash");
|
|
9
|
+
Object.defineProperty(exports, "fnv1a", { enumerable: true, get: function () { return hash_1.fnv1a; } });
|
|
10
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/abtesting/index.ts"],"names":[],"mappings":";;;AAGA,gBAAgB;AAChB,uCAA4C;AAAnC,0GAAA,cAAc,OAAA;AAEvB,6CAA6C;AAC7C,+BAA+B;AAAtB,6FAAA,KAAK,OAAA"}
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A/B Test Variant Resolver
|
|
3
|
+
*
|
|
4
|
+
* Deterministic: the same entityId + testId always resolves to the same variant.
|
|
5
|
+
* Engine-agnostic: works with any compiled ruleset type.
|
|
6
|
+
*/
|
|
7
|
+
import type { ABTestConfig, ResolvedVariant } from './types';
|
|
8
|
+
/**
|
|
9
|
+
* Resolves an entity to a variant. Deterministic: same entityId + testId
|
|
10
|
+
* always returns the same variant.
|
|
11
|
+
*
|
|
12
|
+
* The hash key includes testId so the same entity can be independently
|
|
13
|
+
* bucketed across concurrent tests.
|
|
14
|
+
*
|
|
15
|
+
* @typeParam T - The compiled ruleset type
|
|
16
|
+
* @param config - The A/B test configuration with variants and weights
|
|
17
|
+
* @param entityId - The entity identifier to bucket (user ID, account ID, etc.)
|
|
18
|
+
* @returns The resolved variant with its compiled ruleset
|
|
19
|
+
*
|
|
20
|
+
* @example
|
|
21
|
+
* ```typescript
|
|
22
|
+
* const { variantId, ruleset } = resolveVariant(test, applicationId);
|
|
23
|
+
* const engine = new ScoringEngine(ruleset);
|
|
24
|
+
* const result = engine.execute(facts);
|
|
25
|
+
* ```
|
|
26
|
+
*/
|
|
27
|
+
export declare function resolveVariant<T>(config: ABTestConfig<T>, entityId: string): ResolvedVariant<T>;
|
|
28
|
+
//# sourceMappingURL=resolver.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.d.ts","sourceRoot":"","sources":["../../src/abtesting/resolver.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,YAAY,EAAE,eAAe,EAAE,MAAM,SAAS,CAAC;AA4B7D;;;;;;;;;;;;;;;;;;GAkBG;AACH,wBAAgB,cAAc,CAAC,CAAC,EAC9B,MAAM,EAAE,YAAY,CAAC,CAAC,CAAC,EACvB,QAAQ,EAAE,MAAM,GACf,eAAe,CAAC,CAAC,CAAC,CA4BpB"}
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A/B Test Variant Resolver
|
|
4
|
+
*
|
|
5
|
+
* Deterministic: the same entityId + testId always resolves to the same variant.
|
|
6
|
+
* Engine-agnostic: works with any compiled ruleset type.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
exports.resolveVariant = resolveVariant;
|
|
10
|
+
const hash_1 = require("./hash");
|
|
11
|
+
/**
|
|
12
|
+
* Validates that an ABTestConfig is well-formed.
|
|
13
|
+
* @throws if variants are empty, weights don't sum to ~1.0, or IDs are duplicated
|
|
14
|
+
*/
|
|
15
|
+
function validateConfig(config) {
|
|
16
|
+
if (!config.variants.length) {
|
|
17
|
+
throw new Error(`ABTest "${config.testId}": must have at least one variant`);
|
|
18
|
+
}
|
|
19
|
+
const total = config.variants.reduce((sum, v) => sum + v.weight, 0);
|
|
20
|
+
if (Math.abs(total - 1.0) > 0.001) {
|
|
21
|
+
throw new Error(`ABTest "${config.testId}": variant weights must sum to 1.0 (got ${total})`);
|
|
22
|
+
}
|
|
23
|
+
const ids = new Set();
|
|
24
|
+
for (const v of config.variants) {
|
|
25
|
+
if (ids.has(v.id)) {
|
|
26
|
+
throw new Error(`ABTest "${config.testId}": duplicate variant id "${v.id}"`);
|
|
27
|
+
}
|
|
28
|
+
ids.add(v.id);
|
|
29
|
+
}
|
|
30
|
+
}
|
|
31
|
+
/**
|
|
32
|
+
* Resolves an entity to a variant. Deterministic: same entityId + testId
|
|
33
|
+
* always returns the same variant.
|
|
34
|
+
*
|
|
35
|
+
* The hash key includes testId so the same entity can be independently
|
|
36
|
+
* bucketed across concurrent tests.
|
|
37
|
+
*
|
|
38
|
+
* @typeParam T - The compiled ruleset type
|
|
39
|
+
* @param config - The A/B test configuration with variants and weights
|
|
40
|
+
* @param entityId - The entity identifier to bucket (user ID, account ID, etc.)
|
|
41
|
+
* @returns The resolved variant with its compiled ruleset
|
|
42
|
+
*
|
|
43
|
+
* @example
|
|
44
|
+
* ```typescript
|
|
45
|
+
* const { variantId, ruleset } = resolveVariant(test, applicationId);
|
|
46
|
+
* const engine = new ScoringEngine(ruleset);
|
|
47
|
+
* const result = engine.execute(facts);
|
|
48
|
+
* ```
|
|
49
|
+
*/
|
|
50
|
+
function resolveVariant(config, entityId) {
|
|
51
|
+
validateConfig(config);
|
|
52
|
+
const hashValue = (0, hash_1.fnv1a)(`${config.testId}:${entityId}`);
|
|
53
|
+
let cumulative = 0;
|
|
54
|
+
for (const variant of config.variants) {
|
|
55
|
+
cumulative += variant.weight;
|
|
56
|
+
if (hashValue < cumulative) {
|
|
57
|
+
return {
|
|
58
|
+
variantId: variant.id,
|
|
59
|
+
ruleset: variant.ruleset,
|
|
60
|
+
entityId,
|
|
61
|
+
testId: config.testId,
|
|
62
|
+
hashValue,
|
|
63
|
+
};
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
// Fallback to last variant (floating-point edge case)
|
|
67
|
+
const last = config.variants[config.variants.length - 1];
|
|
68
|
+
return {
|
|
69
|
+
variantId: last.id,
|
|
70
|
+
ruleset: last.ruleset,
|
|
71
|
+
entityId,
|
|
72
|
+
testId: config.testId,
|
|
73
|
+
hashValue,
|
|
74
|
+
};
|
|
75
|
+
}
|
|
76
|
+
//# sourceMappingURL=resolver.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"resolver.js","sourceRoot":"","sources":["../../src/abtesting/resolver.ts"],"names":[],"mappings":";AAAA;;;;;GAKG;;AAiDH,wCA+BC;AA7ED,iCAA+B;AAE/B;;;GAGG;AACH,SAAS,cAAc,CAAI,MAAuB;IAChD,IAAI,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,EAAE,CAAC;QAC5B,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,CAAC,MAAM,mCAAmC,CAAC,CAAC;IAC/E,CAAC;IAED,MAAM,KAAK,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,CAAC,GAAG,EAAE,CAAC,EAAE,EAAE,CAAC,GAAG,GAAG,CAAC,CAAC,MAAM,EAAE,CAAC,CAAC,CAAC;IACpE,IAAI,IAAI,CAAC,GAAG,CAAC,KAAK,GAAG,GAAG,CAAC,GAAG,KAAK,EAAE,CAAC;QAClC,MAAM,IAAI,KAAK,CACb,WAAW,MAAM,CAAC,MAAM,2CAA2C,KAAK,GAAG,CAC5E,CAAC;IACJ,CAAC;IAED,MAAM,GAAG,GAAG,IAAI,GAAG,EAAU,CAAC;IAC9B,KAAK,MAAM,CAAC,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QAChC,IAAI,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,EAAE,CAAC;YAClB,MAAM,IAAI,KAAK,CAAC,WAAW,MAAM,CAAC,MAAM,4BAA4B,CAAC,CAAC,EAAE,GAAG,CAAC,CAAC;QAC/E,CAAC;QACD,GAAG,CAAC,GAAG,CAAC,CAAC,CAAC,EAAE,CAAC,CAAC;IAChB,CAAC;AACH,CAAC;AAED;;;;;;;;;;;;;;;;;;GAkBG;AACH,SAAgB,cAAc,CAC5B,MAAuB,EACvB,QAAgB;IAEhB,cAAc,CAAC,MAAM,CAAC,CAAC;IAEvB,MAAM,SAAS,GAAG,IAAA,YAAK,EAAC,GAAG,MAAM,CAAC,MAAM,IAAI,QAAQ,EAAE,CAAC,CAAC;IAExD,IAAI,UAAU,GAAG,CAAC,CAAC;IACnB,KAAK,MAAM,OAAO,IAAI,MAAM,CAAC,QAAQ,EAAE,CAAC;QACtC,UAAU,IAAI,OAAO,CAAC,MAAM,CAAC;QAC7B,IAAI,SAAS,GAAG,UAAU,EAAE,CAAC;YAC3B,OAAO;gBACL,SAAS,EAAE,OAAO,CAAC,EAAE;gBACrB,OAAO,EAAE,OAAO,CAAC,OAAO;gBACxB,QAAQ;gBACR,MAAM,EAAE,MAAM,CAAC,MAAM;gBACrB,SAAS;aACV,CAAC;QACJ,CAAC;IACH,CAAC;IAED,sDAAsD;IACtD,MAAM,IAAI,GAAG,MAAM,CAAC,QAAQ,CAAC,MAAM,CAAC,QAAQ,CAAC,MAAM,GAAG,CAAC,CAAC,CAAC;IACzD,OAAO;QACL,SAAS,EAAE,IAAI,CAAC,EAAE;QAClB,OAAO,EAAE,IAAI,CAAC,OAAO;QACrB,QAAQ;QACR,MAAM,EAAE,MAAM,CAAC,MAAM;QACrB,SAAS;KACV,CAAC;AACJ,CAAC"}
|
|
@@ -0,0 +1,57 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A/B Testing Types
|
|
3
|
+
*
|
|
4
|
+
* Deterministic variant resolution for ruleset A/B testing.
|
|
5
|
+
* Works with any BOE engine type — the generic parameter T
|
|
6
|
+
* carries the compiled ruleset type through resolution.
|
|
7
|
+
*/
|
|
8
|
+
/**
|
|
9
|
+
* A single variant in an A/B test configuration.
|
|
10
|
+
* @typeParam T - The compiled ruleset type (e.g., CompiledScoringRuleSet)
|
|
11
|
+
*/
|
|
12
|
+
export type ABVariant<T> = {
|
|
13
|
+
/** Unique identifier for this variant (e.g., "control", "challenger-v2") */
|
|
14
|
+
id: string;
|
|
15
|
+
/** Traffic weight as a decimal (0.0 to 1.0). All weights in a test must sum to 1.0. */
|
|
16
|
+
weight: number;
|
|
17
|
+
/** The compiled ruleset to use for entities bucketed into this variant */
|
|
18
|
+
ruleset: T;
|
|
19
|
+
};
|
|
20
|
+
/**
|
|
21
|
+
* Configuration for an A/B test over compiled rulesets.
|
|
22
|
+
* @typeParam T - The compiled ruleset type
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const test: ABTestConfig<CompiledScoringRuleSet> = {
|
|
27
|
+
* testId: 'scoring-v2-rollout',
|
|
28
|
+
* variants: [
|
|
29
|
+
* { id: 'control', weight: 0.8, ruleset: controlRuleset },
|
|
30
|
+
* { id: 'challenger', weight: 0.2, ruleset: challengerRuleset },
|
|
31
|
+
* ],
|
|
32
|
+
* };
|
|
33
|
+
* ```
|
|
34
|
+
*/
|
|
35
|
+
export type ABTestConfig<T> = {
|
|
36
|
+
/** Unique test identifier. Used as part of the hash key for isolation between concurrent tests. */
|
|
37
|
+
testId: string;
|
|
38
|
+
/** Ordered list of variants. Weights must sum to 1.0. */
|
|
39
|
+
variants: ABVariant<T>[];
|
|
40
|
+
};
|
|
41
|
+
/**
|
|
42
|
+
* Result of resolving an entity to a variant.
|
|
43
|
+
* @typeParam T - The compiled ruleset type
|
|
44
|
+
*/
|
|
45
|
+
export type ResolvedVariant<T> = {
|
|
46
|
+
/** The variant ID the entity was bucketed into */
|
|
47
|
+
variantId: string;
|
|
48
|
+
/** The compiled ruleset for this variant */
|
|
49
|
+
ruleset: T;
|
|
50
|
+
/** The entity ID that was resolved */
|
|
51
|
+
entityId: string;
|
|
52
|
+
/** The test ID this resolution belongs to */
|
|
53
|
+
testId: string;
|
|
54
|
+
/** The raw hash value in [0, 1) — useful for debugging distribution */
|
|
55
|
+
hashValue: number;
|
|
56
|
+
};
|
|
57
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/abtesting/types.ts"],"names":[],"mappings":"AAAA;;;;;;GAMG;AAEH;;;GAGG;AACH,MAAM,MAAM,SAAS,CAAC,CAAC,IAAI;IACzB,4EAA4E;IAC5E,EAAE,EAAE,MAAM,CAAC;IAEX,uFAAuF;IACvF,MAAM,EAAE,MAAM,CAAC;IAEf,0EAA0E;IAC1E,OAAO,EAAE,CAAC,CAAC;CACZ,CAAC;AAEF;;;;;;;;;;;;;;GAcG;AACH,MAAM,MAAM,YAAY,CAAC,CAAC,IAAI;IAC5B,mGAAmG;IACnG,MAAM,EAAE,MAAM,CAAC;IAEf,yDAAyD;IACzD,QAAQ,EAAE,SAAS,CAAC,CAAC,CAAC,EAAE,CAAC;CAC1B,CAAC;AAEF;;;GAGG;AACH,MAAM,MAAM,eAAe,CAAC,CAAC,IAAI;IAC/B,kDAAkD;IAClD,SAAS,EAAE,MAAM,CAAC;IAElB,4CAA4C;IAC5C,OAAO,EAAE,CAAC,CAAC;IAEX,sCAAsC;IACtC,QAAQ,EAAE,MAAM,CAAC;IAEjB,6CAA6C;IAC7C,MAAM,EAAE,MAAM,CAAC;IAEf,uEAAuE;IACvE,SAAS,EAAE,MAAM,CAAC;CACnB,CAAC"}
|
|
@@ -0,0 +1,10 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* A/B Testing Types
|
|
4
|
+
*
|
|
5
|
+
* Deterministic variant resolution for ruleset A/B testing.
|
|
6
|
+
* Works with any BOE engine type — the generic parameter T
|
|
7
|
+
* carries the compiled ruleset type through resolution.
|
|
8
|
+
*/
|
|
9
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
10
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/abtesting/types.ts"],"names":[],"mappings":";AAAA;;;;;;GAMG"}
|
|
@@ -0,0 +1,37 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ruleset Comparison Utility
|
|
3
|
+
*
|
|
4
|
+
* Scores a set of entities against two rulesets and computes per-entity
|
|
5
|
+
* deltas and aggregate tier migration statistics. Designed for preview/dry-run
|
|
6
|
+
* workflows where a draft config is compared against the active config
|
|
7
|
+
* before activation.
|
|
8
|
+
*/
|
|
9
|
+
import type { CompiledScoringRuleSet, ScoringOptions, TierDefinition } from '../engines/scoring/types';
|
|
10
|
+
import type { CompareInput, ComparisonResult } from './types';
|
|
11
|
+
/**
|
|
12
|
+
* Compare two scoring rulesets by executing both against the same inputs.
|
|
13
|
+
*
|
|
14
|
+
* For each input, creates a fresh ScoringEngine, adds the facts, and executes
|
|
15
|
+
* against both rulesets. Returns per-entity deltas and aggregate statistics
|
|
16
|
+
* including tier migration counts.
|
|
17
|
+
*
|
|
18
|
+
* @param rulesetA - The baseline ruleset (e.g. active/current config)
|
|
19
|
+
* @param rulesetB - The candidate ruleset (e.g. draft/proposed config)
|
|
20
|
+
* @param inputs - Entity fact sets to score with both rulesets
|
|
21
|
+
* @param options - Optional scoring options passed to both executions
|
|
22
|
+
* @returns Comparison result with per-entity deltas and summary statistics
|
|
23
|
+
*
|
|
24
|
+
* @example
|
|
25
|
+
* ```typescript
|
|
26
|
+
* const result = compareRulesets(activeRuleset, draftRuleset, [
|
|
27
|
+
* { entityId: 'user-1', facts: [{ type: 'User', data: { score: 750 } }] },
|
|
28
|
+
* { entityId: 'user-2', facts: [{ type: 'User', data: { score: 620 } }] },
|
|
29
|
+
* ]);
|
|
30
|
+
*
|
|
31
|
+
* console.log(`Avg score delta: ${result.summary.avgScoreDelta}`);
|
|
32
|
+
* console.log(`Tier changes: ${result.summary.tierChanges}`);
|
|
33
|
+
* console.log(`Migrations:`, result.summary.tierMigrations);
|
|
34
|
+
* ```
|
|
35
|
+
*/
|
|
36
|
+
export declare function compareRulesets<T extends TierDefinition = TierDefinition>(rulesetA: CompiledScoringRuleSet<T>, rulesetB: CompiledScoringRuleSet<T>, inputs: CompareInput[], options?: ScoringOptions): ComparisonResult<T>;
|
|
37
|
+
//# sourceMappingURL=compare.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.d.ts","sourceRoot":"","sources":["../../src/compare/compare.ts"],"names":[],"mappings":"AAAA;;;;;;;GAOG;AAGH,OAAO,KAAK,EAAE,sBAAsB,EAAE,cAAc,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AACvG,OAAO,KAAK,EAAE,YAAY,EAAe,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAE3E;;;;;;;;;;;;;;;;;;;;;;;;GAwBG;AACH,wBAAgB,eAAe,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,EACvE,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,EACnC,QAAQ,EAAE,sBAAsB,CAAC,CAAC,CAAC,EACnC,MAAM,EAAE,YAAY,EAAE,EACtB,OAAO,GAAE,cAAmB,GAC3B,gBAAgB,CAAC,CAAC,CAAC,CA8DrB"}
|
|
@@ -0,0 +1,97 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Ruleset Comparison Utility
|
|
4
|
+
*
|
|
5
|
+
* Scores a set of entities against two rulesets and computes per-entity
|
|
6
|
+
* deltas and aggregate tier migration statistics. Designed for preview/dry-run
|
|
7
|
+
* workflows where a draft config is compared against the active config
|
|
8
|
+
* before activation.
|
|
9
|
+
*/
|
|
10
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
11
|
+
exports.compareRulesets = compareRulesets;
|
|
12
|
+
const engine_1 = require("../engines/scoring/engine");
|
|
13
|
+
/**
|
|
14
|
+
* Compare two scoring rulesets by executing both against the same inputs.
|
|
15
|
+
*
|
|
16
|
+
* For each input, creates a fresh ScoringEngine, adds the facts, and executes
|
|
17
|
+
* against both rulesets. Returns per-entity deltas and aggregate statistics
|
|
18
|
+
* including tier migration counts.
|
|
19
|
+
*
|
|
20
|
+
* @param rulesetA - The baseline ruleset (e.g. active/current config)
|
|
21
|
+
* @param rulesetB - The candidate ruleset (e.g. draft/proposed config)
|
|
22
|
+
* @param inputs - Entity fact sets to score with both rulesets
|
|
23
|
+
* @param options - Optional scoring options passed to both executions
|
|
24
|
+
* @returns Comparison result with per-entity deltas and summary statistics
|
|
25
|
+
*
|
|
26
|
+
* @example
|
|
27
|
+
* ```typescript
|
|
28
|
+
* const result = compareRulesets(activeRuleset, draftRuleset, [
|
|
29
|
+
* { entityId: 'user-1', facts: [{ type: 'User', data: { score: 750 } }] },
|
|
30
|
+
* { entityId: 'user-2', facts: [{ type: 'User', data: { score: 620 } }] },
|
|
31
|
+
* ]);
|
|
32
|
+
*
|
|
33
|
+
* console.log(`Avg score delta: ${result.summary.avgScoreDelta}`);
|
|
34
|
+
* console.log(`Tier changes: ${result.summary.tierChanges}`);
|
|
35
|
+
* console.log(`Migrations:`, result.summary.tierMigrations);
|
|
36
|
+
* ```
|
|
37
|
+
*/
|
|
38
|
+
function compareRulesets(rulesetA, rulesetB, inputs, options = {}) {
|
|
39
|
+
const deltas = [];
|
|
40
|
+
for (const input of inputs) {
|
|
41
|
+
const engineA = new engine_1.ScoringEngine();
|
|
42
|
+
for (const fact of input.facts) {
|
|
43
|
+
engineA.add(fact);
|
|
44
|
+
}
|
|
45
|
+
const resultA = engineA.execute(rulesetA, options);
|
|
46
|
+
const engineB = new engine_1.ScoringEngine();
|
|
47
|
+
for (const fact of input.facts) {
|
|
48
|
+
engineB.add(fact);
|
|
49
|
+
}
|
|
50
|
+
const resultB = engineB.execute(rulesetB, options);
|
|
51
|
+
deltas.push({
|
|
52
|
+
entityId: input.entityId,
|
|
53
|
+
scoreA: resultA.totalScore,
|
|
54
|
+
scoreB: resultB.totalScore,
|
|
55
|
+
scoreDelta: resultB.totalScore - resultA.totalScore,
|
|
56
|
+
tierA: resultA.tier?.id,
|
|
57
|
+
tierB: resultB.tier?.id,
|
|
58
|
+
tierChanged: resultA.tier?.id !== resultB.tier?.id,
|
|
59
|
+
resultA,
|
|
60
|
+
resultB,
|
|
61
|
+
});
|
|
62
|
+
}
|
|
63
|
+
// Compute summary
|
|
64
|
+
const entityCount = deltas.length;
|
|
65
|
+
let sumDelta = 0;
|
|
66
|
+
let minDelta = Infinity;
|
|
67
|
+
let maxDelta = -Infinity;
|
|
68
|
+
let tierChanges = 0;
|
|
69
|
+
const tierMigrations = {};
|
|
70
|
+
for (const d of deltas) {
|
|
71
|
+
sumDelta += d.scoreDelta;
|
|
72
|
+
if (d.scoreDelta < minDelta)
|
|
73
|
+
minDelta = d.scoreDelta;
|
|
74
|
+
if (d.scoreDelta > maxDelta)
|
|
75
|
+
maxDelta = d.scoreDelta;
|
|
76
|
+
if (d.tierChanged) {
|
|
77
|
+
tierChanges++;
|
|
78
|
+
const from = d.tierA ?? '(none)';
|
|
79
|
+
const to = d.tierB ?? '(none)';
|
|
80
|
+
if (!tierMigrations[from])
|
|
81
|
+
tierMigrations[from] = {};
|
|
82
|
+
tierMigrations[from][to] = (tierMigrations[from][to] ?? 0) + 1;
|
|
83
|
+
}
|
|
84
|
+
}
|
|
85
|
+
return {
|
|
86
|
+
deltas,
|
|
87
|
+
summary: {
|
|
88
|
+
entityCount,
|
|
89
|
+
avgScoreDelta: entityCount > 0 ? sumDelta / entityCount : 0,
|
|
90
|
+
minScoreDelta: entityCount > 0 ? minDelta : 0,
|
|
91
|
+
maxScoreDelta: entityCount > 0 ? maxDelta : 0,
|
|
92
|
+
tierChanges,
|
|
93
|
+
tierMigrations,
|
|
94
|
+
},
|
|
95
|
+
};
|
|
96
|
+
}
|
|
97
|
+
//# sourceMappingURL=compare.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"compare.js","sourceRoot":"","sources":["../../src/compare/compare.ts"],"names":[],"mappings":"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"}
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../../src/compare/index.ts"],"names":[],"mappings":"AACA,YAAY,EAAE,YAAY,EAAE,WAAW,EAAE,gBAAgB,EAAE,MAAM,SAAS,CAAC;AAG3E,OAAO,EAAE,eAAe,EAAE,MAAM,WAAW,CAAC"}
|
|
@@ -0,0 +1,7 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
+
exports.compareRulesets = void 0;
|
|
4
|
+
// Core comparison
|
|
5
|
+
var compare_1 = require("./compare");
|
|
6
|
+
Object.defineProperty(exports, "compareRulesets", { enumerable: true, get: function () { return compare_1.compareRulesets; } });
|
|
7
|
+
//# sourceMappingURL=index.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../../src/compare/index.ts"],"names":[],"mappings":";;;AAGA,kBAAkB;AAClB,qCAA4C;AAAnC,0GAAA,eAAe,OAAA"}
|
|
@@ -0,0 +1,38 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare Module Types
|
|
3
|
+
*
|
|
4
|
+
* Types for scoring ruleset comparison — computing per-entity score deltas
|
|
5
|
+
* and tier migration statistics between two rulesets.
|
|
6
|
+
*/
|
|
7
|
+
import type { ScoringResult, TierDefinition } from '../engines/scoring/types';
|
|
8
|
+
import type { FactInput } from '../core';
|
|
9
|
+
/** A single test case: a set of facts to score with both rulesets */
|
|
10
|
+
export type CompareInput = {
|
|
11
|
+
entityId: string;
|
|
12
|
+
facts: FactInput[];
|
|
13
|
+
};
|
|
14
|
+
/** Per-entity diff between ruleset A and B results */
|
|
15
|
+
export type EntityDelta<T extends TierDefinition = TierDefinition> = {
|
|
16
|
+
entityId: string;
|
|
17
|
+
scoreA: number;
|
|
18
|
+
scoreB: number;
|
|
19
|
+
scoreDelta: number;
|
|
20
|
+
tierA?: string;
|
|
21
|
+
tierB?: string;
|
|
22
|
+
tierChanged: boolean;
|
|
23
|
+
resultA: ScoringResult<T>;
|
|
24
|
+
resultB: ScoringResult<T>;
|
|
25
|
+
};
|
|
26
|
+
/** Aggregate comparison result */
|
|
27
|
+
export type ComparisonResult<T extends TierDefinition = TierDefinition> = {
|
|
28
|
+
deltas: EntityDelta<T>[];
|
|
29
|
+
summary: {
|
|
30
|
+
entityCount: number;
|
|
31
|
+
avgScoreDelta: number;
|
|
32
|
+
minScoreDelta: number;
|
|
33
|
+
maxScoreDelta: number;
|
|
34
|
+
tierChanges: number;
|
|
35
|
+
tierMigrations: Record<string, Record<string, number>>;
|
|
36
|
+
};
|
|
37
|
+
};
|
|
38
|
+
//# sourceMappingURL=types.d.ts.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.d.ts","sourceRoot":"","sources":["../../src/compare/types.ts"],"names":[],"mappings":"AAAA;;;;;GAKG;AAEH,OAAO,KAAK,EAAE,aAAa,EAAE,cAAc,EAAE,MAAM,0BAA0B,CAAC;AAC9E,OAAO,KAAK,EAAE,SAAS,EAAE,MAAM,SAAS,CAAC;AAEzC,qEAAqE;AACrE,MAAM,MAAM,YAAY,GAAG;IACzB,QAAQ,EAAE,MAAM,CAAC;IACjB,KAAK,EAAE,SAAS,EAAE,CAAC;CACpB,CAAC;AAEF,sDAAsD;AACtD,MAAM,MAAM,WAAW,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI;IACnE,QAAQ,EAAE,MAAM,CAAC;IACjB,MAAM,EAAE,MAAM,CAAC;IACf,MAAM,EAAE,MAAM,CAAC;IACf,UAAU,EAAE,MAAM,CAAC;IACnB,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,KAAK,CAAC,EAAE,MAAM,CAAC;IACf,WAAW,EAAE,OAAO,CAAC;IACrB,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;IAC1B,OAAO,EAAE,aAAa,CAAC,CAAC,CAAC,CAAC;CAC3B,CAAC;AAEF,kCAAkC;AAClC,MAAM,MAAM,gBAAgB,CAAC,CAAC,SAAS,cAAc,GAAG,cAAc,IAAI;IACxE,MAAM,EAAE,WAAW,CAAC,CAAC,CAAC,EAAE,CAAC;IACzB,OAAO,EAAE;QACP,WAAW,EAAE,MAAM,CAAC;QACpB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,aAAa,EAAE,MAAM,CAAC;QACtB,WAAW,EAAE,MAAM,CAAC;QACpB,cAAc,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,MAAM,EAAE,MAAM,CAAC,CAAC,CAAC;KACxD,CAAC;CACH,CAAC"}
|
|
@@ -0,0 +1,9 @@
|
|
|
1
|
+
"use strict";
|
|
2
|
+
/**
|
|
3
|
+
* Compare Module Types
|
|
4
|
+
*
|
|
5
|
+
* Types for scoring ruleset comparison — computing per-entity score deltas
|
|
6
|
+
* and tier migration statistics between two rulesets.
|
|
7
|
+
*/
|
|
8
|
+
Object.defineProperty(exports, "__esModule", { value: true });
|
|
9
|
+
//# sourceMappingURL=types.js.map
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
{"version":3,"file":"types.js","sourceRoot":"","sources":["../../src/compare/types.ts"],"names":[],"mappings":";AAAA;;;;;GAKG"}
|
package/dist/index.d.ts
CHANGED
|
@@ -58,4 +58,6 @@ export type { ErrorContext } from './core/errors';
|
|
|
58
58
|
export { calculateDecayMultiplier, resolveDecayTimestamp } from './core/evaluation/decay';
|
|
59
59
|
export type { DecayCurve, DecayTimeUnit, DecayConfig, DecayInfo } from './core/evaluation/decay';
|
|
60
60
|
export * from './qfacts';
|
|
61
|
+
export * from './abtesting';
|
|
62
|
+
export * from './compare';
|
|
61
63
|
//# sourceMappingURL=index.d.ts.map
|
package/dist/index.d.ts.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
|
1
|
+
{"version":3,"file":"index.d.ts","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/dist/index.js
CHANGED
|
@@ -183,4 +183,12 @@ Object.defineProperty(exports, "resolveDecayTimestamp", { enumerable: true, get:
|
|
|
183
183
|
// QFacts - Probabilistic Fact Hydration
|
|
184
184
|
// ========================================
|
|
185
185
|
__exportStar(require("./qfacts"), exports);
|
|
186
|
+
// ========================================
|
|
187
|
+
// A/B Testing - Deterministic Variant Resolution
|
|
188
|
+
// ========================================
|
|
189
|
+
__exportStar(require("./abtesting"), exports);
|
|
190
|
+
// ========================================
|
|
191
|
+
// Compare - Ruleset Comparison Utility
|
|
192
|
+
// ========================================
|
|
193
|
+
__exportStar(require("./compare"), exports);
|
|
186
194
|
//# sourceMappingURL=index.js.map
|
package/dist/index.js.map
CHANGED
|
@@ -1 +1 @@
|
|
|
1
|
-
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":";AAAA;;;;;;;;;;;;;;GAcG;;;;;;;;;;;;;;;;;;AAEH,2CAA2C;AAC3C,UAAU;AACV,2CAA2C;AAE3C,iEAAiE;AACjE,6CAAkD;AAAzC,wGAAA,aAAa,OAAA;AACtB,+CAAoD;AAA3C,0GAAA,cAAc,OAAA;AACvB,6CAAkD;AAAzC,wGAAA,aAAa,OAAA;AACtB,mDAAwD;AAA/C,8GAAA,gBAAgB,OAAA;AACzB,yCAA8C;AAArC,oGAAA,WAAW,OAAA;AACpB,+CAAoD;AAA3C,0GAAA,cAAc,OAAA;AACvB,qDAAyD;AAAhD,+GAAA,gBAAgB,OAAA;AACzB,yDAA6D;AAApD,mHAAA,kBAAkB,OAAA;AAC3B,mDAAwD;AAA/C,8GAAA,gBAAgB,OAAA;AACzB,6CAAkD;AAAzC,wGAAA,aAAa,OAAA;AACtB,6CAAkD;AAAzC,wGAAA,aAAa,OAAA;AAEtB,2CAA2C;AAC3C,yBAAyB;AACzB,2CAA2C;AAE3C,yCAAuB;AAEvB,2CAA2C;AAC3C,UAAU;AACV,2CAA2C;AAE3C,mBAAmB;AACnB,6CAK2B;AAJzB,kHAAA,uBAAuB,OAAA;AACvB,gHAAA,qBAAqB,OAAA;AACrB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AAcjB,oBAAoB;AACpB,+CAI4B;AAH1B,oHAAA,wBAAwB,OAAA;AACxB,kHAAA,sBAAsB,OAAA;AACtB,4GAAA,gBAAgB,OAAA;AAalB,UAAU;AACV,6CAK2B;AAJzB,0GAAA,eAAe,OAAA;AACf,gHAAA,qBAAqB,OAAA;AACrB,0GAAA,eAAe,OAAA;AACf,8GAAA,mBAAmB,OAAA;AAmCrB,aAAa;AACb,mDAI8B;AAH5B,gHAAA,kBAAkB,OAAA;AAClB,sHAAA,wBAAwB,OAAA;AACxB,gHAAA,kBAAkB,OAAA;AAWpB,QAAQ;AACR,yCAOyB;AANvB,sGAAA,aAAa,OAAA;AACb,4GAAA,mBAAmB,OAAA;AACnB,sGAAA,aAAa,OAAA;AACb,6GAAA,oBAAoB,OAAA;AACpB,wGAAA,eAAe,OAAA;AACf,2GAAA,kBAAkB,OAAA;AAepB,WAAW;AACX,+CAO4B;AAN1B,4GAAA,gBAAgB,OAAA;AAChB,kHAAA,sBAAsB,OAAA;AACtB,+GAAA,mBAAmB,OAAA;AACnB,4GAAA,gBAAgB,OAAA;AAChB,2GAAA,eAAe,OAAA;AACf,gHAAA,oBAAoB,OAAA;AAmBtB,cAAc;AACd,qDAI+B;AAH7B,iHAAA,kBAAkB,OAAA;AAClB,uHAAA,wBAAwB,OAAA;AACxB,iHAAA,kBAAkB,OAAA;AAapB,gBAAgB;AAChB,yDAKiC;AAJ/B,qHAAA,oBAAoB,OAAA;AACpB,oHAAA,mBAAmB,OAAA;AACnB,8HAAA,6BAA6B,OAAA;AAC7B,qHAAA,oBAAoB,OAAA;AAatB,0BAA0B;AAC1B,mDAI8B;AAH5B,gHAAA,kBAAkB,OAAA;AAClB,sHAAA,wBAAwB,OAAA;AACxB,gHAAA,kBAAkB,OAAA;AAiBpB,mDAAwD;AAA/C,8GAAA,gBAAgB,OAAA;AAEzB,eAAe;AACf,6CAK2B;AAJzB,0GAAA,eAAe,OAAA;AACf,gHAAA,qBAAqB,OAAA;AACrB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AAkBjB,aAAa;AACb,mDAG8B;AAF5B,8GAAA,gBAAgB,OAAA;AAChB,sHAAA,wBAAwB,OAAA;AAW1B,gBAAgB;AAChB,2CAS0B;AARxB,sGAAA,YAAY,OAAA;AACZ,wGAAA,cAAc,OAAA;AACd,8GAAA,oBAAoB,OAAA;AACpB,wGAAA,cAAc,OAAA;AACd,yGAAA,eAAe,OAAA;AACf,qGAAA,WAAW,OAAA;AACX,0GAAA,gBAAgB,OAAA;AAChB,4GAAA,kBAAkB,OAAA;AAoBpB,2CAA2C;AAC3C,oEAAoE;AACpE,2CAA2C;AAE3C,iBAAiB;AACjB,6CAkB2B;AAjBzB,0GAAA,eAAe,OAAA;AACf,gHAAA,qBAAqB,OAAA;AACrB,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,wGAAA,aAAa,OAAA;AACb,wGAAA,aAAa,OAAA;AACb,6GAAA,kBAAkB,OAAA;AAClB,0GAAA,eAAe,OAAA;AACf,yGAAA,cAAc,OAAA;AACd,uGAAA,YAAY,OAAA;AACZ,wGAAA,aAAa,OAAA;AACb,gGAAA,KAAK,OAAA;AACL,4GAAA,iBAAiB,OAAA;AACjB,sGAAA,WAAW,OAAA;AACX,qBAAqB;AACrB,qGAAA,UAAU,OAAA;AACV,0GAAA,eAAe,OAAA;AAqDjB,2CAA2C;AAC3C,oEAAoE;AACpE,2CAA2C;AAE3C,yCAA6E;AAApE,oGAAA,OAAO,OAAA;AAAE,mGAAA,MAAM,OAAA;AAAE,wGAAA,WAAW,OAAA;AAAE,6GAAA,gBAAgB,OAAA;AAEvD,uDAAuD;AACvD,wCAAyE;AAAhE,kGAAA,QAAQ,OAAA;AAAE,0GAAA,gBAAgB,OAAA;AAAE,sGAAA,YAAY,OAAA;AAGjD,yDAAyD;AACzD,iDAGiC;AAF/B,iHAAA,wBAAwB,OAAA;AACxB,8GAAA,qBAAqB,OAAA;AASvB,2CAA2C;AAC3C,wCAAwC;AACxC,2CAA2C;AAE3C,2CAAyB"}
|
|
1
|
+
{"version":3,"file":"index.js","sourceRoot":"","sources":["../src/index.ts"],"names":[],"mappings":"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"}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@higher.archi/boe",
|
|
3
|
-
"version": "1.0.
|
|
3
|
+
"version": "1.0.16",
|
|
4
4
|
"description": "A multi-strategy rule engine supporting forward chaining, backward chaining, scoring, sequential, fuzzy logic, and Bayesian inference",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "dist/index.d.ts",
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* FNV-1a 32-bit hash. Pure JS, zero dependencies, browser-safe.
|
|
3
|
+
* Returns a float in [0.0, 1.0) for deterministic bucket resolution.
|
|
4
|
+
*/
|
|
5
|
+
export function fnv1a(input: string): number {
|
|
6
|
+
let hash = 0x811c9dc5; // FNV offset basis
|
|
7
|
+
for (let i = 0; i < input.length; i++) {
|
|
8
|
+
hash ^= input.charCodeAt(i);
|
|
9
|
+
hash = Math.imul(hash, 0x01000193); // FNV prime
|
|
10
|
+
}
|
|
11
|
+
return (hash >>> 0) / 0x100000000;
|
|
12
|
+
}
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A/B Test Variant Resolver
|
|
3
|
+
*
|
|
4
|
+
* Deterministic: the same entityId + testId always resolves to the same variant.
|
|
5
|
+
* Engine-agnostic: works with any compiled ruleset type.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ABTestConfig, ResolvedVariant } from './types';
|
|
9
|
+
import { fnv1a } from './hash';
|
|
10
|
+
|
|
11
|
+
/**
|
|
12
|
+
* Validates that an ABTestConfig is well-formed.
|
|
13
|
+
* @throws if variants are empty, weights don't sum to ~1.0, or IDs are duplicated
|
|
14
|
+
*/
|
|
15
|
+
function validateConfig<T>(config: ABTestConfig<T>): void {
|
|
16
|
+
if (!config.variants.length) {
|
|
17
|
+
throw new Error(`ABTest "${config.testId}": must have at least one variant`);
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
const total = config.variants.reduce((sum, v) => sum + v.weight, 0);
|
|
21
|
+
if (Math.abs(total - 1.0) > 0.001) {
|
|
22
|
+
throw new Error(
|
|
23
|
+
`ABTest "${config.testId}": variant weights must sum to 1.0 (got ${total})`
|
|
24
|
+
);
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
const ids = new Set<string>();
|
|
28
|
+
for (const v of config.variants) {
|
|
29
|
+
if (ids.has(v.id)) {
|
|
30
|
+
throw new Error(`ABTest "${config.testId}": duplicate variant id "${v.id}"`);
|
|
31
|
+
}
|
|
32
|
+
ids.add(v.id);
|
|
33
|
+
}
|
|
34
|
+
}
|
|
35
|
+
|
|
36
|
+
/**
|
|
37
|
+
* Resolves an entity to a variant. Deterministic: same entityId + testId
|
|
38
|
+
* always returns the same variant.
|
|
39
|
+
*
|
|
40
|
+
* The hash key includes testId so the same entity can be independently
|
|
41
|
+
* bucketed across concurrent tests.
|
|
42
|
+
*
|
|
43
|
+
* @typeParam T - The compiled ruleset type
|
|
44
|
+
* @param config - The A/B test configuration with variants and weights
|
|
45
|
+
* @param entityId - The entity identifier to bucket (user ID, account ID, etc.)
|
|
46
|
+
* @returns The resolved variant with its compiled ruleset
|
|
47
|
+
*
|
|
48
|
+
* @example
|
|
49
|
+
* ```typescript
|
|
50
|
+
* const { variantId, ruleset } = resolveVariant(test, applicationId);
|
|
51
|
+
* const engine = new ScoringEngine(ruleset);
|
|
52
|
+
* const result = engine.execute(facts);
|
|
53
|
+
* ```
|
|
54
|
+
*/
|
|
55
|
+
export function resolveVariant<T>(
|
|
56
|
+
config: ABTestConfig<T>,
|
|
57
|
+
entityId: string
|
|
58
|
+
): ResolvedVariant<T> {
|
|
59
|
+
validateConfig(config);
|
|
60
|
+
|
|
61
|
+
const hashValue = fnv1a(`${config.testId}:${entityId}`);
|
|
62
|
+
|
|
63
|
+
let cumulative = 0;
|
|
64
|
+
for (const variant of config.variants) {
|
|
65
|
+
cumulative += variant.weight;
|
|
66
|
+
if (hashValue < cumulative) {
|
|
67
|
+
return {
|
|
68
|
+
variantId: variant.id,
|
|
69
|
+
ruleset: variant.ruleset,
|
|
70
|
+
entityId,
|
|
71
|
+
testId: config.testId,
|
|
72
|
+
hashValue,
|
|
73
|
+
};
|
|
74
|
+
}
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
// Fallback to last variant (floating-point edge case)
|
|
78
|
+
const last = config.variants[config.variants.length - 1];
|
|
79
|
+
return {
|
|
80
|
+
variantId: last.id,
|
|
81
|
+
ruleset: last.ruleset,
|
|
82
|
+
entityId,
|
|
83
|
+
testId: config.testId,
|
|
84
|
+
hashValue,
|
|
85
|
+
};
|
|
86
|
+
}
|
|
@@ -0,0 +1,66 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* A/B Testing Types
|
|
3
|
+
*
|
|
4
|
+
* Deterministic variant resolution for ruleset A/B testing.
|
|
5
|
+
* Works with any BOE engine type — the generic parameter T
|
|
6
|
+
* carries the compiled ruleset type through resolution.
|
|
7
|
+
*/
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* A single variant in an A/B test configuration.
|
|
11
|
+
* @typeParam T - The compiled ruleset type (e.g., CompiledScoringRuleSet)
|
|
12
|
+
*/
|
|
13
|
+
export type ABVariant<T> = {
|
|
14
|
+
/** Unique identifier for this variant (e.g., "control", "challenger-v2") */
|
|
15
|
+
id: string;
|
|
16
|
+
|
|
17
|
+
/** Traffic weight as a decimal (0.0 to 1.0). All weights in a test must sum to 1.0. */
|
|
18
|
+
weight: number;
|
|
19
|
+
|
|
20
|
+
/** The compiled ruleset to use for entities bucketed into this variant */
|
|
21
|
+
ruleset: T;
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* Configuration for an A/B test over compiled rulesets.
|
|
26
|
+
* @typeParam T - The compiled ruleset type
|
|
27
|
+
*
|
|
28
|
+
* @example
|
|
29
|
+
* ```typescript
|
|
30
|
+
* const test: ABTestConfig<CompiledScoringRuleSet> = {
|
|
31
|
+
* testId: 'scoring-v2-rollout',
|
|
32
|
+
* variants: [
|
|
33
|
+
* { id: 'control', weight: 0.8, ruleset: controlRuleset },
|
|
34
|
+
* { id: 'challenger', weight: 0.2, ruleset: challengerRuleset },
|
|
35
|
+
* ],
|
|
36
|
+
* };
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export type ABTestConfig<T> = {
|
|
40
|
+
/** Unique test identifier. Used as part of the hash key for isolation between concurrent tests. */
|
|
41
|
+
testId: string;
|
|
42
|
+
|
|
43
|
+
/** Ordered list of variants. Weights must sum to 1.0. */
|
|
44
|
+
variants: ABVariant<T>[];
|
|
45
|
+
};
|
|
46
|
+
|
|
47
|
+
/**
|
|
48
|
+
* Result of resolving an entity to a variant.
|
|
49
|
+
* @typeParam T - The compiled ruleset type
|
|
50
|
+
*/
|
|
51
|
+
export type ResolvedVariant<T> = {
|
|
52
|
+
/** The variant ID the entity was bucketed into */
|
|
53
|
+
variantId: string;
|
|
54
|
+
|
|
55
|
+
/** The compiled ruleset for this variant */
|
|
56
|
+
ruleset: T;
|
|
57
|
+
|
|
58
|
+
/** The entity ID that was resolved */
|
|
59
|
+
entityId: string;
|
|
60
|
+
|
|
61
|
+
/** The test ID this resolution belongs to */
|
|
62
|
+
testId: string;
|
|
63
|
+
|
|
64
|
+
/** The raw hash value in [0, 1) — useful for debugging distribution */
|
|
65
|
+
hashValue: number;
|
|
66
|
+
};
|
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Ruleset Comparison Utility
|
|
3
|
+
*
|
|
4
|
+
* Scores a set of entities against two rulesets and computes per-entity
|
|
5
|
+
* deltas and aggregate tier migration statistics. Designed for preview/dry-run
|
|
6
|
+
* workflows where a draft config is compared against the active config
|
|
7
|
+
* before activation.
|
|
8
|
+
*/
|
|
9
|
+
|
|
10
|
+
import { ScoringEngine } from '../engines/scoring/engine';
|
|
11
|
+
import type { CompiledScoringRuleSet, ScoringOptions, TierDefinition } from '../engines/scoring/types';
|
|
12
|
+
import type { CompareInput, EntityDelta, ComparisonResult } from './types';
|
|
13
|
+
|
|
14
|
+
/**
|
|
15
|
+
* Compare two scoring rulesets by executing both against the same inputs.
|
|
16
|
+
*
|
|
17
|
+
* For each input, creates a fresh ScoringEngine, adds the facts, and executes
|
|
18
|
+
* against both rulesets. Returns per-entity deltas and aggregate statistics
|
|
19
|
+
* including tier migration counts.
|
|
20
|
+
*
|
|
21
|
+
* @param rulesetA - The baseline ruleset (e.g. active/current config)
|
|
22
|
+
* @param rulesetB - The candidate ruleset (e.g. draft/proposed config)
|
|
23
|
+
* @param inputs - Entity fact sets to score with both rulesets
|
|
24
|
+
* @param options - Optional scoring options passed to both executions
|
|
25
|
+
* @returns Comparison result with per-entity deltas and summary statistics
|
|
26
|
+
*
|
|
27
|
+
* @example
|
|
28
|
+
* ```typescript
|
|
29
|
+
* const result = compareRulesets(activeRuleset, draftRuleset, [
|
|
30
|
+
* { entityId: 'user-1', facts: [{ type: 'User', data: { score: 750 } }] },
|
|
31
|
+
* { entityId: 'user-2', facts: [{ type: 'User', data: { score: 620 } }] },
|
|
32
|
+
* ]);
|
|
33
|
+
*
|
|
34
|
+
* console.log(`Avg score delta: ${result.summary.avgScoreDelta}`);
|
|
35
|
+
* console.log(`Tier changes: ${result.summary.tierChanges}`);
|
|
36
|
+
* console.log(`Migrations:`, result.summary.tierMigrations);
|
|
37
|
+
* ```
|
|
38
|
+
*/
|
|
39
|
+
export function compareRulesets<T extends TierDefinition = TierDefinition>(
|
|
40
|
+
rulesetA: CompiledScoringRuleSet<T>,
|
|
41
|
+
rulesetB: CompiledScoringRuleSet<T>,
|
|
42
|
+
inputs: CompareInput[],
|
|
43
|
+
options: ScoringOptions = {}
|
|
44
|
+
): ComparisonResult<T> {
|
|
45
|
+
const deltas: EntityDelta<T>[] = [];
|
|
46
|
+
|
|
47
|
+
for (const input of inputs) {
|
|
48
|
+
const engineA = new ScoringEngine();
|
|
49
|
+
for (const fact of input.facts) {
|
|
50
|
+
engineA.add(fact);
|
|
51
|
+
}
|
|
52
|
+
const resultA = engineA.execute(rulesetA, options);
|
|
53
|
+
|
|
54
|
+
const engineB = new ScoringEngine();
|
|
55
|
+
for (const fact of input.facts) {
|
|
56
|
+
engineB.add(fact);
|
|
57
|
+
}
|
|
58
|
+
const resultB = engineB.execute(rulesetB, options);
|
|
59
|
+
|
|
60
|
+
deltas.push({
|
|
61
|
+
entityId: input.entityId,
|
|
62
|
+
scoreA: resultA.totalScore,
|
|
63
|
+
scoreB: resultB.totalScore,
|
|
64
|
+
scoreDelta: resultB.totalScore - resultA.totalScore,
|
|
65
|
+
tierA: resultA.tier?.id,
|
|
66
|
+
tierB: resultB.tier?.id,
|
|
67
|
+
tierChanged: resultA.tier?.id !== resultB.tier?.id,
|
|
68
|
+
resultA,
|
|
69
|
+
resultB,
|
|
70
|
+
});
|
|
71
|
+
}
|
|
72
|
+
|
|
73
|
+
// Compute summary
|
|
74
|
+
const entityCount = deltas.length;
|
|
75
|
+
let sumDelta = 0;
|
|
76
|
+
let minDelta = Infinity;
|
|
77
|
+
let maxDelta = -Infinity;
|
|
78
|
+
let tierChanges = 0;
|
|
79
|
+
const tierMigrations: Record<string, Record<string, number>> = {};
|
|
80
|
+
|
|
81
|
+
for (const d of deltas) {
|
|
82
|
+
sumDelta += d.scoreDelta;
|
|
83
|
+
if (d.scoreDelta < minDelta) minDelta = d.scoreDelta;
|
|
84
|
+
if (d.scoreDelta > maxDelta) maxDelta = d.scoreDelta;
|
|
85
|
+
|
|
86
|
+
if (d.tierChanged) {
|
|
87
|
+
tierChanges++;
|
|
88
|
+
const from = d.tierA ?? '(none)';
|
|
89
|
+
const to = d.tierB ?? '(none)';
|
|
90
|
+
if (!tierMigrations[from]) tierMigrations[from] = {};
|
|
91
|
+
tierMigrations[from][to] = (tierMigrations[from][to] ?? 0) + 1;
|
|
92
|
+
}
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
return {
|
|
96
|
+
deltas,
|
|
97
|
+
summary: {
|
|
98
|
+
entityCount,
|
|
99
|
+
avgScoreDelta: entityCount > 0 ? sumDelta / entityCount : 0,
|
|
100
|
+
minScoreDelta: entityCount > 0 ? minDelta : 0,
|
|
101
|
+
maxScoreDelta: entityCount > 0 ? maxDelta : 0,
|
|
102
|
+
tierChanges,
|
|
103
|
+
tierMigrations,
|
|
104
|
+
},
|
|
105
|
+
};
|
|
106
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Compare Module Types
|
|
3
|
+
*
|
|
4
|
+
* Types for scoring ruleset comparison — computing per-entity score deltas
|
|
5
|
+
* and tier migration statistics between two rulesets.
|
|
6
|
+
*/
|
|
7
|
+
|
|
8
|
+
import type { ScoringResult, TierDefinition } from '../engines/scoring/types';
|
|
9
|
+
import type { FactInput } from '../core';
|
|
10
|
+
|
|
11
|
+
/** A single test case: a set of facts to score with both rulesets */
|
|
12
|
+
export type CompareInput = {
|
|
13
|
+
entityId: string;
|
|
14
|
+
facts: FactInput[];
|
|
15
|
+
};
|
|
16
|
+
|
|
17
|
+
/** Per-entity diff between ruleset A and B results */
|
|
18
|
+
export type EntityDelta<T extends TierDefinition = TierDefinition> = {
|
|
19
|
+
entityId: string;
|
|
20
|
+
scoreA: number;
|
|
21
|
+
scoreB: number;
|
|
22
|
+
scoreDelta: number;
|
|
23
|
+
tierA?: string;
|
|
24
|
+
tierB?: string;
|
|
25
|
+
tierChanged: boolean;
|
|
26
|
+
resultA: ScoringResult<T>;
|
|
27
|
+
resultB: ScoringResult<T>;
|
|
28
|
+
};
|
|
29
|
+
|
|
30
|
+
/** Aggregate comparison result */
|
|
31
|
+
export type ComparisonResult<T extends TierDefinition = TierDefinition> = {
|
|
32
|
+
deltas: EntityDelta<T>[];
|
|
33
|
+
summary: {
|
|
34
|
+
entityCount: number;
|
|
35
|
+
avgScoreDelta: number;
|
|
36
|
+
minScoreDelta: number;
|
|
37
|
+
maxScoreDelta: number;
|
|
38
|
+
tierChanges: number;
|
|
39
|
+
tierMigrations: Record<string, Record<string, number>>;
|
|
40
|
+
};
|
|
41
|
+
};
|
package/src/index.ts
CHANGED
|
@@ -406,3 +406,15 @@ export type {
|
|
|
406
406
|
// ========================================
|
|
407
407
|
|
|
408
408
|
export * from './qfacts';
|
|
409
|
+
|
|
410
|
+
// ========================================
|
|
411
|
+
// A/B Testing - Deterministic Variant Resolution
|
|
412
|
+
// ========================================
|
|
413
|
+
|
|
414
|
+
export * from './abtesting';
|
|
415
|
+
|
|
416
|
+
// ========================================
|
|
417
|
+
// Compare - Ruleset Comparison Utility
|
|
418
|
+
// ========================================
|
|
419
|
+
|
|
420
|
+
export * from './compare';
|