oracular 0.15.0 → 0.16.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 +6 -0
- package/README.md +1 -1
- package/dist/index.cjs +14 -2
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +22 -5
- package/dist/index.d.ts +22 -5
- package/dist/index.js +14 -2
- package/dist/index.js.map +1 -1
- package/package.json +1 -1
package/dist/index.d.cts
CHANGED
|
@@ -474,6 +474,15 @@ interface RecordedDecision {
|
|
|
474
474
|
}[];
|
|
475
475
|
/** Effective weight of every declared signal, in config order. */
|
|
476
476
|
weights: Record<string, number>;
|
|
477
|
+
/**
|
|
478
|
+
* The prediction just before the event, on the record of this intent's own outcome event, when
|
|
479
|
+
* {@link createRecord} was given the state before it: the number `resetOnOutcome` wipes, which
|
|
480
|
+
* `p` above no longer shows. Absent on every other record.
|
|
481
|
+
*/
|
|
482
|
+
before?: {
|
|
483
|
+
p: number;
|
|
484
|
+
lower: number;
|
|
485
|
+
};
|
|
477
486
|
}
|
|
478
487
|
/**
|
|
479
488
|
* One line of a recorder log: an event the app observed and what the engine decided right after
|
|
@@ -821,25 +830,33 @@ declare function markRevealed(state: EngineState, intent: string, at: number): E
|
|
|
821
830
|
* and the effective weight of every signal. Pure: the install id comes from the app, never from
|
|
822
831
|
* here. See the Shadow mode guide for the whole workflow.
|
|
823
832
|
*
|
|
833
|
+
* On the record of an intent's own outcome event, `p` is what the engine decides right after it,
|
|
834
|
+
* which with `resetOnOutcome` is the prior: the estimate the outcome confirmed is gone. Pass the
|
|
835
|
+
* state from before the event as `previous` and that intent's decision gains `before`, its `p`
|
|
836
|
+
* and `lower` at `event.at` on that state. Replay never reads it; people reading the log do.
|
|
837
|
+
*
|
|
824
838
|
* @param state - The state after `observe(state, event)`. Not mutated.
|
|
825
839
|
* @param event - The event just observed. Only `name` and `at` are recorded.
|
|
826
840
|
* @param opts - `installId` (generated and stored by the app), `mode` (`'shadow'` while
|
|
827
841
|
* reveals are computed but not shown, `'live'` once they are), and `top` (default 3).
|
|
842
|
+
* @param previous - The state before `observe(state, event)`, optional. Only read for the
|
|
843
|
+
* intents whose outcome the event is.
|
|
828
844
|
* @returns A plain, JSON-serializable {@link EventRecord}.
|
|
829
|
-
* @throws `TypeError` when `state` is not an engine state, the event is
|
|
830
|
-
* not an object, has an unknown key, an empty `installId` or a `mode`
|
|
831
|
-
* `'shadow'` or `'live'`.
|
|
845
|
+
* @throws `TypeError` when `state` or `previous` is not an engine state, the event is
|
|
846
|
+
* malformed, `opts` is not an object, has an unknown key, an empty `installId` or a `mode`
|
|
847
|
+
* other than `'shadow'` or `'live'`.
|
|
832
848
|
* @throws `RangeError` when `opts.top` is negative or fractional.
|
|
833
849
|
* @example
|
|
834
850
|
* ```ts
|
|
835
851
|
* const recording = { installId: localStorage.getItem('oracular.install')!, mode: 'shadow' } as const;
|
|
836
852
|
*
|
|
837
853
|
* const event = { name: 'pricing.viewed', at: Date.now() };
|
|
854
|
+
* const previous = state;
|
|
838
855
|
* state = observe(state, event);
|
|
839
|
-
* appendLine(JSON.stringify(createRecord(state, event, recording)));
|
|
856
|
+
* appendLine(JSON.stringify(createRecord(state, event, recording, previous)));
|
|
840
857
|
* ```
|
|
841
858
|
*/
|
|
842
|
-
declare function createRecord(state: EngineState, event: IntentEvent, opts: RecordOptions): EventRecord;
|
|
859
|
+
declare function createRecord(state: EngineState, event: IntentEvent, opts: RecordOptions, previous?: EngineState): EventRecord;
|
|
843
860
|
/**
|
|
844
861
|
* A recorder record for the end of an episode. Write one whenever the app calls
|
|
845
862
|
* {@link endEpisode}, with the same `at`, so that replay can end the episode at the same moment
|
package/dist/index.d.ts
CHANGED
|
@@ -474,6 +474,15 @@ interface RecordedDecision {
|
|
|
474
474
|
}[];
|
|
475
475
|
/** Effective weight of every declared signal, in config order. */
|
|
476
476
|
weights: Record<string, number>;
|
|
477
|
+
/**
|
|
478
|
+
* The prediction just before the event, on the record of this intent's own outcome event, when
|
|
479
|
+
* {@link createRecord} was given the state before it: the number `resetOnOutcome` wipes, which
|
|
480
|
+
* `p` above no longer shows. Absent on every other record.
|
|
481
|
+
*/
|
|
482
|
+
before?: {
|
|
483
|
+
p: number;
|
|
484
|
+
lower: number;
|
|
485
|
+
};
|
|
477
486
|
}
|
|
478
487
|
/**
|
|
479
488
|
* One line of a recorder log: an event the app observed and what the engine decided right after
|
|
@@ -821,25 +830,33 @@ declare function markRevealed(state: EngineState, intent: string, at: number): E
|
|
|
821
830
|
* and the effective weight of every signal. Pure: the install id comes from the app, never from
|
|
822
831
|
* here. See the Shadow mode guide for the whole workflow.
|
|
823
832
|
*
|
|
833
|
+
* On the record of an intent's own outcome event, `p` is what the engine decides right after it,
|
|
834
|
+
* which with `resetOnOutcome` is the prior: the estimate the outcome confirmed is gone. Pass the
|
|
835
|
+
* state from before the event as `previous` and that intent's decision gains `before`, its `p`
|
|
836
|
+
* and `lower` at `event.at` on that state. Replay never reads it; people reading the log do.
|
|
837
|
+
*
|
|
824
838
|
* @param state - The state after `observe(state, event)`. Not mutated.
|
|
825
839
|
* @param event - The event just observed. Only `name` and `at` are recorded.
|
|
826
840
|
* @param opts - `installId` (generated and stored by the app), `mode` (`'shadow'` while
|
|
827
841
|
* reveals are computed but not shown, `'live'` once they are), and `top` (default 3).
|
|
842
|
+
* @param previous - The state before `observe(state, event)`, optional. Only read for the
|
|
843
|
+
* intents whose outcome the event is.
|
|
828
844
|
* @returns A plain, JSON-serializable {@link EventRecord}.
|
|
829
|
-
* @throws `TypeError` when `state` is not an engine state, the event is
|
|
830
|
-
* not an object, has an unknown key, an empty `installId` or a `mode`
|
|
831
|
-
* `'shadow'` or `'live'`.
|
|
845
|
+
* @throws `TypeError` when `state` or `previous` is not an engine state, the event is
|
|
846
|
+
* malformed, `opts` is not an object, has an unknown key, an empty `installId` or a `mode`
|
|
847
|
+
* other than `'shadow'` or `'live'`.
|
|
832
848
|
* @throws `RangeError` when `opts.top` is negative or fractional.
|
|
833
849
|
* @example
|
|
834
850
|
* ```ts
|
|
835
851
|
* const recording = { installId: localStorage.getItem('oracular.install')!, mode: 'shadow' } as const;
|
|
836
852
|
*
|
|
837
853
|
* const event = { name: 'pricing.viewed', at: Date.now() };
|
|
854
|
+
* const previous = state;
|
|
838
855
|
* state = observe(state, event);
|
|
839
|
-
* appendLine(JSON.stringify(createRecord(state, event, recording)));
|
|
856
|
+
* appendLine(JSON.stringify(createRecord(state, event, recording, previous)));
|
|
840
857
|
* ```
|
|
841
858
|
*/
|
|
842
|
-
declare function createRecord(state: EngineState, event: IntentEvent, opts: RecordOptions): EventRecord;
|
|
859
|
+
declare function createRecord(state: EngineState, event: IntentEvent, opts: RecordOptions, previous?: EngineState): EventRecord;
|
|
843
860
|
/**
|
|
844
861
|
* A recorder record for the end of an episode. Write one whenever the app calls
|
|
845
862
|
* {@link endEpisode}, with the same `at`, so that replay can end the episode at the same moment
|
package/dist/index.js
CHANGED
|
@@ -897,10 +897,11 @@ function normalizeRecordOptions(value) {
|
|
|
897
897
|
}
|
|
898
898
|
return { installId, mode, top };
|
|
899
899
|
}
|
|
900
|
-
function createRecord(state, event, opts) {
|
|
900
|
+
function createRecord(state, event, opts, previous) {
|
|
901
901
|
const s = requireState(state);
|
|
902
902
|
const { name, at } = validateEvent(event);
|
|
903
903
|
const { installId, mode, top } = normalizeRecordOptions(opts);
|
|
904
|
+
const prev = previous === void 0 ? void 0 : requireState(previous);
|
|
904
905
|
const learning = resolveLearning(s.config);
|
|
905
906
|
const decisions = {};
|
|
906
907
|
for (const [intentName, intent] of Object.entries(s.config.intents)) {
|
|
@@ -912,7 +913,7 @@ function createRecord(state, event, opts) {
|
|
|
912
913
|
for (const signal of Object.keys(intent.signals)) {
|
|
913
914
|
weights[signal] = rows.find((row) => row.signal === signal).effectiveWeight;
|
|
914
915
|
}
|
|
915
|
-
|
|
916
|
+
const decision = {
|
|
916
917
|
p: prediction.p,
|
|
917
918
|
lower: prediction.lower,
|
|
918
919
|
shouldReveal: decideReveal(prediction, reveal),
|
|
@@ -920,6 +921,17 @@ function createRecord(state, event, opts) {
|
|
|
920
921
|
top: rows.filter((row) => row.contribution !== 0).slice(0, top).map((row) => ({ signal: row.signal, impact: row.impact })),
|
|
921
922
|
weights
|
|
922
923
|
};
|
|
924
|
+
const prevIntent = prev === void 0 ? void 0 : getOwn(prev.config.intents, intentName);
|
|
925
|
+
if (prev !== void 0 && prevIntent !== void 0 && intent.outcome === name) {
|
|
926
|
+
const was = buildPrediction(
|
|
927
|
+
assessIntent(prev, intentName, prevIntent, resolveLearning(prev.config), at),
|
|
928
|
+
intentName,
|
|
929
|
+
resolveReveal(prev.config, prevIntent, void 0),
|
|
930
|
+
at
|
|
931
|
+
);
|
|
932
|
+
decision.before = { p: was.p, lower: was.lower };
|
|
933
|
+
}
|
|
934
|
+
decisions[intentName] = decision;
|
|
923
935
|
}
|
|
924
936
|
return { v: RECORD_VERSION, kind: "event", installId, mode, event: { name, at }, decisions };
|
|
925
937
|
}
|