oracular 0.15.0 → 0.17.0
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/CHANGELOG.md +12 -0
- package/README.md +8 -10
- package/dist/index.cjs +14 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +42 -7
- package/dist/index.d.ts +42 -7
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
- package/schema/config.schema.json +1 -1
package/CHANGELOG.md
CHANGED
|
@@ -1,5 +1,17 @@
|
|
|
1
1
|
# Changelog
|
|
2
2
|
|
|
3
|
+
## 0.17.0
|
|
4
|
+
|
|
5
|
+
### Minor Changes
|
|
6
|
+
|
|
7
|
+
- [#75](https://github.com/Yuribenjamin/oracular/pull/75) [`bdce2bd`](https://github.com/Yuribenjamin/oracular/commit/bdce2bdcffceab6465b08cf1ae1361ec47b8a1bf) Thanks [@Yuribenjamin](https://github.com/Yuribenjamin)! - **Six lift measures are deprecated.** 0.9 added `learning.lift` with seven association measures. A sweep of all seven on the clickstream case study found none that beats the default, `'normalized-difference'`, on either split, and 1.0 commits every value in the schema for the whole of 1.x. So `'absolute-difference'`, `'relative-lift'`, `'risk-ratio'`, `'log-lift'`, `'odds-ratio'` and `'association-rule'` are marked `@deprecated` in `LiftType`, named in the JSON Schema's description, and removed in 1.0. Nothing changes in this release: every value keeps working exactly as it did, a config that sets one keeps loading, and the type checker shows the mark. `learning.lift` itself stays, accepting the default, so that a measure can come back as a minor when a real integration shows it should.
|
|
8
|
+
|
|
9
|
+
## 0.16.0
|
|
10
|
+
|
|
11
|
+
### Minor Changes
|
|
12
|
+
|
|
13
|
+
- [#74](https://github.com/Yuribenjamin/oracular/pull/74) [`7e6b6fc`](https://github.com/Yuribenjamin/oracular/commit/7e6b6fc3885dac0167b92661e188e3f779a73a45) Thanks [@Yuribenjamin](https://github.com/Yuribenjamin)! - **The outcome record keeps the estimate the reset wipes.** `createRecord` takes the state after `observe`, so on the record of an intent's own outcome event `p` is what the engine decides right after it, with `resetOnOutcome` the prior (0.150 where it had been 0.506 one event earlier). Replay recomputes everything from the events and never reads it, but people read the log, and the number that matters most on an outcome line was gone. `createRecord(state, event, options, previous)` takes the state from before the event as an optional fourth argument; the decision of each intent whose outcome the event is then gains `before`, its `p` and `lower` at `event.at` on that state. Every other record is byte for byte what it was. `RECORD_VERSION` stays 1: a new optional field does not change it, readers ignore fields they do not know, and `replay` and `tune` never read decisions, so a log with `before` replays on every release that reads version 1. Found by the second production integration.
|
|
14
|
+
|
|
3
15
|
## 0.15.0
|
|
4
16
|
|
|
5
17
|
### Minor Changes
|
package/README.md
CHANGED
|
@@ -204,15 +204,13 @@ effective = (k × w₀ + nEff × learned) / (k + nEff) k = priorEpiso
|
|
|
204
204
|
|
|
205
205
|
`w₀` is your initial weight, `gain` (default 2) caps the learned magnitude at `gain × |w₀|` for every lift option, and `priorEpisodes` (default 10) is how much contrast it takes for the user's own history to count as much as your guess. A signal's strength and your confidence in it are separate knobs. A signal is judged against the sessions in which it did *not* appear, so for an outcome that happens in 3% of sessions, a signal followed by it 30% of the time is strong evidence, not weak. And learning needs contrast: a signal that has never been absent has no counterfactual, so its weight stands until there are sessions without it. Nothing drifts after a single outcome.
|
|
206
206
|
|
|
207
|
-
The formula above is the default **normalized difference**. `learning.lift`
|
|
208
|
-
|
|
209
|
-
`
|
|
210
|
-
|
|
211
|
-
are
|
|
212
|
-
|
|
213
|
-
`
|
|
214
|
-
keep their scale. Learning uses `gain × |w₀| × score`; `oracular tune` uses the same score for
|
|
215
|
-
weights and intervals and reports raw lift separately when it differs from the score.
|
|
207
|
+
The formula above is the default **normalized difference**. `learning.lift` can name another
|
|
208
|
+
association measure (`'relative-lift'`, `'risk-ratio'`, `'log-lift'`, `'odds-ratio'`,
|
|
209
|
+
`'absolute-difference'`, `'association-rule'`), each converted to a signed evidence score in
|
|
210
|
+
[−1, 1] with independence at zero, as [Episodes & learning](https://yuribenjamin.github.io/oracular/guide/concepts/episodes-and-learning#choosing-a-lift-formula)
|
|
211
|
+
lists. **Those six are deprecated and removed in 1.0:** a sweep of all seven on the clickstream
|
|
212
|
+
case study found none that beats the default on either split. They keep working until then; omit
|
|
213
|
+
`learning.lift`, or set the default, and nothing changes.
|
|
216
214
|
|
|
217
215
|
Learned counts never decay unless you set `learning.halfLife`.
|
|
218
216
|
|
|
@@ -264,7 +262,7 @@ evaluate(state, intent: string, at: number, opts?): { prediction, reveal, explan
|
|
|
264
262
|
inspect(state, at: number, opts?): { at, intents: [{ intent, prediction, reveal, explanation }] } // evaluate for every intent
|
|
265
263
|
evidenceToReveal(intentConfig, opts?): number // pseudo-counts at which shouldReveal first becomes true
|
|
266
264
|
episodeClosed(prev, next): boolean // did an episode close between two states?
|
|
267
|
-
createRecord(state, event, { installId, mode }): EventRecord // one recorder line: the event and what was decided
|
|
265
|
+
createRecord(state, event, { installId, mode }, previous?): EventRecord // one recorder line: the event and what was decided; with the state before the event, the outcome's record keeps the estimate the reset wipes
|
|
268
266
|
createEndRecord(at, { installId, mode }): EndRecord // one recorder line: the episode ended
|
|
269
267
|
serialize(state): string
|
|
270
268
|
deserialize(json: string, opts?: { config?: EngineConfig; migrate?: Migrate }): EngineState
|
package/dist/index.cjs
CHANGED
|
@@ -899,10 +899,11 @@ function normalizeRecordOptions(value) {
|
|
|
899
899
|
}
|
|
900
900
|
return { installId, mode, top };
|
|
901
901
|
}
|
|
902
|
-
function createRecord(state, event, opts) {
|
|
902
|
+
function createRecord(state, event, opts, previous) {
|
|
903
903
|
const s = requireState(state);
|
|
904
904
|
const { name, at } = validateEvent(event);
|
|
905
905
|
const { installId, mode, top } = normalizeRecordOptions(opts);
|
|
906
|
+
const prev = previous === void 0 ? void 0 : requireState(previous);
|
|
906
907
|
const learning = resolveLearning(s.config);
|
|
907
908
|
const decisions = {};
|
|
908
909
|
for (const [intentName, intent] of Object.entries(s.config.intents)) {
|
|
@@ -914,7 +915,7 @@ function createRecord(state, event, opts) {
|
|
|
914
915
|
for (const signal of Object.keys(intent.signals)) {
|
|
915
916
|
weights[signal] = rows.find((row) => row.signal === signal).effectiveWeight;
|
|
916
917
|
}
|
|
917
|
-
|
|
918
|
+
const decision = {
|
|
918
919
|
p: prediction.p,
|
|
919
920
|
lower: prediction.lower,
|
|
920
921
|
shouldReveal: decideReveal(prediction, reveal),
|
|
@@ -922,6 +923,17 @@ function createRecord(state, event, opts) {
|
|
|
922
923
|
top: rows.filter((row) => row.contribution !== 0).slice(0, top).map((row) => ({ signal: row.signal, impact: row.impact })),
|
|
923
924
|
weights
|
|
924
925
|
};
|
|
926
|
+
const prevIntent = prev === void 0 ? void 0 : getOwn(prev.config.intents, intentName);
|
|
927
|
+
if (prev !== void 0 && prevIntent !== void 0 && intent.outcome === name) {
|
|
928
|
+
const was = buildPrediction(
|
|
929
|
+
assessIntent(prev, intentName, prevIntent, resolveLearning(prev.config), at),
|
|
930
|
+
intentName,
|
|
931
|
+
resolveReveal(prev.config, prevIntent, void 0),
|
|
932
|
+
at
|
|
933
|
+
);
|
|
934
|
+
decision.before = { p: was.p, lower: was.lower };
|
|
935
|
+
}
|
|
936
|
+
decisions[intentName] = decision;
|
|
925
937
|
}
|
|
926
938
|
return { v: RECORD_VERSION, kind: "event", installId, mode, event: { name, at }, decisions };
|
|
927
939
|
}
|