@lmzhen/dsh-evolution-replay 0.3.66 → 0.3.68
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/lib/index.js +63 -5
- package/lib/types/index.d.ts +37 -3
- package/package.json +5 -3
package/lib/index.js
CHANGED
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import z from "@deepseek-ai/schemastery";
|
|
2
|
-
import { clampedNumber } from "@lmzhen/dsh-evolution-core";
|
|
2
|
+
import { clampedNumber, evolutionHome, evolutionIoAdapter } from "@lmzhen/dsh-evolution-core";
|
|
3
|
+
import { loadActivity } from "@lmzhen/dsh-evolution-activity";
|
|
3
4
|
//#region lib/types/index.js
|
|
4
5
|
/**
|
|
5
6
|
* Replay/A-B evaluation for evolution plans.
|
|
@@ -8,9 +9,16 @@ import { clampedNumber } from "@lmzhen/dsh-evolution-core";
|
|
|
8
9
|
* driver records every `evolution/plan-applied` process event (payload v2,
|
|
9
10
|
* with sessionId) into an in-memory leaderboard and exposes `/evolution
|
|
10
11
|
* replay` (via the `/evolution` command family) for comparison, so a human can
|
|
11
|
-
* A/B review policy/prompt changes against real plan outcomes.
|
|
12
|
-
*
|
|
13
|
-
*
|
|
12
|
+
* A/B review policy/prompt changes against real plan outcomes.
|
|
13
|
+
*
|
|
14
|
+
* V24-13 (v24): the in-memory leaderboard is BACKFILLED at mount from the
|
|
15
|
+
* `evolution-activity` sidecar (`$DSH_HOME/evolution/activity.json`) — the
|
|
16
|
+
* header previously claimed "durability across restarts is the
|
|
17
|
+
* evolution-activity store's job" while nothing ever read the sidecar, so a
|
|
18
|
+
* restart emptied `/evolution replay` and the persisted history sat unread.
|
|
19
|
+
* Records are fed in sidecar order (oldest first); the driver's `maxPlans`
|
|
20
|
+
* window keeps the newest. Best-effort: a missing/unreadable sidecar starts
|
|
21
|
+
* the leaderboard empty, exactly as before.
|
|
14
22
|
* @module @lmzhen/dsh-evolution-replay
|
|
15
23
|
*/
|
|
16
24
|
const DEFAULT_WEIGHTS = {
|
|
@@ -80,9 +88,26 @@ function comparePlans(plans, weights = DEFAULT_WEIGHTS) {
|
|
|
80
88
|
report: scored.map(({ plan, score }) => `${plan.policyId}: ${score.toFixed(1)} (${plan.acceptedOps} accepted, ${plan.rejectedOps} rejected${(plan.executionFailures ?? 0) > 0 ? `, ${plan.executionFailures} failed${plan.executionError !== void 0 ? `: ${plan.executionError}` : ""}` : ""})`).join("\n")
|
|
81
89
|
};
|
|
82
90
|
}
|
|
83
|
-
var EvolutionReplayDriver = class {
|
|
91
|
+
var EvolutionReplayDriver = class EvolutionReplayDriver {
|
|
84
92
|
plans = [];
|
|
85
93
|
maxPlans;
|
|
94
|
+
/** V25-01 (v25): backfill-once latch — see {@link EvolutionReplayDriver.backfill}. */
|
|
95
|
+
backfilled = false;
|
|
96
|
+
/** V26-05 (v25): plan ids recorded live BEFORE the backfill settled. A
|
|
97
|
+
* plan-applied landing inside the one-shot `loadActivity` read window is
|
|
98
|
+
* recorded live AND persisted into the sidecar the backfill is reading —
|
|
99
|
+
* `backfill()` drops those ids so the event is not counted twice.
|
|
100
|
+
*
|
|
101
|
+
* V27 INS-05: the set is BOUNDED. `backfill()` is the only place that cleared
|
|
102
|
+
* it, so a sidecar load that never succeeded (the inject callback's catch
|
|
103
|
+
* warns and does not retry until the io dependency is replaced) let every
|
|
104
|
+
* live plan id accumulate for the process lifetime. Past
|
|
105
|
+
* PRE_BACKFILL_ID_CAP the oldest ids are dropped: the dedupe window is then
|
|
106
|
+
* finite, and a dropped duplicate can at worst repeat an entry inside the
|
|
107
|
+
* `maxPlans` window instead of growing memory without bound. */
|
|
108
|
+
preBackfillIds = /* @__PURE__ */ new Set();
|
|
109
|
+
/** Upper bound on {@link EvolutionReplayDriver.preBackfillIds} (V27 INS-05). */
|
|
110
|
+
static PRE_BACKFILL_ID_CAP = 4096;
|
|
86
111
|
weights;
|
|
87
112
|
constructor(config = {}, warn = () => {}) {
|
|
88
113
|
const maxPlans = clampedNumber(config.maxPlans ?? 50, 50, { min: 1 });
|
|
@@ -96,6 +121,14 @@ var EvolutionReplayDriver = class {
|
|
|
96
121
|
}
|
|
97
122
|
}
|
|
98
123
|
record(plan) {
|
|
124
|
+
if (!this.backfilled) {
|
|
125
|
+
this.preBackfillIds.add(plan.planId);
|
|
126
|
+
while (this.preBackfillIds.size > EvolutionReplayDriver.PRE_BACKFILL_ID_CAP) {
|
|
127
|
+
const oldest = this.preBackfillIds.keys().next().value;
|
|
128
|
+
if (oldest === void 0) break;
|
|
129
|
+
this.preBackfillIds.delete(oldest);
|
|
130
|
+
}
|
|
131
|
+
}
|
|
99
132
|
const count = (value) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : 0;
|
|
100
133
|
const countOr = (value, fallback) => typeof value === "number" && Number.isFinite(value) && value >= 0 ? value : fallback;
|
|
101
134
|
const memoryApplied = count(plan.memoryApplied);
|
|
@@ -114,6 +147,22 @@ var EvolutionReplayDriver = class {
|
|
|
114
147
|
if (this.plans.length > this.maxPlans) this.plans.shift();
|
|
115
148
|
}
|
|
116
149
|
/**
|
|
150
|
+
* V25-01 (v25): backfill the leaderboard from the activity sidecar, ONCE
|
|
151
|
+
* per driver instance. The inject callback that loads the sidecar re-runs
|
|
152
|
+
* whenever the `evolutionIo` dependency is replaced (cordis derived-fiber
|
|
153
|
+
* reload — plugin restart / HMR) while THIS driver survives at the apply
|
|
154
|
+
* scope, so the dedupe guard must live here, not in the callback: without
|
|
155
|
+
* it, every io reload doubled the leaderboard entries.
|
|
156
|
+
* @param items - activity records in sidecar order (oldest first).
|
|
157
|
+
*/
|
|
158
|
+
backfill(items) {
|
|
159
|
+
if (this.backfilled) return;
|
|
160
|
+
this.backfilled = true;
|
|
161
|
+
const fresh = items.filter((item) => !this.preBackfillIds.has(item.planId));
|
|
162
|
+
this.preBackfillIds.clear();
|
|
163
|
+
for (const item of fresh) this.record(item);
|
|
164
|
+
}
|
|
165
|
+
/**
|
|
117
166
|
* F-11: test-support API — no production consumer reads the raw
|
|
118
167
|
* plan list (the leaderboard path goes through `compare()`); tests use this
|
|
119
168
|
* as a read/inspection window. Kept by declaration (same posture as the
|
|
@@ -137,6 +186,15 @@ function apply(ctx, rawConfig = {}) {
|
|
|
137
186
|
ctx.on("evolution/plan-applied", (event) => {
|
|
138
187
|
driver.record(event);
|
|
139
188
|
});
|
|
189
|
+
ctx.inject(["evolutionIo"], (ioCtx) => {
|
|
190
|
+
const ioRegistry = ioCtx.evolutionIo;
|
|
191
|
+
const io = evolutionIoAdapter(() => ioRegistry.provider());
|
|
192
|
+
loadActivity(evolutionHome(), io).then((items) => {
|
|
193
|
+
driver.backfill(items);
|
|
194
|
+
}).catch((error) => {
|
|
195
|
+
ioCtx.logger.warn(`evolution-replay: activity sidecar backfill skipped (${error instanceof Error ? error.message : String(error)})`);
|
|
196
|
+
});
|
|
197
|
+
});
|
|
140
198
|
}
|
|
141
199
|
//#endregion
|
|
142
200
|
export { Config, DEFAULT_WEIGHTS, EvolutionReplayDriver, apply, clampReplayWeights, comparePlans, name };
|
package/lib/types/index.d.ts
CHANGED
|
@@ -5,9 +5,16 @@
|
|
|
5
5
|
* driver records every `evolution/plan-applied` process event (payload v2,
|
|
6
6
|
* with sessionId) into an in-memory leaderboard and exposes `/evolution
|
|
7
7
|
* replay` (via the `/evolution` command family) for comparison, so a human can
|
|
8
|
-
* A/B review policy/prompt changes against real plan outcomes.
|
|
9
|
-
*
|
|
10
|
-
*
|
|
8
|
+
* A/B review policy/prompt changes against real plan outcomes.
|
|
9
|
+
*
|
|
10
|
+
* V24-13 (v24): the in-memory leaderboard is BACKFILLED at mount from the
|
|
11
|
+
* `evolution-activity` sidecar (`$DSH_HOME/evolution/activity.json`) — the
|
|
12
|
+
* header previously claimed "durability across restarts is the
|
|
13
|
+
* evolution-activity store's job" while nothing ever read the sidecar, so a
|
|
14
|
+
* restart emptied `/evolution replay` and the persisted history sat unread.
|
|
15
|
+
* Records are fed in sidecar order (oldest first); the driver's `maxPlans`
|
|
16
|
+
* window keeps the newest. Best-effort: a missing/unreadable sidecar starts
|
|
17
|
+
* the leaderboard empty, exactly as before.
|
|
11
18
|
* @module @lmzhen/dsh-evolution-replay
|
|
12
19
|
*/
|
|
13
20
|
import type { Context } from '@deepseek-ai/cordis';
|
|
@@ -61,9 +68,36 @@ declare module '@deepseek-ai/cordis' {
|
|
|
61
68
|
export declare class EvolutionReplayDriver {
|
|
62
69
|
private readonly plans;
|
|
63
70
|
private readonly maxPlans;
|
|
71
|
+
/** V25-01 (v25): backfill-once latch — see {@link EvolutionReplayDriver.backfill}. */
|
|
72
|
+
private backfilled;
|
|
73
|
+
/** V26-05 (v25): plan ids recorded live BEFORE the backfill settled. A
|
|
74
|
+
* plan-applied landing inside the one-shot `loadActivity` read window is
|
|
75
|
+
* recorded live AND persisted into the sidecar the backfill is reading —
|
|
76
|
+
* `backfill()` drops those ids so the event is not counted twice.
|
|
77
|
+
*
|
|
78
|
+
* V27 INS-05: the set is BOUNDED. `backfill()` is the only place that cleared
|
|
79
|
+
* it, so a sidecar load that never succeeded (the inject callback's catch
|
|
80
|
+
* warns and does not retry until the io dependency is replaced) let every
|
|
81
|
+
* live plan id accumulate for the process lifetime. Past
|
|
82
|
+
* PRE_BACKFILL_ID_CAP the oldest ids are dropped: the dedupe window is then
|
|
83
|
+
* finite, and a dropped duplicate can at worst repeat an entry inside the
|
|
84
|
+
* `maxPlans` window instead of growing memory without bound. */
|
|
85
|
+
private readonly preBackfillIds;
|
|
86
|
+
/** Upper bound on {@link EvolutionReplayDriver.preBackfillIds} (V27 INS-05). */
|
|
87
|
+
private static readonly PRE_BACKFILL_ID_CAP;
|
|
64
88
|
private readonly weights;
|
|
65
89
|
constructor(config?: Config, warn?: (message: string) => void);
|
|
66
90
|
record(plan: EvolutionPlanAppliedEvent): void;
|
|
91
|
+
/**
|
|
92
|
+
* V25-01 (v25): backfill the leaderboard from the activity sidecar, ONCE
|
|
93
|
+
* per driver instance. The inject callback that loads the sidecar re-runs
|
|
94
|
+
* whenever the `evolutionIo` dependency is replaced (cordis derived-fiber
|
|
95
|
+
* reload — plugin restart / HMR) while THIS driver survives at the apply
|
|
96
|
+
* scope, so the dedupe guard must live here, not in the callback: without
|
|
97
|
+
* it, every io reload doubled the leaderboard entries.
|
|
98
|
+
* @param items - activity records in sidecar order (oldest first).
|
|
99
|
+
*/
|
|
100
|
+
backfill(items: ReadonlyArray<import('@lmzhen/dsh-evolution-activity').EvolutionActivityRecord>): void;
|
|
67
101
|
/**
|
|
68
102
|
* F-11: test-support API — no production consumer reads the raw
|
|
69
103
|
* plan list (the leaderboard path goes through `compare()`); tests use this
|
package/package.json
CHANGED
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@lmzhen/dsh-evolution-replay",
|
|
3
3
|
"description": "Replay/A-B evaluation primitives for evolution plans (community build)",
|
|
4
|
-
"version": "0.3.
|
|
4
|
+
"version": "0.3.68",
|
|
5
5
|
"publishConfig": {
|
|
6
6
|
"access": "public"
|
|
7
7
|
},
|
|
@@ -31,7 +31,8 @@
|
|
|
31
31
|
"license": "MIT",
|
|
32
32
|
"dependencies": {
|
|
33
33
|
"@deepseek-ai/schemastery": "^3.18.1",
|
|
34
|
-
"@lmzhen/dsh-evolution-
|
|
34
|
+
"@lmzhen/dsh-evolution-activity": "^0.3.68",
|
|
35
|
+
"@lmzhen/dsh-evolution-core": "^0.3.68"
|
|
35
36
|
},
|
|
36
37
|
"peerDependencies": {
|
|
37
38
|
"@deepseek-ai/cordis": "^4.0.1",
|
|
@@ -39,6 +40,7 @@
|
|
|
39
40
|
},
|
|
40
41
|
"devDependencies": {
|
|
41
42
|
"@deepseek-ai/dsh-invariants": "^0.1.1-rc.2",
|
|
42
|
-
"@lmzhen/dsh-evolution-
|
|
43
|
+
"@lmzhen/dsh-evolution-activity": "^0.3.68",
|
|
44
|
+
"@lmzhen/dsh-evolution-core": "^0.3.68"
|
|
43
45
|
}
|
|
44
46
|
}
|