Skip to content

Commit f9946eb

Browse files
committed
fix(memory): parse qmd vector status variants
1 parent 1f7b7c2 commit f9946eb

3 files changed

Lines changed: 68 additions & 5 deletions

File tree

CHANGELOG.md

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -68,6 +68,7 @@ Docs: https://docs.openclaw.ai
6868
- Ollama/onboarding: de-dupe suggested bare local models against installed `:latest` tags and skip redundant pulls, so setup shows the installed model once and no longer says it is downloading an already available model. Fixes #68952. Thanks @tleyden.
6969
- Memory-core/doctor: keep `doctor.memory.status` on the cached path by default and only run live embedding pings for explicit deep probes, preventing slow local embedding backends from blocking Gateway status checks. Fixes #71568. Thanks @apex-system.
7070
- Memory/QMD: group same-source collections into one QMD search invocation when the installed QMD supports multiple `-c` filters, while keeping older QMD builds on the per-collection fallback. Fixes #72484; supersedes #72485 and #69583. Thanks @BsnizND and @zeroaltitude.
71+
- Memory/QMD: accept QMD status vector-count variants such as `Vectors = 42`, `Vectors:42`, and `Vectors: 42 embedded`, so `memory status --deep` no longer reports embeddings unavailable for healthy QMD wrappers. Fixes #63652; carries forward #63678. Thanks @apoapostolov and @WarrenJones.
7172
- Memory/QMD: skip QMD vector status probes and embedding maintenance in lexical `searchMode: "search"`, so BM25-only QMD setups on ARM do not trigger llama.cpp/Vulkan builds during status checks or embed cycles. Fixes #59234 and #67113. Thanks @PrinceOfEgypt, @Vksh07, @Snipe76, @NomLom, @t4r3e2q1-commits, and @dmak.
7273
- Memory/QMD: report the live watcher dirty state in memory status, so changed QMD-backed memory files show as dirty until the queued sync finishes. Fixes #60244. Thanks @xinzf.
7374
- Compaction: skip oversized pre-compaction checkpoint snapshots and prune duplicate long user turns from compaction input and rotated successor transcripts, preventing retry storms from being preserved across checkpoint cycles. Fixes #72780. Thanks @SweetSophia.

extensions/memory-core/src/memory/qmd-manager.test.ts

Lines changed: 58 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4798,6 +4798,64 @@ describe("QmdMemoryManager", () => {
47984798
await manager.close();
47994799
});
48004800

4801+
it.each([
4802+
["equals separator", "Documents: 12\nVectors = 42\n"],
4803+
["tab separator", "Documents: 12\nVectors:\t42\n"],
4804+
["compact separator", "Documents: 12\nVectors:42\n"],
4805+
["embedded suffix", "Documents: 12\nVectors: 42 embedded\n"],
4806+
])("reports vector availability as ready for qmd status %s", async (_name, statusOutput) => {
4807+
spawnMock.mockImplementation((_cmd: string, args: string[]) => {
4808+
if (args[0] === "status") {
4809+
const child = createMockChild({ autoClose: false });
4810+
emitAndClose(child, "stdout", statusOutput);
4811+
return child;
4812+
}
4813+
return createMockChild();
4814+
});
4815+
4816+
const { manager } = await createManager({
4817+
cfg: {
4818+
...cfg,
4819+
memory: {
4820+
...cfg.memory,
4821+
qmd: { ...cfg.memory?.qmd, searchMode: "query" },
4822+
},
4823+
} as OpenClawConfig,
4824+
});
4825+
4826+
await expect(manager.probeVectorAvailability()).resolves.toBe(true);
4827+
await manager.close();
4828+
});
4829+
4830+
it("does not parse unrelated qmd status vector-like fields", async () => {
4831+
spawnMock.mockImplementation((_cmd: string, args: string[]) => {
4832+
if (args[0] === "status") {
4833+
const child = createMockChild({ autoClose: false });
4834+
emitAndClose(child, "stdout", "Documents: 12\nMaxVectors: 42\nVector index: yes\n");
4835+
return child;
4836+
}
4837+
return createMockChild();
4838+
});
4839+
4840+
const { manager } = await createManager({
4841+
cfg: {
4842+
...cfg,
4843+
memory: {
4844+
...cfg.memory,
4845+
qmd: { ...cfg.memory?.qmd, searchMode: "query" },
4846+
},
4847+
} as OpenClawConfig,
4848+
});
4849+
4850+
await expect(manager.probeVectorAvailability()).resolves.toBe(false);
4851+
expect(manager.status().vector).toEqual({
4852+
enabled: true,
4853+
available: false,
4854+
loadError: "Could not determine QMD vector status from `qmd status`",
4855+
});
4856+
await manager.close();
4857+
});
4858+
48014859
it("skips qmd status vector probes for lexical search mode", async () => {
48024860
const { manager } = await createManager({
48034861
cfg: {

extensions/memory-core/src/memory/qmd-manager.ts

Lines changed: 9 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -154,12 +154,16 @@ function normalizeHanBm25Query(query: string): string {
154154
}
155155

156156
function parseQmdStatusVectorCount(raw: string): number | null {
157-
const match = raw.match(/(?:^|\n)\s*Vectors:\s*(\d+)\b/i);
158-
if (!match) {
159-
return null;
157+
for (const line of raw.split(/\r?\n/)) {
158+
const match = line.match(/^\s*Vectors(?:\s*[:=]\s*|\s+)(\d+)\b/i);
159+
if (match?.[1]) {
160+
const count = Number.parseInt(match[1], 10);
161+
if (Number.isFinite(count)) {
162+
return count;
163+
}
164+
}
160165
}
161-
const count = Number.parseInt(match[1] ?? "", 10);
162-
return Number.isFinite(count) ? count : null;
166+
return null;
163167
}
164168

165169
function resolveStableJitterMs(params: { seed: string; windowMs: number }): number {

0 commit comments

Comments
 (0)