diff --git a/mem0-ts/src/client/mem0.ts b/mem0-ts/src/client/mem0.ts index 89fe45cef..b2d1cca85 100644 --- a/mem0-ts/src/client/mem0.ts +++ b/mem0-ts/src/client/mem0.ts @@ -696,7 +696,10 @@ export default class MemoryClient { throw new Error("Missing filters or schema"); } - const { filters, ...rest } = data; + // filters and schema are user-controlled blobs whose keys must reach the + // API verbatim; only the remaining SDK params (e.g. exportInstructions) + // get camel->snake conversion. See issue #5593. + const { filters, schema, ...rest } = data; const response = await this._fetchWithErrorHandling( `${this.host}/v1/exports/`, { @@ -705,6 +708,7 @@ export default class MemoryClient { body: JSON.stringify({ ...camelToSnakeKeys(rest), filters, + schema, }), }, ); diff --git a/mem0-ts/src/client/tests/memoryClient.exports.test.ts b/mem0-ts/src/client/tests/memoryClient.exports.test.ts new file mode 100644 index 000000000..c235d77b8 --- /dev/null +++ b/mem0-ts/src/client/tests/memoryClient.exports.test.ts @@ -0,0 +1,47 @@ +/** + * MemoryClient unit tests — createMemoryExport. + * Verifies request construction, not mock response echo. + */ +import { MemoryClient } from "../mem0"; +import { TEST_API_KEY } from "./helpers"; +import { + setupMockFetch, + findFetchCall, + getFetchBody, + installConsoleSuppression, +} from "./setup"; + +installConsoleSuppression(); + +describe("MemoryClient - createMemoryExport()", () => { + test("sends user-defined schema keys verbatim, converts SDK params", async () => { + const extra = new Map(); + extra.set("/v1/exports/", { + status: 200, + body: { message: "ok", id: "exp_1" }, + }); + const mock = setupMockFetch(extra); + + const client = new MemoryClient({ apiKey: TEST_API_KEY }); + await client.createMemoryExport({ + // camelCase keys here are user-defined export field names — they must + // not be snake_cased on the way out. + schema: { messageId: "string", customField: { nestedKey: "number" } }, + filters: { user_id: "u1" }, + exportInstructions: "export it", + }); + + const call = findFetchCall(mock, "/v1/exports/", "POST"); + expect(call).toBeDefined(); + const body = getFetchBody(call!); + + // User blobs round-trip verbatim (no camel->snake on their keys). + expect(body.schema).toEqual({ + messageId: "string", + customField: { nestedKey: "number" }, + }); + expect(body.filters).toEqual({ user_id: "u1" }); + // SDK param is still snake_cased. + expect(body.export_instructions).toBe("export it"); + }); +});