mirror of https://github.com/openclaw/openclaw.git
perf(memory): avoid eager provider init on empty search
This commit is contained in:
parent
db0f957aba
commit
7330e2ce23
|
|
@ -19,6 +19,8 @@ export type BaseConfigSchemaResponse = {
|
|||
generatedAt: string;
|
||||
};
|
||||
|
||||
type BaseConfigSchemaStablePayload = Omit<BaseConfigSchemaResponse, "generatedAt">;
|
||||
|
||||
function cloneSchema<T>(value: T): T {
|
||||
if (typeof structuredClone === "function") {
|
||||
return structuredClone(value);
|
||||
|
|
@ -54,19 +56,42 @@ function stripChannelSchema(schema: ConfigSchema): ConfigSchema {
|
|||
return next;
|
||||
}
|
||||
|
||||
export function computeBaseConfigSchemaResponse(params?: {
|
||||
generatedAt?: string;
|
||||
}): BaseConfigSchemaResponse {
|
||||
let baseConfigSchemaStablePayload: BaseConfigSchemaStablePayload | null = null;
|
||||
|
||||
function computeBaseConfigSchemaStablePayload(): BaseConfigSchemaStablePayload {
|
||||
if (baseConfigSchemaStablePayload) {
|
||||
return {
|
||||
schema: cloneSchema(baseConfigSchemaStablePayload.schema),
|
||||
uiHints: cloneSchema(baseConfigSchemaStablePayload.uiHints),
|
||||
version: baseConfigSchemaStablePayload.version,
|
||||
};
|
||||
}
|
||||
const schema = OpenClawSchema.toJSONSchema({
|
||||
target: "draft-07",
|
||||
unrepresentable: "any",
|
||||
});
|
||||
schema.title = "OpenClawConfig";
|
||||
const hints = applyDerivedTags(mapSensitivePaths(OpenClawSchema, "", buildBaseHints()));
|
||||
return {
|
||||
const stablePayload = {
|
||||
schema: stripChannelSchema(schema),
|
||||
uiHints: hints,
|
||||
uiHints: applyDerivedTags(mapSensitivePaths(OpenClawSchema, "", buildBaseHints())),
|
||||
version: VERSION,
|
||||
} satisfies BaseConfigSchemaStablePayload;
|
||||
baseConfigSchemaStablePayload = stablePayload;
|
||||
return {
|
||||
schema: cloneSchema(stablePayload.schema),
|
||||
uiHints: cloneSchema(stablePayload.uiHints),
|
||||
version: stablePayload.version,
|
||||
};
|
||||
}
|
||||
|
||||
export function computeBaseConfigSchemaResponse(params?: {
|
||||
generatedAt?: string;
|
||||
}): BaseConfigSchemaResponse {
|
||||
const stablePayload = computeBaseConfigSchemaStablePayload();
|
||||
return {
|
||||
schema: stablePayload.schema,
|
||||
uiHints: stablePayload.uiHints,
|
||||
version: stablePayload.version,
|
||||
generatedAt: params?.generatedAt ?? new Date().toISOString(),
|
||||
};
|
||||
}
|
||||
|
|
|
|||
|
|
@ -957,6 +957,7 @@ describe("memory index", () => {
|
|||
|
||||
const result = await getMemorySearchManager({ cfg, agentId: "main" });
|
||||
const manager = requireManager(result);
|
||||
await manager.probeEmbeddingAvailability();
|
||||
|
||||
expect(
|
||||
providerCalls.some(
|
||||
|
|
|
|||
|
|
@ -93,17 +93,15 @@ describe("memory manager cache hydration", () => {
|
|||
|
||||
expect(managers).toHaveLength(12);
|
||||
expect(new Set(managers).size).toBe(1);
|
||||
expect(hoisted.providerCreateCalls).toBe(1);
|
||||
expect(hoisted.providerCreateCalls).toBe(0);
|
||||
|
||||
await managers[0].close();
|
||||
});
|
||||
|
||||
it("drains in-flight manager creation during global teardown", async () => {
|
||||
it("evicts cached managers during global teardown", async () => {
|
||||
const indexPath = path.join(workspaceDir, "index.sqlite");
|
||||
const cfg = createMemoryConcurrencyConfig(indexPath);
|
||||
|
||||
hoisted.providerDelayMs = 100;
|
||||
|
||||
const pendingResult = RawMemoryIndexManager.get({ cfg, agentId: "main" });
|
||||
await closeAllMemoryIndexManagers();
|
||||
const firstManager = await pendingResult;
|
||||
|
|
@ -113,7 +111,7 @@ describe("memory manager cache hydration", () => {
|
|||
expect(firstManager).toBeTruthy();
|
||||
expect(secondManager).toBeTruthy();
|
||||
expect(Object.is(secondManager, firstManager)).toBe(false);
|
||||
expect(hoisted.providerCreateCalls).toBe(2);
|
||||
expect(hoisted.providerCreateCalls).toBe(0);
|
||||
|
||||
await secondManager?.close?.();
|
||||
});
|
||||
|
|
|
|||
|
|
@ -193,11 +193,6 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem
|
|||
return pending;
|
||||
}
|
||||
const createPromise = (async () => {
|
||||
const providerResult = await MemoryIndexManager.loadProviderResult({
|
||||
cfg,
|
||||
agentId,
|
||||
settings,
|
||||
});
|
||||
const refreshed = INDEX_CACHE.get(key);
|
||||
if (refreshed) {
|
||||
return refreshed;
|
||||
|
|
@ -208,7 +203,6 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem
|
|||
agentId,
|
||||
workspaceDir,
|
||||
settings,
|
||||
providerResult,
|
||||
purpose: params.purpose,
|
||||
});
|
||||
INDEX_CACHE.set(key, manager);
|
||||
|
|
@ -334,17 +328,21 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem
|
|||
sessionKey?: string;
|
||||
},
|
||||
): Promise<MemorySearchResult[]> {
|
||||
await this.ensureProviderInitialized();
|
||||
const cleaned = query.trim();
|
||||
if (!cleaned) {
|
||||
return [];
|
||||
}
|
||||
void this.warmSession(opts?.sessionKey);
|
||||
if (this.settings.sync.onSearch && (this.dirty || this.sessionsDirty)) {
|
||||
void this.sync({ reason: "search" }).catch((err) => {
|
||||
log.warn(`memory sync failed (search): ${String(err)}`);
|
||||
});
|
||||
}
|
||||
const cleaned = query.trim();
|
||||
if (!cleaned) {
|
||||
const hasIndexedContent = this.hasIndexedContent();
|
||||
if (!hasIndexedContent) {
|
||||
return [];
|
||||
}
|
||||
await this.ensureProviderInitialized();
|
||||
const minScore = opts?.minScore ?? this.settings.query.minScore;
|
||||
const maxResults = opts?.maxResults ?? this.settings.query.maxResults;
|
||||
const hybrid = this.settings.query.hybrid;
|
||||
|
|
@ -437,6 +435,32 @@ export class MemoryIndexManager extends MemoryManagerEmbeddingOps implements Mem
|
|||
.slice(0, maxResults);
|
||||
}
|
||||
|
||||
private hasIndexedContent(): boolean {
|
||||
const chunks =
|
||||
(
|
||||
this.db.prepare(`SELECT COUNT(*) as c FROM chunks`).get() as
|
||||
| {
|
||||
c: number;
|
||||
}
|
||||
| undefined
|
||||
)?.c ?? 0;
|
||||
if (chunks > 0) {
|
||||
return true;
|
||||
}
|
||||
if (!this.fts.enabled || !this.fts.available) {
|
||||
return false;
|
||||
}
|
||||
const ftsRows =
|
||||
(
|
||||
this.db.prepare(`SELECT COUNT(*) as c FROM ${FTS_TABLE}`).get() as
|
||||
| {
|
||||
c: number;
|
||||
}
|
||||
| undefined
|
||||
)?.c ?? 0;
|
||||
return ftsRows > 0;
|
||||
}
|
||||
|
||||
private async searchVector(
|
||||
queryVec: number[],
|
||||
limit: number,
|
||||
|
|
|
|||
Loading…
Reference in New Issue