mirror of
https://github.com/openclaw/openclaw.git
synced 2026-04-24 15:41:40 +00:00
feat(memory-core): add dreaming verbose logging
This commit is contained in:
@@ -1,4 +1,4 @@
|
||||
1dc927cd4be5a0ef6e17958a53ceb6df155107ca8100cdb4d417003483f17990 config-baseline.json
|
||||
b1a181b667568b5860a80945837d544fdec4f946fba34e871936ce0cd3eb689b config-baseline.core.json
|
||||
ea126fa950fe65c4f7be68c92ff06f3a2256dfe1e770c8643c93b132528bb217 config-baseline.json
|
||||
587eb0dde83443aa49d743010d224cdc2d7bb6c9d21c3c4effae44f5a06913c5 config-baseline.core.json
|
||||
3c999707b167138de34f6255e3488b99e404c5132d3fc5879a1fa12d815c31f5 config-baseline.channel.json
|
||||
fcf32a00815f392ceda9195b8c2af82ae7e88da333feaacee9296f7d5921e73f config-baseline.plugin.json
|
||||
31a7d5fd79cb3591a6469c13e5ab42010f8c54e9c1f74da0aad4a1629969085d config-baseline.plugin.json
|
||||
|
||||
@@ -133,3 +133,4 @@ Notes:
|
||||
- If effectively active memory remote API key fields are configured as SecretRefs, the command resolves those values from the active gateway snapshot. If gateway is unavailable, the command fails fast.
|
||||
- Gateway version skew note: this command path requires a gateway that supports `secrets.resolve`; older gateways return an unknown-method error.
|
||||
- Dreaming cadence defaults to each mode's preset schedule. Override cadence with `plugins.entries.memory-core.config.dreaming.frequency` as a cron expression (for example `0 3 * * *`) and fine-tune with `timezone`, `limit`, `minScore`, `minRecallCount`, and `minUniqueQueries`.
|
||||
- Set `plugins.entries.memory-core.config.dreaming.verboseLogging` to `true` to emit per-run candidate and apply details into the normal gateway logs while tuning the feature.
|
||||
|
||||
@@ -82,6 +82,7 @@ You can still tune behavior with explicit overrides such as:
|
||||
- `dreaming.minScore`
|
||||
- `dreaming.minRecallCount`
|
||||
- `dreaming.minUniqueQueries`
|
||||
- `dreaming.verboseLogging`
|
||||
|
||||
## Configure
|
||||
|
||||
|
||||
@@ -2649,6 +2649,7 @@ See [Local Models](/gateway/local-models). TL;DR: run a large local model via LM
|
||||
- `minScore`: minimum weighted score threshold for promotion.
|
||||
- `minRecallCount`: minimum recall count threshold.
|
||||
- `minUniqueQueries`: minimum distinct query count threshold.
|
||||
- `verboseLogging`: emit detailed per-run dreaming logs into the normal gateway log stream.
|
||||
- Enabled Claude bundle plugins can also contribute embedded Pi defaults from `settings.json`; OpenClaw applies those as sanitized agent settings, not as raw OpenClaw config patches.
|
||||
- `plugins.slots.memory`: pick the active memory plugin id, or `"none"` to disable memory plugins.
|
||||
- `plugins.slots.contextEngine`: pick the active context engine plugin id; defaults to `"legacy"` unless you install and select another engine.
|
||||
|
||||
@@ -379,15 +379,16 @@ Dreaming is configured under `plugins.entries.memory-core.config.dreaming`,
|
||||
not under `agents.defaults.memorySearch`. For conceptual details and chat
|
||||
commands, see [Dreaming](/concepts/memory-dreaming).
|
||||
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------ | -------- | -------------- | ----------------------------------------- |
|
||||
| `mode` | `string` | `"off"` | Preset: `off`, `core`, `rem`, or `deep` |
|
||||
| `cron` | `string` | preset default | Cron expression override for the schedule |
|
||||
| `timezone` | `string` | user timezone | Timezone for schedule evaluation |
|
||||
| `limit` | `number` | preset default | Max candidates to promote per cycle |
|
||||
| `minScore` | `number` | preset default | Minimum weighted score for promotion |
|
||||
| `minRecallCount` | `number` | preset default | Minimum recall count threshold |
|
||||
| `minUniqueQueries` | `number` | preset default | Minimum distinct query count threshold |
|
||||
| Key | Type | Default | Description |
|
||||
| ------------------ | --------- | -------------- | ----------------------------------------- |
|
||||
| `mode` | `string` | `"off"` | Preset: `off`, `core`, `rem`, or `deep` |
|
||||
| `cron` | `string` | preset default | Cron expression override for the schedule |
|
||||
| `timezone` | `string` | user timezone | Timezone for schedule evaluation |
|
||||
| `limit` | `number` | preset default | Max candidates to promote per cycle |
|
||||
| `minScore` | `number` | preset default | Minimum weighted score for promotion |
|
||||
| `minRecallCount` | `number` | preset default | Minimum recall count threshold |
|
||||
| `minUniqueQueries` | `number` | preset default | Minimum distinct query count threshold |
|
||||
| `verboseLogging` | `boolean` | `false` | Emit detailed per-run dreaming logs |
|
||||
|
||||
### Preset defaults
|
||||
|
||||
|
||||
@@ -41,6 +41,11 @@
|
||||
"label": "Promotion Min Queries",
|
||||
"placeholder": "2",
|
||||
"help": "Minimum unique query count required for automatic promotion."
|
||||
},
|
||||
"dreaming.verboseLogging": {
|
||||
"label": "Dreaming Verbose Logging",
|
||||
"placeholder": "false",
|
||||
"help": "Emit detailed dreaming-run logs for candidate ranking and promotion decisions."
|
||||
}
|
||||
},
|
||||
"configSchema": {
|
||||
@@ -80,6 +85,9 @@
|
||||
"minUniqueQueries": {
|
||||
"type": "number",
|
||||
"minimum": 0
|
||||
},
|
||||
"verboseLogging": {
|
||||
"type": "boolean"
|
||||
}
|
||||
},
|
||||
"required": ["mode"]
|
||||
|
||||
@@ -132,6 +132,7 @@ describe("memory-core /dreaming command", () => {
|
||||
expect(result.text).toContain("Dreaming status:");
|
||||
expect(result.text).toContain("- mode: deep");
|
||||
expect(result.text).toContain("- cadence: 0 */12 * * * (America/Los_Angeles)");
|
||||
expect(result.text).toContain("- verboseLogging: off");
|
||||
expect(runtime.config.writeConfigFile).not.toHaveBeenCalled();
|
||||
});
|
||||
|
||||
|
||||
@@ -107,6 +107,7 @@ function formatStatus(cfg: OpenClawConfig): string {
|
||||
`- cadence: ${cadence}${timezone}`,
|
||||
`- limit: ${resolved.limit}`,
|
||||
`- thresholds: minScore=${resolved.minScore}, minRecallCount=${resolved.minRecallCount}, minUniqueQueries=${resolved.minUniqueQueries}`,
|
||||
`- verboseLogging: ${resolved.verboseLogging ? "on" : "off"}`,
|
||||
].join("\n");
|
||||
}
|
||||
|
||||
|
||||
@@ -117,6 +117,7 @@ describe("short-term dreaming config", () => {
|
||||
minScore: constants.DEFAULT_DREAMING_MIN_SCORE,
|
||||
minRecallCount: constants.DEFAULT_DREAMING_MIN_RECALL_COUNT,
|
||||
minUniqueQueries: constants.DEFAULT_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
verboseLogging: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -131,6 +132,7 @@ describe("short-term dreaming config", () => {
|
||||
minScore: 0.4,
|
||||
minRecallCount: 2,
|
||||
minUniqueQueries: 3,
|
||||
verboseLogging: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
@@ -142,6 +144,7 @@ describe("short-term dreaming config", () => {
|
||||
minScore: 0.4,
|
||||
minRecallCount: 2,
|
||||
minUniqueQueries: 3,
|
||||
verboseLogging: true,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -165,6 +168,7 @@ describe("short-term dreaming config", () => {
|
||||
minScore: 0.6,
|
||||
minRecallCount: 2,
|
||||
minUniqueQueries: 3,
|
||||
verboseLogging: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -187,6 +191,7 @@ describe("short-term dreaming config", () => {
|
||||
minScore: constants.DREAMING_PRESET_DEFAULTS.deep.minScore,
|
||||
minRecallCount: constants.DREAMING_PRESET_DEFAULTS.deep.minRecallCount,
|
||||
minUniqueQueries: constants.DREAMING_PRESET_DEFAULTS.deep.minUniqueQueries,
|
||||
verboseLogging: false,
|
||||
});
|
||||
});
|
||||
|
||||
@@ -202,6 +207,28 @@ describe("short-term dreaming config", () => {
|
||||
expect(resolved.limit).toBe(0);
|
||||
});
|
||||
|
||||
it("accepts verboseLogging as a boolean or boolean string", () => {
|
||||
const enabled = resolveShortTermPromotionDreamingConfig({
|
||||
pluginConfig: {
|
||||
dreaming: {
|
||||
mode: "core",
|
||||
verboseLogging: true,
|
||||
},
|
||||
},
|
||||
});
|
||||
const disabled = resolveShortTermPromotionDreamingConfig({
|
||||
pluginConfig: {
|
||||
dreaming: {
|
||||
mode: "core",
|
||||
verboseLogging: "false",
|
||||
},
|
||||
},
|
||||
});
|
||||
|
||||
expect(enabled.verboseLogging).toBe(true);
|
||||
expect(disabled.verboseLogging).toBe(false);
|
||||
});
|
||||
|
||||
it("falls back to defaults when thresholds are negative", () => {
|
||||
const resolved = resolveShortTermPromotionDreamingConfig({
|
||||
pluginConfig: {
|
||||
@@ -263,6 +290,7 @@ describe("short-term dreaming cron reconciliation", () => {
|
||||
minScore: 0.5,
|
||||
minRecallCount: 4,
|
||||
minUniqueQueries: 5,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -294,6 +322,7 @@ describe("short-term dreaming cron reconciliation", () => {
|
||||
minScore: constants.DEFAULT_DREAMING_MIN_SCORE,
|
||||
minRecallCount: constants.DEFAULT_DREAMING_MIN_RECALL_COUNT,
|
||||
minUniqueQueries: constants.DEFAULT_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
verboseLogging: false,
|
||||
} as const;
|
||||
const desired = __testing.buildManagedDreamingCronJob(desiredConfig);
|
||||
const stalePrimary: CronJobLike = {
|
||||
@@ -384,6 +413,7 @@ describe("short-term dreaming cron reconciliation", () => {
|
||||
minScore: constants.DEFAULT_DREAMING_MIN_SCORE,
|
||||
minRecallCount: constants.DEFAULT_DREAMING_MIN_RECALL_COUNT,
|
||||
minUniqueQueries: constants.DEFAULT_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -417,6 +447,7 @@ describe("short-term dreaming cron reconciliation", () => {
|
||||
minScore: constants.DEFAULT_DREAMING_MIN_SCORE,
|
||||
minRecallCount: constants.DEFAULT_DREAMING_MIN_RECALL_COUNT,
|
||||
minUniqueQueries: constants.DEFAULT_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -449,6 +480,7 @@ describe("short-term dreaming cron reconciliation", () => {
|
||||
minScore: constants.DEFAULT_DREAMING_MIN_SCORE,
|
||||
minRecallCount: constants.DEFAULT_DREAMING_MIN_RECALL_COUNT,
|
||||
minUniqueQueries: constants.DEFAULT_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -499,6 +531,7 @@ describe("short-term dreaming trigger", () => {
|
||||
minScore: 0,
|
||||
minRecallCount: 0,
|
||||
minUniqueQueries: 0,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -539,6 +572,7 @@ describe("short-term dreaming trigger", () => {
|
||||
minScore: constants.DEFAULT_DREAMING_MIN_SCORE,
|
||||
minRecallCount: constants.DEFAULT_DREAMING_MIN_RECALL_COUNT,
|
||||
minUniqueQueries: constants.DEFAULT_DREAMING_MIN_UNIQUE_QUERIES,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -568,6 +602,7 @@ describe("short-term dreaming trigger", () => {
|
||||
minScore: 0,
|
||||
minRecallCount: 0,
|
||||
minUniqueQueries: 0,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -590,6 +625,7 @@ describe("short-term dreaming trigger", () => {
|
||||
minScore: 0,
|
||||
minRecallCount: 0,
|
||||
minUniqueQueries: 0,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -654,6 +690,7 @@ describe("short-term dreaming trigger", () => {
|
||||
minScore: 0,
|
||||
minRecallCount: 0,
|
||||
minUniqueQueries: 0,
|
||||
verboseLogging: false,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
@@ -680,4 +717,52 @@ describe("short-term dreaming trigger", () => {
|
||||
expect.arrayContaining(["glacier", "router", "failover"]),
|
||||
);
|
||||
});
|
||||
|
||||
it("emits detailed run logs when verboseLogging is enabled", async () => {
|
||||
const logger = createLogger();
|
||||
const workspaceDir = await fs.mkdtemp(path.join(os.tmpdir(), "memory-dreaming-verbose-"));
|
||||
tempDirs.push(workspaceDir);
|
||||
|
||||
await recordShortTermRecalls({
|
||||
workspaceDir,
|
||||
query: "backup policy",
|
||||
results: [
|
||||
{
|
||||
path: "memory/2026-04-02.md",
|
||||
startLine: 1,
|
||||
endLine: 1,
|
||||
score: 0.9,
|
||||
snippet: "Move backups to S3 Glacier.",
|
||||
source: "memory",
|
||||
},
|
||||
],
|
||||
});
|
||||
|
||||
const result = await runShortTermDreamingPromotionIfTriggered({
|
||||
cleanedBody: constants.DREAMING_SYSTEM_EVENT_TEXT,
|
||||
trigger: "heartbeat",
|
||||
workspaceDir,
|
||||
config: {
|
||||
enabled: true,
|
||||
cron: constants.DEFAULT_DREAMING_CRON_EXPR,
|
||||
limit: 10,
|
||||
minScore: 0,
|
||||
minRecallCount: 0,
|
||||
minUniqueQueries: 0,
|
||||
verboseLogging: true,
|
||||
},
|
||||
logger,
|
||||
});
|
||||
|
||||
expect(result?.handled).toBe(true);
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining("memory-core: dreaming verbose enabled"),
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining("memory-core: dreaming candidate details"),
|
||||
);
|
||||
expect(logger.info).toHaveBeenCalledWith(
|
||||
expect.stringContaining("memory-core: dreaming applied details"),
|
||||
);
|
||||
});
|
||||
});
|
||||
|
||||
@@ -113,6 +113,7 @@ export type ShortTermPromotionDreamingConfig = {
|
||||
minScore: number;
|
||||
minRecallCount: number;
|
||||
minUniqueQueries: number;
|
||||
verboseLogging: boolean;
|
||||
};
|
||||
|
||||
type ReconcileResult =
|
||||
@@ -179,6 +180,22 @@ function normalizeScore(value: unknown, fallback: number): number {
|
||||
return num;
|
||||
}
|
||||
|
||||
function normalizeBoolean(value: unknown, fallback: boolean): boolean {
|
||||
if (typeof value === "boolean") {
|
||||
return value;
|
||||
}
|
||||
if (typeof value === "string") {
|
||||
const normalized = value.trim().toLowerCase();
|
||||
if (normalized === "true") {
|
||||
return true;
|
||||
}
|
||||
if (normalized === "false") {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return fallback;
|
||||
}
|
||||
|
||||
function formatErrorMessage(err: unknown): string {
|
||||
if (err instanceof Error) {
|
||||
return err.message;
|
||||
@@ -370,6 +387,7 @@ export function resolveShortTermPromotionDreamingConfig(params: {
|
||||
minScore,
|
||||
minRecallCount,
|
||||
minUniqueQueries,
|
||||
verboseLogging: normalizeBoolean(dreaming?.verboseLogging, false),
|
||||
};
|
||||
}
|
||||
|
||||
@@ -471,6 +489,11 @@ export async function runShortTermDreamingPromotionIfTriggered(params: {
|
||||
}
|
||||
|
||||
try {
|
||||
if (params.config.verboseLogging) {
|
||||
params.logger.info(
|
||||
`memory-core: dreaming verbose enabled (cron=${params.config.cron}, limit=${params.config.limit}, minScore=${params.config.minScore.toFixed(3)}, minRecallCount=${params.config.minRecallCount}, minUniqueQueries=${params.config.minUniqueQueries}).`,
|
||||
);
|
||||
}
|
||||
const repair = await repairShortTermPromotionArtifacts({ workspaceDir });
|
||||
if (repair.changed) {
|
||||
params.logger.info(
|
||||
@@ -484,6 +507,18 @@ export async function runShortTermDreamingPromotionIfTriggered(params: {
|
||||
minRecallCount: params.config.minRecallCount,
|
||||
minUniqueQueries: params.config.minUniqueQueries,
|
||||
});
|
||||
if (params.config.verboseLogging) {
|
||||
const candidateSummary =
|
||||
candidates.length > 0
|
||||
? candidates
|
||||
.map(
|
||||
(candidate) =>
|
||||
`${candidate.path}:${candidate.startLine}-${candidate.endLine} score=${candidate.score.toFixed(3)} recalls=${candidate.recallCount} queries=${candidate.uniqueQueries} components={freq=${candidate.components.frequency.toFixed(3)},rel=${candidate.components.relevance.toFixed(3)},div=${candidate.components.diversity.toFixed(3)},rec=${candidate.components.recency.toFixed(3)},cons=${candidate.components.consolidation.toFixed(3)},concept=${candidate.components.conceptual.toFixed(3)}}`,
|
||||
)
|
||||
.join(" | ")
|
||||
: "none";
|
||||
params.logger.info(`memory-core: dreaming candidate details ${candidateSummary}`);
|
||||
}
|
||||
const applied = await applyShortTermPromotions({
|
||||
workspaceDir,
|
||||
candidates,
|
||||
@@ -492,6 +527,18 @@ export async function runShortTermDreamingPromotionIfTriggered(params: {
|
||||
minRecallCount: params.config.minRecallCount,
|
||||
minUniqueQueries: params.config.minUniqueQueries,
|
||||
});
|
||||
if (params.config.verboseLogging) {
|
||||
const appliedSummary =
|
||||
applied.appliedCandidates.length > 0
|
||||
? applied.appliedCandidates
|
||||
.map(
|
||||
(candidate) =>
|
||||
`${candidate.path}:${candidate.startLine}-${candidate.endLine} score=${candidate.score.toFixed(3)} recalls=${candidate.recallCount}`,
|
||||
)
|
||||
.join(" | ")
|
||||
: "none";
|
||||
params.logger.info(`memory-core: dreaming applied details ${appliedSummary}`);
|
||||
}
|
||||
params.logger.info(
|
||||
`memory-core: dreaming promotion complete (candidates=${candidates.length}, applied=${applied.applied}).`,
|
||||
);
|
||||
|
||||
Reference in New Issue
Block a user