mirror of
https://github.com/ValueCell-ai/ClawX.git
synced 2026-08-14 00:48:10 +00:00
fix: seed memorySearch disabled when no user embedding config (#1148)
This commit is contained in:
@@ -29,6 +29,7 @@ import {
|
||||
} from './provider-keys';
|
||||
import { normalizePiAiModelCost, type PiAiModelCostRates } from '../shared/pi-ai-model-cost';
|
||||
import { withConfigLock } from './config-mutex';
|
||||
import { ensureMemorySearchDisabledDefault, hasUserMemorySearchConfig } from './openclaw-memory-search';
|
||||
import { PORTS } from './config';
|
||||
import { getSetting } from './store';
|
||||
import {
|
||||
@@ -2675,6 +2676,19 @@ export async function batchSyncConfigFields(token: string): Promise<void> {
|
||||
console.log('[batch-sync] Seeded agents.defaults.compaction.mode=safeguard');
|
||||
}
|
||||
|
||||
// ── Memory search default ──
|
||||
// OpenClaw defaults to the openai embedding provider; without a key that
|
||||
// yields doctor errors and a broken memory_search tool. Seed enabled=false
|
||||
// only when the user has no memorySearch config anywhere AND no OpenAI key
|
||||
// (i.e. the default embedding model is unusable). Existing user config is
|
||||
// never modified.
|
||||
if (!hasUserMemorySearchConfig(config)
|
||||
&& !(await getProviderApiKeyFromOpenClaw('openai'))
|
||||
&& ensureMemorySearchDisabledDefault(config)) {
|
||||
modified = true;
|
||||
console.log('[batch-sync] Seeded agents.defaults.memorySearch.enabled=false (no embedding provider configured)');
|
||||
}
|
||||
|
||||
// ── Custom provider contextWindow backfill ──
|
||||
const backfilledContextWindows = backfillCustomProviderModelContextWindows(config);
|
||||
if (backfilledContextWindows.length > 0) {
|
||||
|
||||
@@ -0,0 +1,46 @@
|
||||
/**
|
||||
* Memory search default seeding for openclaw.json.
|
||||
*
|
||||
* OpenClaw enables semantic memory search by default with the `openai`
|
||||
* embedding provider, so a user without an OpenAI key gets doctor errors and
|
||||
* a broken memory_search tool. ClawX seeds `agents.defaults.memorySearch =
|
||||
* { enabled: false }` at Gateway prelaunch — but only when the user has no
|
||||
* memorySearch config anywhere (global defaults or per-agent overrides).
|
||||
* Existing user config is never modified.
|
||||
*/
|
||||
|
||||
function isRecord(value: unknown): value is Record<string, unknown> {
|
||||
return Boolean(value) && typeof value === 'object' && !Array.isArray(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* True when the user manages memorySearch themselves: either
|
||||
* `agents.defaults.memorySearch` or any `agents.list[].memorySearch` exists.
|
||||
*/
|
||||
export function hasUserMemorySearchConfig(config: Record<string, unknown>): boolean {
|
||||
const agents = isRecord(config.agents) ? config.agents : undefined;
|
||||
if (!agents) return false;
|
||||
|
||||
const defaults = isRecord(agents.defaults) ? agents.defaults : undefined;
|
||||
if (defaults && defaults.memorySearch !== undefined) return true;
|
||||
|
||||
const list = Array.isArray(agents.list) ? agents.list : [];
|
||||
return list.some((entry) => isRecord(entry) && entry.memorySearch !== undefined);
|
||||
}
|
||||
|
||||
/**
|
||||
* Seed `agents.defaults.memorySearch = { enabled: false }` when the user has
|
||||
* no memorySearch config at all. Mutates `config` in place and returns true
|
||||
* when a change was made. Never touches existing memorySearch objects.
|
||||
*/
|
||||
export function ensureMemorySearchDisabledDefault(config: Record<string, unknown>): boolean {
|
||||
if (hasUserMemorySearchConfig(config)) return false;
|
||||
|
||||
const agents = (isRecord(config.agents) ? config.agents : {}) as Record<string, unknown>;
|
||||
const defaults = (isRecord(agents.defaults) ? agents.defaults : {}) as Record<string, unknown>;
|
||||
|
||||
defaults.memorySearch = { enabled: false };
|
||||
agents.defaults = defaults;
|
||||
config.agents = agents;
|
||||
return true;
|
||||
}
|
||||
@@ -0,0 +1,80 @@
|
||||
import { describe, expect, it } from 'vitest';
|
||||
import {
|
||||
ensureMemorySearchDisabledDefault,
|
||||
hasUserMemorySearchConfig,
|
||||
} from '@electron/utils/openclaw-memory-search';
|
||||
|
||||
describe('openclaw-memory-search', () => {
|
||||
it('seeds memorySearch.enabled=false when no memorySearch config exists', () => {
|
||||
const config: Record<string, unknown> = {
|
||||
agents: { defaults: { model: { primary: 'custom-customfc/gpt-5.5' } } },
|
||||
};
|
||||
expect(ensureMemorySearchDisabledDefault(config)).toBe(true);
|
||||
expect(config).toEqual({
|
||||
agents: {
|
||||
defaults: {
|
||||
model: { primary: 'custom-customfc/gpt-5.5' },
|
||||
memorySearch: { enabled: false },
|
||||
},
|
||||
},
|
||||
});
|
||||
});
|
||||
|
||||
it('seeds on a completely empty config', () => {
|
||||
const config: Record<string, unknown> = {};
|
||||
expect(ensureMemorySearchDisabledDefault(config)).toBe(true);
|
||||
expect(config).toEqual({
|
||||
agents: { defaults: { memorySearch: { enabled: false } } },
|
||||
});
|
||||
});
|
||||
|
||||
it('never touches existing defaults.memorySearch config', () => {
|
||||
const config: Record<string, unknown> = {
|
||||
agents: {
|
||||
defaults: {
|
||||
memorySearch: {
|
||||
provider: 'custom-customfc',
|
||||
model: 'text-embedding-3-small',
|
||||
fallback: 'none',
|
||||
remote: { baseUrl: 'https://taolat.com/v1' },
|
||||
},
|
||||
},
|
||||
},
|
||||
};
|
||||
const before = JSON.parse(JSON.stringify(config));
|
||||
expect(ensureMemorySearchDisabledDefault(config)).toBe(false);
|
||||
expect(config).toEqual(before);
|
||||
});
|
||||
|
||||
it('never seeds when a per-agent memorySearch override exists', () => {
|
||||
const config: Record<string, unknown> = {
|
||||
agents: {
|
||||
defaults: { model: { primary: 'openai/gpt-4o' } },
|
||||
list: [
|
||||
{ id: 'main' },
|
||||
{ id: 'research', memorySearch: { provider: 'openai' } },
|
||||
],
|
||||
},
|
||||
};
|
||||
const before = JSON.parse(JSON.stringify(config));
|
||||
expect(ensureMemorySearchDisabledDefault(config)).toBe(false);
|
||||
expect(config).toEqual(before);
|
||||
});
|
||||
|
||||
it('treats explicit enabled=true as user config', () => {
|
||||
const config: Record<string, unknown> = {
|
||||
agents: { defaults: { memorySearch: { enabled: true } } },
|
||||
};
|
||||
expect(hasUserMemorySearchConfig(config)).toBe(true);
|
||||
expect(ensureMemorySearchDisabledDefault(config)).toBe(false);
|
||||
expect((config.agents as { defaults: { memorySearch: { enabled: boolean } } }).defaults.memorySearch.enabled).toBe(true);
|
||||
});
|
||||
|
||||
it('treats an empty memorySearch object as user config', () => {
|
||||
const config: Record<string, unknown> = {
|
||||
agents: { defaults: { memorySearch: {} } },
|
||||
};
|
||||
expect(hasUserMemorySearchConfig(config)).toBe(true);
|
||||
expect(ensureMemorySearchDisabledDefault(config)).toBe(false);
|
||||
});
|
||||
});
|
||||
Reference in New Issue
Block a user