@higher.archi/boe 1.0.7 → 1.0.8
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/core/evaluation/decay.d.ts +60 -0
- package/dist/core/evaluation/decay.d.ts.map +1 -0
- package/dist/core/evaluation/decay.js +111 -0
- package/dist/core/evaluation/decay.js.map +1 -0
- package/dist/core/evaluation/index.d.ts +2 -0
- package/dist/core/evaluation/index.d.ts.map +1 -1
- package/dist/core/evaluation/index.js +5 -1
- package/dist/core/evaluation/index.js.map +1 -1
- package/dist/engines/bayesian/compiler.d.ts.map +1 -1
- package/dist/engines/bayesian/compiler.js +14 -2
- package/dist/engines/bayesian/compiler.js.map +1 -1
- package/dist/engines/bayesian/strategy.d.ts.map +1 -1
- package/dist/engines/bayesian/strategy.js +41 -4
- package/dist/engines/bayesian/strategy.js.map +1 -1
- package/dist/engines/bayesian/types.d.ts +16 -0
- package/dist/engines/bayesian/types.d.ts.map +1 -1
- package/dist/engines/bayesian/types.js.map +1 -1
- package/dist/engines/defeasible/compiler.d.ts.map +1 -1
- package/dist/engines/defeasible/compiler.js +16 -2
- package/dist/engines/defeasible/compiler.js.map +1 -1
- package/dist/engines/defeasible/strategy.d.ts.map +1 -1
- package/dist/engines/defeasible/strategy.js +47 -2
- package/dist/engines/defeasible/strategy.js.map +1 -1
- package/dist/engines/defeasible/types.d.ts +29 -1
- package/dist/engines/defeasible/types.d.ts.map +1 -1
- package/dist/engines/defeasible/types.js.map +1 -1
- package/dist/engines/scoring/compiler.d.ts.map +1 -1
- package/dist/engines/scoring/compiler.js +17 -4
- package/dist/engines/scoring/compiler.js.map +1 -1
- package/dist/engines/scoring/strategy.d.ts.map +1 -1
- package/dist/engines/scoring/strategy.js +49 -0
- package/dist/engines/scoring/strategy.js.map +1 -1
- package/dist/engines/scoring/types.d.ts +16 -0
- package/dist/engines/scoring/types.d.ts.map +1 -1
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +5 -1
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/src/core/evaluation/decay.ts +165 -0
- package/src/core/evaluation/index.ts +13 -0
- package/src/engines/bayesian/compiler.ts +15 -2
- package/src/engines/bayesian/strategy.ts +54 -4
- package/src/engines/bayesian/types.ts +17 -0
- package/src/engines/defeasible/compiler.ts +17 -2
- package/src/engines/defeasible/strategy.ts +62 -2
- package/src/engines/defeasible/types.ts +33 -0
- package/src/engines/scoring/compiler.ts +18 -4
- package/src/engines/scoring/strategy.ts +63 -0
- package/src/engines/scoring/types.ts +17 -0
- package/src/index.ts +12 -0
|
@@ -77,7 +77,18 @@ function compileInputParameter(input: any) {
|
|
|
77
77
|
// ========================================
|
|
78
78
|
|
|
79
79
|
function compileScoringAction(action: any): CompiledScoringAction {
|
|
80
|
-
const { score, weight, normalize, reason, nullHandling } = action;
|
|
80
|
+
const { score, weight, normalize, reason, nullHandling, decay } = action;
|
|
81
|
+
|
|
82
|
+
// Compile decay timestamp if it's an expression
|
|
83
|
+
let compiledDecay: CompiledScoringAction['decay'] | undefined;
|
|
84
|
+
if (decay) {
|
|
85
|
+
compiledDecay = {
|
|
86
|
+
timestamp: Array.isArray(decay.timestamp)
|
|
87
|
+
? compileCondition(decay.timestamp as Condition)
|
|
88
|
+
: decay.timestamp,
|
|
89
|
+
config: decay.config
|
|
90
|
+
};
|
|
91
|
+
}
|
|
81
92
|
|
|
82
93
|
// Handle expression scores
|
|
83
94
|
if (Array.isArray(score)) {
|
|
@@ -87,7 +98,8 @@ function compileScoringAction(action: any): CompiledScoringAction {
|
|
|
87
98
|
weight,
|
|
88
99
|
normalize,
|
|
89
100
|
reason,
|
|
90
|
-
nullHandling
|
|
101
|
+
nullHandling,
|
|
102
|
+
decay: compiledDecay
|
|
91
103
|
};
|
|
92
104
|
}
|
|
93
105
|
|
|
@@ -98,7 +110,8 @@ function compileScoringAction(action: any): CompiledScoringAction {
|
|
|
98
110
|
weight,
|
|
99
111
|
normalize,
|
|
100
112
|
reason,
|
|
101
|
-
nullHandling
|
|
113
|
+
nullHandling,
|
|
114
|
+
decay: compiledDecay
|
|
102
115
|
};
|
|
103
116
|
}
|
|
104
117
|
|
|
@@ -200,7 +213,8 @@ export function compileScoringRuleSet(ruleSet: ScoringRuleSet): CompiledScoringR
|
|
|
200
213
|
tiers: ruleSet.config?.tiers,
|
|
201
214
|
categories,
|
|
202
215
|
overrides,
|
|
203
|
-
nullHandling: ruleSet.config?.nullHandling
|
|
216
|
+
nullHandling: ruleSet.config?.nullHandling,
|
|
217
|
+
decay: ruleSet.config?.decay
|
|
204
218
|
};
|
|
205
219
|
|
|
206
220
|
return {
|
|
@@ -10,6 +10,12 @@ import {
|
|
|
10
10
|
evaluateCondition
|
|
11
11
|
} from '../../core';
|
|
12
12
|
import { IWorkingMemory } from '../../core/memory';
|
|
13
|
+
import {
|
|
14
|
+
calculateDecayMultiplier,
|
|
15
|
+
resolveDecayTimestamp,
|
|
16
|
+
DecayConfig,
|
|
17
|
+
DecayInfo
|
|
18
|
+
} from '../../core/evaluation/decay';
|
|
13
19
|
|
|
14
20
|
import {
|
|
15
21
|
CompiledScoringRuleSet,
|
|
@@ -49,8 +55,12 @@ export class ScoringStrategy {
|
|
|
49
55
|
const weights: Record<string, number> = {};
|
|
50
56
|
const fired: string[] = [];
|
|
51
57
|
const rawScores: Record<string, number[]> = {}; // Track raw scores for adaptive
|
|
58
|
+
const decayInfoMap: Record<string, DecayInfo> = {};
|
|
59
|
+
const decayMultipliers: Record<string, number> = {};
|
|
52
60
|
|
|
53
61
|
// Phase 1: Calculate base scores
|
|
62
|
+
const now = new Date();
|
|
63
|
+
|
|
54
64
|
for (const rule of ruleSet.rules) {
|
|
55
65
|
const activations = match(rule, wm);
|
|
56
66
|
|
|
@@ -74,6 +84,34 @@ export class ScoringStrategy {
|
|
|
74
84
|
throw new Error(`Invalid score type: ${typeof ruleAction.score}`);
|
|
75
85
|
}
|
|
76
86
|
|
|
87
|
+
// Apply temporal decay if configured
|
|
88
|
+
if (!decayMultipliers[rule.id]) {
|
|
89
|
+
const ruleDecay = ruleAction.decay;
|
|
90
|
+
const engineDecay = config.decay;
|
|
91
|
+
|
|
92
|
+
if (ruleDecay || engineDecay) {
|
|
93
|
+
// Resolve timestamp: rule-level overrides engine-level
|
|
94
|
+
const tsRef = ruleDecay?.timestamp ?? engineDecay?.timestamp;
|
|
95
|
+
let dataTimestamp: Date | null = null;
|
|
96
|
+
|
|
97
|
+
if (typeof tsRef === 'string') {
|
|
98
|
+
dataTimestamp = resolveDecayTimestamp(tsRef, activation.facts);
|
|
99
|
+
}
|
|
100
|
+
|
|
101
|
+
if (dataTimestamp) {
|
|
102
|
+
// Merge decay config: rule-level overrides engine-level
|
|
103
|
+
const decayConfig: DecayConfig = {
|
|
104
|
+
...(engineDecay?.config ?? { curve: 'exponential', timeUnit: 'days' }),
|
|
105
|
+
...ruleDecay?.config
|
|
106
|
+
} as DecayConfig;
|
|
107
|
+
|
|
108
|
+
const info = calculateDecayMultiplier(dataTimestamp, now, decayConfig);
|
|
109
|
+
decayInfoMap[rule.id] = info;
|
|
110
|
+
decayMultipliers[rule.id] = info.multiplier;
|
|
111
|
+
}
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
|
|
77
115
|
// Store raw scores for adaptive strategy
|
|
78
116
|
if (!rawScores[rule.id]) {
|
|
79
117
|
rawScores[rule.id] = [];
|
|
@@ -139,6 +177,20 @@ export class ScoringStrategy {
|
|
|
139
177
|
contributions[rule.id] = accumulatedScore;
|
|
140
178
|
}
|
|
141
179
|
|
|
180
|
+
// Phase 2.25: Apply decay multipliers to contributions
|
|
181
|
+
const hasDecay = Object.keys(decayInfoMap).length > 0;
|
|
182
|
+
let contributionsBeforeDecay: Record<string, number> | undefined;
|
|
183
|
+
|
|
184
|
+
if (hasDecay) {
|
|
185
|
+
contributionsBeforeDecay = {};
|
|
186
|
+
for (const ruleId in contributions) {
|
|
187
|
+
contributionsBeforeDecay[ruleId] = contributions[ruleId];
|
|
188
|
+
if (decayMultipliers[ruleId] !== undefined) {
|
|
189
|
+
contributions[ruleId] *= decayMultipliers[ruleId];
|
|
190
|
+
}
|
|
191
|
+
}
|
|
192
|
+
}
|
|
193
|
+
|
|
142
194
|
// Phase 2.5: Null handling — treat unfired 'zero' rules as fired with score 0
|
|
143
195
|
for (const rule of ruleSet.rules) {
|
|
144
196
|
if (!fired.includes(rule.id)) {
|
|
@@ -501,6 +553,12 @@ export class ScoringStrategy {
|
|
|
501
553
|
|
|
502
554
|
const executionTimeMs = Math.round((performance.now() - startTime) * 100) / 100;
|
|
503
555
|
|
|
556
|
+
// Compute totalScoreBeforeDecay if decay was applied
|
|
557
|
+
let totalScoreBeforeDecay: number | undefined;
|
|
558
|
+
if (hasDecay && contributionsBeforeDecay) {
|
|
559
|
+
totalScoreBeforeDecay = baseScore + Object.values(contributionsBeforeDecay).reduce((sum, s) => sum + s, 0);
|
|
560
|
+
}
|
|
561
|
+
|
|
504
562
|
return {
|
|
505
563
|
totalScore,
|
|
506
564
|
confidence,
|
|
@@ -511,6 +569,11 @@ export class ScoringStrategy {
|
|
|
511
569
|
tier,
|
|
512
570
|
categoryBreakdown,
|
|
513
571
|
override: overrideResult,
|
|
572
|
+
...(hasDecay ? {
|
|
573
|
+
totalScoreBeforeDecay,
|
|
574
|
+
contributionsBeforeDecay,
|
|
575
|
+
decayInfo: decayInfoMap
|
|
576
|
+
} : {}),
|
|
514
577
|
executionTimeMs
|
|
515
578
|
};
|
|
516
579
|
}
|
|
@@ -13,6 +13,8 @@ import {
|
|
|
13
13
|
BindingContext
|
|
14
14
|
} from '../../core';
|
|
15
15
|
|
|
16
|
+
import { DecayConfig, DecayInfo } from '../../core/evaluation/decay';
|
|
17
|
+
|
|
16
18
|
// ========================================
|
|
17
19
|
// Scoring Method Types
|
|
18
20
|
// ========================================
|
|
@@ -268,6 +270,10 @@ export type ScoringAction = {
|
|
|
268
270
|
normalize?: { min: number; max: number }; // Optional normalization bounds
|
|
269
271
|
reason?: string; // Optional human-readable explanation for the score
|
|
270
272
|
nullHandling?: NullHandling; // How to handle if this signal is missing (default: 'exclude')
|
|
273
|
+
decay?: {
|
|
274
|
+
timestamp: string | Expression; // e.g. '$inspection.date'
|
|
275
|
+
config?: Partial<DecayConfig>; // overrides engine-level defaults
|
|
276
|
+
};
|
|
271
277
|
};
|
|
272
278
|
|
|
273
279
|
/**
|
|
@@ -300,6 +306,10 @@ export type ScoringConfig = {
|
|
|
300
306
|
categories?: ScoringCategory[]; // Optional category grouping for two-level weight hierarchy
|
|
301
307
|
overrides?: OverrideDefinition[]; // Optional post-scoring overrides evaluated in order (first match wins)
|
|
302
308
|
nullHandling?: NullHandling; // Default null handling for all rules (default: 'exclude')
|
|
309
|
+
decay?: {
|
|
310
|
+
timestamp?: string | Expression; // default timestamp path for all rules
|
|
311
|
+
config: DecayConfig; // engine-level decay config
|
|
312
|
+
};
|
|
303
313
|
};
|
|
304
314
|
|
|
305
315
|
// ========================================
|
|
@@ -316,6 +326,10 @@ export type CompiledScoringAction = {
|
|
|
316
326
|
normalize?: { min: number; max: number };
|
|
317
327
|
reason?: string; // Preserved from source for reporting
|
|
318
328
|
nullHandling?: NullHandling; // Preserved from source
|
|
329
|
+
decay?: {
|
|
330
|
+
timestamp: string | CompiledCondition; // string = $path, CompiledCondition = expression
|
|
331
|
+
config?: Partial<DecayConfig>;
|
|
332
|
+
};
|
|
319
333
|
};
|
|
320
334
|
|
|
321
335
|
/**
|
|
@@ -384,5 +398,8 @@ export type ScoringResult = {
|
|
|
384
398
|
tier?: ScoringTierMatch; // Matched tier (if tiers configured)
|
|
385
399
|
categoryBreakdown?: CategoryResult[]; // Per-category breakdown (if categories configured)
|
|
386
400
|
override?: OverrideResult; // Override that fired (if any)
|
|
401
|
+
totalScoreBeforeDecay?: number; // undecayed total for comparison
|
|
402
|
+
contributionsBeforeDecay?: Record<string, number>; // undecayed per-rule contributions
|
|
403
|
+
decayInfo?: Record<string, DecayInfo>; // per-rule decay audit
|
|
387
404
|
executionTimeMs: number; // Processing time in milliseconds
|
|
388
405
|
};
|
package/src/index.ts
CHANGED
|
@@ -375,6 +375,18 @@ export type {
|
|
|
375
375
|
|
|
376
376
|
export { soundex, nysiis, caverphone2, cosineSimilarity } from './functions';
|
|
377
377
|
|
|
378
|
+
// Temporal Decay (re-exported from core for convenience)
|
|
379
|
+
export {
|
|
380
|
+
calculateDecayMultiplier,
|
|
381
|
+
resolveDecayTimestamp
|
|
382
|
+
} from './core/evaluation/decay';
|
|
383
|
+
export type {
|
|
384
|
+
DecayCurve,
|
|
385
|
+
DecayTimeUnit,
|
|
386
|
+
DecayConfig,
|
|
387
|
+
DecayInfo
|
|
388
|
+
} from './core/evaluation/decay';
|
|
389
|
+
|
|
378
390
|
// ========================================
|
|
379
391
|
// QFacts - Probabilistic Fact Hydration
|
|
380
392
|
// ========================================
|