// Memory V2 API 客户端。对应后端 /api/memory/v2 系列端点。 import { apiFetch } from "../api"; import { getBackendBaseURL } from "../config"; import { OptType, writeYLog } from "../log/y-log"; import type { MemorySearchMode, MemorySearchResult, MemoryTarget, MemoryV2ConfigResponse, MemoryV2Data, } from "./types"; /** 从响应里提取后端的错误详情文本。 */ function formatErrorDetail(detail: unknown): string | null { if (typeof detail === "string") { return detail; } if (Array.isArray(detail)) { const parts = detail .map((item) => { if (typeof item === "string") return item; if (item && typeof item === "object") { const record = item as Record; if (typeof record.msg === "string") return record.msg; } return null; }) .filter(Boolean); return parts.length > 0 ? parts.join("; ") : null; } return null; } async function readJson(response: Response, fallbackMessage: string): Promise { if (!response.ok) { const errorData = (await response.json().catch(() => ({}))) as { detail?: unknown; }; const detailMessage = formatErrorDetail(errorData.detail); throw new Error(detailMessage ?? `${fallbackMessage}: ${response.statusText}`); } return response.json() as Promise; } /** 拼接可选的 ?agent= 查询串。 */ function agentQuery(agent?: string): string { return agent ? `?agent=${encodeURIComponent(agent)}` : ""; } // --- 本地记忆数据 (USER.md / MEMORY.md) --- export async function loadMemoryV2(agent?: string): Promise { const response = await apiFetch( `${getBackendBaseURL()}/api/memory/v2${agentQuery(agent)}`, ); const data = await readJson(response, "Failed to fetch memory"); writeYLog({ optType: OptType.view, optDetail: "CM助手-全量-记忆-查看", url: `/api/memory/v2${agentQuery(agent)}`, reqParam: { agent }, returnParam: data, }); return data; } export async function addMemoryEntry( target: MemoryTarget, content: string, agent?: string, ): Promise { const response = await apiFetch( `${getBackendBaseURL()}/api/memory/v2/entries${agentQuery(agent)}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ target, content }), }, ); const data = await readJson(response, "Failed to add memory entry"); writeYLog({ optType: OptType.create, optDetail: "CM助手-全量-记忆-新增", url: `/api/memory/v2/entries${agentQuery(agent)}`, reqParam: { target, content, agent }, returnParam: data, }); return data; } export async function replaceMemoryEntry( target: MemoryTarget, oldText: string, content: string, agent?: string, ): Promise { const response = await apiFetch( `${getBackendBaseURL()}/api/memory/v2/entries${agentQuery(agent)}`, { method: "PUT", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ target, old_text: oldText, content }), }, ); const data = await readJson(response, "Failed to replace memory entry"); writeYLog({ optType: OptType.edit, optDetail: "CM助手-全量-记忆-编辑", url: `/api/memory/v2/entries${agentQuery(agent)}`, reqParam: { target, old_text: oldText, content, agent }, returnParam: data, }); return data; } export async function deleteMemoryEntry( target: MemoryTarget, oldText: string, agent?: string, ): Promise { const params = new URLSearchParams({ target, old_text: oldText }); if (agent) { params.set("agent", agent); } const response = await apiFetch( `${getBackendBaseURL()}/api/memory/v2/entries?${params.toString()}`, { method: "DELETE" }, ); const data = await readJson(response, "Failed to delete memory entry"); writeYLog({ optType: OptType.delete, optDetail: "CM助手-全量-记忆-删除", url: `/api/memory/v2/entries?${params.toString()}`, reqParam: { target, old_text: oldText, agent }, returnParam: data, }); return data; } // --- 配置 --- export async function loadMemoryConfig(): Promise { const response = await apiFetch(`${getBackendBaseURL()}/api/memory/v2/config`); const data = await readJson(response, "Failed to fetch memory config"); writeYLog({ optType: OptType.view, optDetail: "CM助手-全量-记忆-配置查看", url: "/api/memory/v2/config", returnParam: data, }); return data; } export async function patchMemoryConfig( overrides: Record, ): Promise { const response = await apiFetch(`${getBackendBaseURL()}/api/memory/v2/config`, { method: "PATCH", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ overrides }), }); const data = await readJson(response, "Failed to update memory config"); writeYLog({ optType: OptType.edit, optDetail: "CM助手-全量-记忆-配置编辑", url: "/api/memory/v2/config", reqParam: { overrides }, returnParam: data, }); return data; } // --- Hindsight 检索 --- export async function searchMemory( query: string, mode: MemorySearchMode = "recall", agent?: string, ): Promise { const response = await apiFetch( `${getBackendBaseURL()}/api/memory/v2/search${agentQuery(agent)}`, { method: "POST", headers: { "Content-Type": "application/json" }, body: JSON.stringify({ query, mode }), }, ); const data = await readJson(response, "Failed to search memory"); writeYLog({ optType: OptType.search, optDetail: "CM助手-全量-记忆-检索", url: `/api/memory/v2/search${agentQuery(agent)}`, reqParam: { query, mode, agent }, returnParam: data, }); return data; }