mirror of
https://github.com/openclaw/openclaw.git
synced 2026-05-08 07:51:26 +00:00
fix(doctor): use gateway health status for memory search key check (#22327)
Merged via /review-pr -> /prepare-pr -> /merge-pr.
Prepared head SHA: 2f02ec9403
Co-authored-by: therk <901920+therk@users.noreply.github.com>
Co-authored-by: gumadeiras <5599352+gumadeiras@users.noreply.github.com>
Reviewed-by: @gumadeiras
This commit is contained in:
committed by
GitHub
parent
bf373eeb43
commit
8d69251475
128
src/gateway/server-methods/doctor.test.ts
Normal file
128
src/gateway/server-methods/doctor.test.ts
Normal file
@@ -0,0 +1,128 @@
|
||||
import { beforeEach, describe, expect, it, vi } from "vitest";
|
||||
import type { OpenClawConfig } from "../../config/config.js";
|
||||
|
||||
const loadConfig = vi.hoisted(() => vi.fn(() => ({}) as OpenClawConfig));
|
||||
const resolveDefaultAgentId = vi.hoisted(() => vi.fn(() => "main"));
|
||||
const getMemorySearchManager = vi.hoisted(() => vi.fn());
|
||||
|
||||
vi.mock("../../config/config.js", () => ({
|
||||
loadConfig,
|
||||
}));
|
||||
|
||||
vi.mock("../../agents/agent-scope.js", () => ({
|
||||
resolveDefaultAgentId,
|
||||
}));
|
||||
|
||||
vi.mock("../../memory/index.js", () => ({
|
||||
getMemorySearchManager,
|
||||
}));
|
||||
|
||||
import { doctorHandlers } from "./doctor.js";
|
||||
|
||||
describe("doctor.memory.status", () => {
|
||||
beforeEach(() => {
|
||||
loadConfig.mockClear();
|
||||
resolveDefaultAgentId.mockClear();
|
||||
getMemorySearchManager.mockReset();
|
||||
});
|
||||
|
||||
it("returns gateway embedding probe status for the default agent", async () => {
|
||||
const close = vi.fn().mockResolvedValue(undefined);
|
||||
getMemorySearchManager.mockResolvedValue({
|
||||
manager: {
|
||||
status: () => ({ provider: "gemini" }),
|
||||
probeEmbeddingAvailability: vi.fn().mockResolvedValue({ ok: true }),
|
||||
close,
|
||||
},
|
||||
});
|
||||
const respond = vi.fn();
|
||||
|
||||
await doctorHandlers["doctor.memory.status"]({
|
||||
req: {} as never,
|
||||
params: {} as never,
|
||||
respond: respond as never,
|
||||
context: {} as never,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
});
|
||||
|
||||
expect(getMemorySearchManager).toHaveBeenCalledWith({
|
||||
cfg: expect.any(Object),
|
||||
agentId: "main",
|
||||
purpose: "status",
|
||||
});
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
true,
|
||||
{
|
||||
agentId: "main",
|
||||
provider: "gemini",
|
||||
embedding: { ok: true },
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
|
||||
it("returns unavailable when memory manager is missing", async () => {
|
||||
getMemorySearchManager.mockResolvedValue({
|
||||
manager: null,
|
||||
error: "memory search unavailable",
|
||||
});
|
||||
const respond = vi.fn();
|
||||
|
||||
await doctorHandlers["doctor.memory.status"]({
|
||||
req: {} as never,
|
||||
params: {} as never,
|
||||
respond: respond as never,
|
||||
context: {} as never,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
});
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
true,
|
||||
{
|
||||
agentId: "main",
|
||||
embedding: {
|
||||
ok: false,
|
||||
error: "memory search unavailable",
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
});
|
||||
|
||||
it("returns probe failure when manager probe throws", async () => {
|
||||
const close = vi.fn().mockResolvedValue(undefined);
|
||||
getMemorySearchManager.mockResolvedValue({
|
||||
manager: {
|
||||
status: () => ({ provider: "openai" }),
|
||||
probeEmbeddingAvailability: vi.fn().mockRejectedValue(new Error("timeout")),
|
||||
close,
|
||||
},
|
||||
});
|
||||
const respond = vi.fn();
|
||||
|
||||
await doctorHandlers["doctor.memory.status"]({
|
||||
req: {} as never,
|
||||
params: {} as never,
|
||||
respond: respond as never,
|
||||
context: {} as never,
|
||||
client: null,
|
||||
isWebchatConnect: () => false,
|
||||
});
|
||||
|
||||
expect(respond).toHaveBeenCalledWith(
|
||||
true,
|
||||
{
|
||||
agentId: "main",
|
||||
embedding: {
|
||||
ok: false,
|
||||
error: "gateway memory probe failed: timeout",
|
||||
},
|
||||
},
|
||||
undefined,
|
||||
);
|
||||
expect(close).toHaveBeenCalled();
|
||||
});
|
||||
});
|
||||
62
src/gateway/server-methods/doctor.ts
Normal file
62
src/gateway/server-methods/doctor.ts
Normal file
@@ -0,0 +1,62 @@
|
||||
import { resolveDefaultAgentId } from "../../agents/agent-scope.js";
|
||||
import { loadConfig } from "../../config/config.js";
|
||||
import { getMemorySearchManager } from "../../memory/index.js";
|
||||
import { formatError } from "../server-utils.js";
|
||||
import type { GatewayRequestHandlers } from "./types.js";
|
||||
|
||||
export type DoctorMemoryStatusPayload = {
|
||||
agentId: string;
|
||||
provider?: string;
|
||||
embedding: {
|
||||
ok: boolean;
|
||||
error?: string;
|
||||
};
|
||||
};
|
||||
|
||||
export const doctorHandlers: GatewayRequestHandlers = {
|
||||
"doctor.memory.status": async ({ respond }) => {
|
||||
const cfg = loadConfig();
|
||||
const agentId = resolveDefaultAgentId(cfg);
|
||||
const { manager, error } = await getMemorySearchManager({
|
||||
cfg,
|
||||
agentId,
|
||||
purpose: "status",
|
||||
});
|
||||
if (!manager) {
|
||||
const payload: DoctorMemoryStatusPayload = {
|
||||
agentId,
|
||||
embedding: {
|
||||
ok: false,
|
||||
error: error ?? "memory search unavailable",
|
||||
},
|
||||
};
|
||||
respond(true, payload, undefined);
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
const status = manager.status();
|
||||
let embedding = await manager.probeEmbeddingAvailability();
|
||||
if (!embedding.ok && !embedding.error) {
|
||||
embedding = { ok: false, error: "memory embeddings unavailable" };
|
||||
}
|
||||
const payload: DoctorMemoryStatusPayload = {
|
||||
agentId,
|
||||
provider: status.provider,
|
||||
embedding,
|
||||
};
|
||||
respond(true, payload, undefined);
|
||||
} catch (err) {
|
||||
const payload: DoctorMemoryStatusPayload = {
|
||||
agentId,
|
||||
embedding: {
|
||||
ok: false,
|
||||
error: `gateway memory probe failed: ${formatError(err)}`,
|
||||
},
|
||||
};
|
||||
respond(true, payload, undefined);
|
||||
} finally {
|
||||
await manager.close?.().catch(() => {});
|
||||
}
|
||||
},
|
||||
};
|
||||
Reference in New Issue
Block a user