Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions apps/memos-local-openclaw/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -514,6 +514,7 @@ All optional — shown with defaults:
"recall": {
"maxResultsDefault": 6, // Default search results
"maxResultsMax": 20, // Max search results
"autoRecallMaxResults": 6, // Optional: override max results for the auto-recall hook only. Defaults to `maxResultsDefault`. Set lower (e.g. 3 or 5) to reduce auto-injected tokens while keeping `memory_search` results richer.
"minScoreDefault": 0.45, // Default min score threshold
"minScoreFloor": 0.35, // Lowest allowed min score
"rrfK": 60, // RRF fusion constant
Expand Down
15 changes: 13 additions & 2 deletions apps/memos-local-openclaw/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -1889,9 +1889,20 @@ Groups: ${groupNames.length > 0 ? groupNames.join(", ") : "(none)"}`,
ctx.log.debug(`auto-recall: query="${query.slice(0, 80)}"`);

// ── Phase 1: Local search ∥ Hub search (parallel) ──
const arLocalP = engine.search({ query, maxResults: 10, minScore: 0.45, ownerFilter: recallOwnerFilter });
// Issue #1514: previously hardcoded maxResults: 10 here, which made
// `recall.maxResultsDefault` and the new `recall.autoRecallMaxResults`
// ineffective for the auto-recall path. Resolution order:
// 1. `recall.autoRecallMaxResults` (explicit auto-recall cap)
// 2. `recall.maxResultsDefault` (shared default with memory_search)
// 3. literal 10 (defensive — `resolveConfig`
// always fills `maxResultsDefault`, so this fires only if a
// caller built a context without going through `resolveConfig`).
const recallCfg = ctx.config.recall ?? {};
const autoRecallMax = recallCfg.autoRecallMaxResults ?? recallCfg.maxResultsDefault ?? 10;

const arLocalP = engine.search({ query, maxResults: autoRecallMax, minScore: 0.45, ownerFilter: recallOwnerFilter });
const arHubP = ctx.config?.sharing?.enabled
? hubSearchMemories(store, ctx, { query, maxResults: 10, scope: "all" })
? hubSearchMemories(store, ctx, { query, maxResults: autoRecallMax, scope: "all" })
.catch((err: any) => { ctx.log.debug(`auto-recall: hub search failed (${err})`); return { hits: [] as any[], meta: {} }; })
: Promise.resolve({ hits: [] as any[], meta: {} });

Expand Down
4 changes: 4 additions & 0 deletions apps/memos-local-openclaw/src/config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -60,6 +60,10 @@ export function resolveConfig(raw: Partial<MemosLocalConfig> | undefined, stateD
recall: {
maxResultsDefault: cfg.recall?.maxResultsDefault ?? DEFAULTS.maxResultsDefault,
maxResultsMax: cfg.recall?.maxResultsMax ?? DEFAULTS.maxResultsMax,
// Optional override for the `before_prompt_build` auto-recall hook.
// Left undefined when the user does not configure it so the hook can
// fall through to `maxResultsDefault` at call time (issue #1514).
autoRecallMaxResults: cfg.recall?.autoRecallMaxResults,
minScoreDefault: cfg.recall?.minScoreDefault ?? DEFAULTS.minScoreDefault,
minScoreFloor: cfg.recall?.minScoreFloor ?? DEFAULTS.minScoreFloor,
rrfK: cfg.recall?.rrfK ?? DEFAULTS.rrfK,
Expand Down
8 changes: 8 additions & 0 deletions apps/memos-local-openclaw/src/types.ts
Original file line number Diff line number Diff line change
Expand Up @@ -305,6 +305,14 @@ export interface MemosLocalConfig {
recall?: {
maxResultsDefault?: number;
maxResultsMax?: number;
/**
* Override the maximum number of candidates the `before_prompt_build`
* auto-recall hook fetches per source (local + Hub). When undefined the
* hook falls back to `maxResultsDefault`. Use this to make auto-recall
* leaner (e.g. 3 or 5) without shrinking the result set of explicit
* `memory_search` tool calls. See issue #1514.
*/
autoRecallMaxResults?: number;
minScoreDefault?: number;
minScoreFloor?: number;
rrfK?: number;
Expand Down
40 changes: 40 additions & 0 deletions apps/memos-local-openclaw/tests/config.test.ts
Original file line number Diff line number Diff line change
Expand Up @@ -48,6 +48,46 @@ describe("resolveConfig", () => {
});
});

describe("recall.autoRecallMaxResults", () => {
it("leaves autoRecallMaxResults undefined when no recall config is provided (fall-through to maxResultsDefault)", () => {
const resolved = resolveConfig(undefined, "/tmp/memos-config-recall-default");

// Default policy: auto-recall path inherits maxResultsDefault when not overridden.
expect(resolved.recall?.maxResultsDefault).toBe(6);
expect(resolved.recall?.autoRecallMaxResults).toBeUndefined();
});

it("preserves an explicit autoRecallMaxResults value when set", () => {
const resolved = resolveConfig(
{
recall: {
maxResultsDefault: 6,
autoRecallMaxResults: 3,
},
} as any,
"/tmp/memos-config-recall-override",
);

expect(resolved.recall?.maxResultsDefault).toBe(6);
expect(resolved.recall?.autoRecallMaxResults).toBe(3);
});

it("allows independent control: maxResultsDefault for memory_search, autoRecallMaxResults for auto-recall", () => {
const resolved = resolveConfig(
{
recall: {
maxResultsDefault: 10,
autoRecallMaxResults: 5,
},
} as any,
"/tmp/memos-config-recall-split",
);

expect(resolved.recall?.maxResultsDefault).toBe(10);
expect(resolved.recall?.autoRecallMaxResults).toBe(5);
});
});

it("preserves explicit user providers when host capabilities are enabled", () => {
const resolved = resolveConfig(
{
Expand Down
Loading