Files
openclaw/src/agents/memory-search.test.ts
Josh Avant fb289b7a79 Memory: handle SecretRef keys in doctor embeddings (#36835)
Merged via squash.

Prepared head SHA: c1a3d0caae60115d886e8bfc9983c9533c773f04
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Co-authored-by: joshavant <830519+joshavant@users.noreply.github.com>
Reviewed-by: @joshavant
2026-03-05 20:05:59 -06:00

306 lines
8.3 KiB
TypeScript

import { describe, expect, it } from "vitest";
import type { OpenClawConfig } from "../config/config.js";
import { resolveMemorySearchConfig } from "./memory-search.js";
const asConfig = (cfg: OpenClawConfig): OpenClawConfig => cfg;
describe("memory search config", () => {
function configWithDefaultProvider(
provider: "openai" | "local" | "gemini" | "mistral" | "ollama",
): OpenClawConfig {
return asConfig({
agents: {
defaults: {
memorySearch: {
provider,
},
},
},
});
}
function expectDefaultRemoteBatch(resolved: ReturnType<typeof resolveMemorySearchConfig>): void {
expect(resolved?.remote?.batch).toEqual({
enabled: false,
wait: true,
concurrency: 2,
pollIntervalMs: 2000,
timeoutMinutes: 60,
});
}
it("returns null when disabled", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: { enabled: true },
},
list: [
{
id: "main",
default: true,
memorySearch: { enabled: false },
},
],
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved).toBeNull();
});
it("defaults provider to auto when unspecified", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
enabled: true,
},
},
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.provider).toBe("auto");
expect(resolved?.fallback).toBe("none");
});
it("merges defaults and overrides", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
provider: "openai",
model: "text-embedding-3-small",
store: {
vector: {
enabled: false,
extensionPath: "/opt/sqlite-vec.dylib",
},
},
chunking: { tokens: 500, overlap: 100 },
query: { maxResults: 4, minScore: 0.2 },
},
},
list: [
{
id: "main",
default: true,
memorySearch: {
chunking: { tokens: 320 },
query: { maxResults: 8 },
store: {
vector: {
enabled: true,
},
},
},
},
],
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.provider).toBe("openai");
expect(resolved?.model).toBe("text-embedding-3-small");
expect(resolved?.chunking.tokens).toBe(320);
expect(resolved?.chunking.overlap).toBe(100);
expect(resolved?.query.maxResults).toBe(8);
expect(resolved?.query.minScore).toBe(0.2);
expect(resolved?.store.vector.enabled).toBe(true);
expect(resolved?.store.vector.extensionPath).toBe("/opt/sqlite-vec.dylib");
});
it("merges extra memory paths from defaults and overrides", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
extraPaths: ["/shared/notes", " docs "],
},
},
list: [
{
id: "main",
default: true,
memorySearch: {
extraPaths: ["/shared/notes", "../team-notes"],
},
},
],
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.extraPaths).toEqual(["/shared/notes", "docs", "../team-notes"]);
});
it("includes batch defaults for openai without remote overrides", () => {
const cfg = configWithDefaultProvider("openai");
const resolved = resolveMemorySearchConfig(cfg, "main");
expectDefaultRemoteBatch(resolved);
});
it("keeps remote unset for local provider without overrides", () => {
const cfg = configWithDefaultProvider("local");
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.remote).toBeUndefined();
});
it("includes remote defaults for gemini without overrides", () => {
const cfg = configWithDefaultProvider("gemini");
const resolved = resolveMemorySearchConfig(cfg, "main");
expectDefaultRemoteBatch(resolved);
});
it("includes remote defaults and model default for mistral without overrides", () => {
const cfg = configWithDefaultProvider("mistral");
const resolved = resolveMemorySearchConfig(cfg, "main");
expectDefaultRemoteBatch(resolved);
expect(resolved?.model).toBe("mistral-embed");
});
it("includes remote defaults and model default for ollama without overrides", () => {
const cfg = configWithDefaultProvider("ollama");
const resolved = resolveMemorySearchConfig(cfg, "main");
expectDefaultRemoteBatch(resolved);
expect(resolved?.model).toBe("nomic-embed-text");
});
it("defaults session delta thresholds", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
provider: "openai",
},
},
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.sync.sessions).toEqual({
deltaBytes: 100000,
deltaMessages: 50,
});
});
it("merges remote defaults with agent overrides", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
provider: "openai",
remote: {
baseUrl: "https://default.example/v1",
apiKey: "default-key",
headers: { "X-Default": "on" },
},
},
},
list: [
{
id: "main",
default: true,
memorySearch: {
remote: {
baseUrl: "https://agent.example/v1",
},
},
},
],
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.remote).toEqual({
baseUrl: "https://agent.example/v1",
apiKey: "default-key",
headers: { "X-Default": "on" },
batch: {
enabled: false,
wait: true,
concurrency: 2,
pollIntervalMs: 2000,
timeoutMinutes: 60,
},
});
});
it("preserves SecretRef remote apiKey when merging defaults with agent overrides", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
provider: "openai",
remote: {
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
headers: { "X-Default": "on" },
},
},
},
list: [
{
id: "main",
default: true,
memorySearch: {
remote: {
baseUrl: "https://agent.example/v1",
},
},
},
],
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.remote).toEqual({
baseUrl: "https://agent.example/v1",
apiKey: { source: "env", provider: "default", id: "OPENAI_API_KEY" },
headers: { "X-Default": "on" },
batch: {
enabled: false,
wait: true,
concurrency: 2,
pollIntervalMs: 2000,
timeoutMinutes: 60,
},
});
});
it("gates session sources behind experimental flag", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
provider: "openai",
sources: ["memory", "sessions"],
},
},
list: [
{
id: "main",
default: true,
memorySearch: {
experimental: { sessionMemory: false },
},
},
],
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.sources).toEqual(["memory"]);
});
it("allows session sources when experimental flag is enabled", () => {
const cfg = asConfig({
agents: {
defaults: {
memorySearch: {
provider: "openai",
sources: ["memory", "sessions"],
experimental: { sessionMemory: true },
},
},
},
});
const resolved = resolveMemorySearchConfig(cfg, "main");
expect(resolved?.sources).toContain("sessions");
});
});