feat(cli): migrate Python and Node CLIs to v3 API routes (#4916)

This commit is contained in:
Saket Aryan
2026-04-22 15:20:38 +05:30
committed by GitHub
parent daa4495583
commit 32b74e18b7
22 changed files with 45 additions and 296 deletions
+1 -1
View File
@@ -1,6 +1,6 @@
{ {
"name": "@mem0/cli", "name": "@mem0/cli",
"version": "0.2.3", "version": "0.2.4",
"description": "The official CLI for mem0 — the memory layer for AI agents", "description": "The official CLI for mem0 — the memory layer for AI agents",
"type": "module", "type": "module",
"bin": { "bin": {
-3
View File
@@ -15,7 +15,6 @@ export interface AddOptions {
infer?: boolean; infer?: boolean;
expires?: string; expires?: string;
categories?: string[]; categories?: string[];
enableGraph?: boolean;
} }
export interface SearchOptions { export interface SearchOptions {
@@ -29,7 +28,6 @@ export interface SearchOptions {
keyword?: boolean; keyword?: boolean;
filters?: Record<string, unknown>; filters?: Record<string, unknown>;
fields?: string[]; fields?: string[];
enableGraph?: boolean;
} }
export interface ListOptions { export interface ListOptions {
@@ -42,7 +40,6 @@ export interface ListOptions {
category?: string; category?: string;
after?: string; after?: string;
before?: string; before?: string;
enableGraph?: boolean;
} }
export interface DeleteOptions { export interface DeleteOptions {
+3 -6
View File
@@ -115,10 +115,9 @@ export class PlatformBackend implements Backend {
if (opts.infer === false) payload.infer = false; if (opts.infer === false) payload.infer = false;
if (opts.expires) payload.expiration_date = opts.expires; if (opts.expires) payload.expiration_date = opts.expires;
if (opts.categories) payload.categories = opts.categories; if (opts.categories) payload.categories = opts.categories;
if (opts.enableGraph) payload.enable_graph = true;
payload.source = "CLI"; payload.source = "CLI";
return (await this._request("POST", "/v1/memories/", { return (await this._request("POST", "/v3/memories/add/", {
json: payload, json: payload,
})) as Record<string, unknown>; })) as Record<string, unknown>;
} }
@@ -176,10 +175,9 @@ export class PlatformBackend implements Backend {
if (opts.rerank) payload.rerank = true; if (opts.rerank) payload.rerank = true;
if (opts.keyword) payload.keyword_search = true; if (opts.keyword) payload.keyword_search = true;
if (opts.fields) payload.fields = opts.fields; if (opts.fields) payload.fields = opts.fields;
if (opts.enableGraph) payload.enable_graph = true;
payload.source = "CLI"; payload.source = "CLI";
const result = (await this._request("POST", "/v2/memories/search/", { const result = (await this._request("POST", "/v3/memories/search/", {
json: payload, json: payload,
})) as unknown; })) as unknown;
if (Array.isArray(result)) return result; if (Array.isArray(result)) return result;
@@ -227,10 +225,9 @@ export class PlatformBackend implements Backend {
extraFilters: Object.keys(extra).length > 0 ? extra : undefined, extraFilters: Object.keys(extra).length > 0 ? extra : undefined,
}); });
if (apiFilters) payload.filters = apiFilters; if (apiFilters) payload.filters = apiFilters;
if (opts.enableGraph) payload.enable_graph = true;
payload.source = "CLI"; payload.source = "CLI";
const result = (await this._request("POST", "/v2/memories/", { const result = (await this._request("POST", "/v3/memories/", {
json: payload, json: payload,
params, params,
})) as unknown; })) as unknown;
-2
View File
@@ -29,7 +29,6 @@ export function cmdConfigShow(opts: { output?: string } = {}): void {
agent_id: config.defaults.agentId || null, agent_id: config.defaults.agentId || null,
app_id: config.defaults.appId || null, app_id: config.defaults.appId || null,
run_id: config.defaults.runId || null, run_id: config.defaults.runId || null,
enable_graph: config.defaults.enableGraph,
}, },
platform: { platform: {
api_key: redactKey(config.platform.apiKey), api_key: redactKey(config.platform.apiKey),
@@ -56,7 +55,6 @@ export function cmdConfigShow(opts: { output?: string } = {}): void {
]); ]);
table.push(["defaults.app_id", config.defaults.appId || dim("(not set)")]); table.push(["defaults.app_id", config.defaults.appId || dim("(not set)")]);
table.push(["defaults.run_id", config.defaults.runId || dim("(not set)")]); table.push(["defaults.run_id", config.defaults.runId || dim("(not set)")]);
table.push(["defaults.enable_graph", String(config.defaults.enableGraph)]);
table.push(["", ""]); table.push(["", ""]);
// Platform // Platform
-6
View File
@@ -49,7 +49,6 @@ export async function cmdAdd(
noInfer: boolean; noInfer: boolean;
expires?: string; expires?: string;
categories?: string; categories?: string;
enableGraph: boolean;
output: string; output: string;
}, },
): Promise<void> { ): Promise<void> {
@@ -140,7 +139,6 @@ export async function cmdAdd(
infer: !opts.noInfer, infer: !opts.noInfer,
expires: opts.expires, expires: opts.expires,
categories: cats, categories: cats,
enableGraph: opts.enableGraph,
}); });
}); });
} catch (e) { } catch (e) {
@@ -225,7 +223,6 @@ export async function cmdSearch(
keyword: boolean; keyword: boolean;
filterJson?: string; filterJson?: string;
fields?: string; fields?: string;
enableGraph: boolean;
output: string; output: string;
}, },
): Promise<void> { ): Promise<void> {
@@ -274,7 +271,6 @@ export async function cmdSearch(
keyword: opts.keyword, keyword: opts.keyword,
filters, filters,
fields: fieldList, fields: fieldList,
enableGraph: opts.enableGraph,
}); });
}); });
} catch (e) { } catch (e) {
@@ -368,7 +364,6 @@ export async function cmdList(
category?: string; category?: string;
after?: string; after?: string;
before?: string; before?: string;
enableGraph: boolean;
output: string; output: string;
}, },
): Promise<void> { ): Promise<void> {
@@ -396,7 +391,6 @@ export async function cmdList(
category: opts.category, category: opts.category,
after: opts.after, after: opts.after,
before: opts.before, before: opts.before,
enableGraph: opts.enableGraph,
}); });
}); });
} catch (e) { } catch (e) {
-13
View File
@@ -28,7 +28,6 @@ export interface DefaultsConfig {
agentId: string; agentId: string;
appId: string; appId: string;
runId: string; runId: string;
enableGraph: boolean;
} }
export interface TelemetryConfig { export interface TelemetryConfig {
@@ -50,7 +49,6 @@ export function createDefaultConfig(): Mem0Config {
agentId: "", agentId: "",
appId: "", appId: "",
runId: "", runId: "",
enableGraph: false,
}, },
platform: { platform: {
apiKey: "", apiKey: "",
@@ -87,8 +85,6 @@ export function loadConfig(): Mem0Config {
config.defaults.agentId = defaults.agent_id ?? ""; config.defaults.agentId = defaults.agent_id ?? "";
config.defaults.appId = defaults.app_id ?? ""; config.defaults.appId = defaults.app_id ?? "";
config.defaults.runId = defaults.run_id ?? ""; config.defaults.runId = defaults.run_id ?? "";
config.defaults.enableGraph = defaults.enable_graph ?? false;
const telemetry = data.telemetry ?? {}; const telemetry = data.telemetry ?? {};
config.telemetry.anonymousId = telemetry.anonymous_id ?? ""; config.telemetry.anonymousId = telemetry.anonymous_id ?? "";
} }
@@ -104,12 +100,6 @@ export function loadConfig(): Mem0Config {
config.defaults.agentId = process.env.MEM0_AGENT_ID; config.defaults.agentId = process.env.MEM0_AGENT_ID;
if (process.env.MEM0_APP_ID) config.defaults.appId = process.env.MEM0_APP_ID; if (process.env.MEM0_APP_ID) config.defaults.appId = process.env.MEM0_APP_ID;
if (process.env.MEM0_RUN_ID) config.defaults.runId = process.env.MEM0_RUN_ID; if (process.env.MEM0_RUN_ID) config.defaults.runId = process.env.MEM0_RUN_ID;
if (process.env.MEM0_ENABLE_GRAPH) {
config.defaults.enableGraph = ["true", "1", "yes"].includes(
process.env.MEM0_ENABLE_GRAPH.toLowerCase(),
);
}
return config; return config;
} }
@@ -123,7 +113,6 @@ export function saveConfig(config: Mem0Config): void {
agent_id: config.defaults.agentId, agent_id: config.defaults.agentId,
app_id: config.defaults.appId, app_id: config.defaults.appId,
run_id: config.defaults.runId, run_id: config.defaults.runId,
enable_graph: config.defaults.enableGraph,
}, },
platform: { platform: {
api_key: config.platform.apiKey, api_key: config.platform.apiKey,
@@ -154,7 +143,6 @@ const KEY_MAP: Record<string, [keyof Mem0Config, string]> = {
"defaults.agent_id": ["defaults", "agentId"], "defaults.agent_id": ["defaults", "agentId"],
"defaults.app_id": ["defaults", "appId"], "defaults.app_id": ["defaults", "appId"],
"defaults.run_id": ["defaults", "runId"], "defaults.run_id": ["defaults", "runId"],
"defaults.enable_graph": ["defaults", "enableGraph"],
// Short-form aliases // Short-form aliases
api_key: ["platform", "apiKey"], api_key: ["platform", "apiKey"],
base_url: ["platform", "baseUrl"], base_url: ["platform", "baseUrl"],
@@ -163,7 +151,6 @@ const KEY_MAP: Record<string, [keyof Mem0Config, string]> = {
agent_id: ["defaults", "agentId"], agent_id: ["defaults", "agentId"],
app_id: ["defaults", "appId"], app_id: ["defaults", "appId"],
run_id: ["defaults", "runId"], run_id: ["defaults", "runId"],
enable_graph: ["defaults", "enableGraph"],
}; };
export function getNestedValue(config: Mem0Config, dottedKey: string): unknown { export function getNestedValue(config: Mem0Config, dottedKey: string): unknown {
+1 -24
View File
@@ -134,18 +134,6 @@ function resolveIds(
}; };
} }
/**
* Resolve graph tri-state: --no-graph > --graph > config default.
*/
function resolveGraph(
config: Mem0Config,
opts: { graph?: boolean; noGraph?: boolean },
): boolean {
if (opts.noGraph) return false;
if (opts.graph) return true;
return config.defaults.enableGraph;
}
// ── Main program ────────────────────────────────────────────────────────── // ── Main program ──────────────────────────────────────────────────────────
program program
@@ -236,8 +224,6 @@ program
.option("--no-infer", "Skip inference, store raw.") .option("--no-infer", "Skip inference, store raw.")
.option("--expires <date>", "Expiration date (YYYY-MM-DD).") .option("--expires <date>", "Expiration date (YYYY-MM-DD).")
.option("--categories <value>", "Categories (JSON array or comma-separated).") .option("--categories <value>", "Categories (JSON array or comma-separated).")
.option("--graph", "Enable graph memory extraction.", false)
.option("--no-graph", "Disable graph memory extraction.")
.option("-o, --output <format>", "Output format: text, json, quiet.", "text") .option("-o, --output <format>", "Output format: text, json, quiet.", "text")
.option("--api-key <key>", "Override API key.") .option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.") .option("--base-url <url>", "Override API base URL.")
@@ -253,9 +239,8 @@ program
opts.baseUrl, opts.baseUrl,
); );
const ids = resolveIds(config, opts); const ids = resolveIds(config, opts);
const enableGraph = resolveGraph(config, opts);
const output = isAgent ? "agent" : opts.output; const output = isAgent ? "agent" : opts.output;
await cmdAdd(backend, text, { ...ids, ...opts, enableGraph, output }); await cmdAdd(backend, text, { ...ids, ...opts, output });
}); });
// ── Memory: search ──────────────────────────────────────────────────────── // ── Memory: search ────────────────────────────────────────────────────────
@@ -285,8 +270,6 @@ program
.option("--keyword", "Use keyword search.", false) .option("--keyword", "Use keyword search.", false)
.option("--filter <json>", "Advanced filter expression (JSON).") .option("--filter <json>", "Advanced filter expression (JSON).")
.option("--fields <list>", "Specific fields to return (comma-separated).") .option("--fields <list>", "Specific fields to return (comma-separated).")
.option("--graph", "Enable graph in search.", false)
.option("--no-graph", "Disable graph in search.")
.option("-o, --output <format>", "Output: text, json, table.", "text") .option("-o, --output <format>", "Output: text, json, table.", "text")
.option("--api-key <key>", "Override API key.") .option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.") .option("--base-url <url>", "Override API base URL.")
@@ -310,7 +293,6 @@ program
opts.baseUrl, opts.baseUrl,
); );
const ids = resolveIds(config, opts); const ids = resolveIds(config, opts);
const enableGraph = resolveGraph(config, opts);
const output = isAgent ? "agent" : opts.output; const output = isAgent ? "agent" : opts.output;
await cmdSearch(backend, resolvedQuery, { await cmdSearch(backend, resolvedQuery, {
...ids, ...ids,
@@ -320,7 +302,6 @@ program
keyword: opts.keyword, keyword: opts.keyword,
filterJson: opts.filter, filterJson: opts.filter,
fields: opts.fields, fields: opts.fields,
enableGraph,
output, output,
}); });
}); });
@@ -364,8 +345,6 @@ program
.option("--category <name>", "Filter by category.") .option("--category <name>", "Filter by category.")
.option("--after <date>", "Created after (YYYY-MM-DD).") .option("--after <date>", "Created after (YYYY-MM-DD).")
.option("--before <date>", "Created before (YYYY-MM-DD).") .option("--before <date>", "Created before (YYYY-MM-DD).")
.option("--graph", "Enable graph in listing.", false)
.option("--no-graph", "Disable graph in listing.")
.option("-o, --output <format>", "Output: text, json, table.", "table") .option("-o, --output <format>", "Output: text, json, table.", "table")
.option("--api-key <key>", "Override API key.") .option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.") .option("--base-url <url>", "Override API base URL.")
@@ -381,7 +360,6 @@ program
opts.baseUrl, opts.baseUrl,
); );
const ids = resolveIds(config, opts); const ids = resolveIds(config, opts);
const enableGraph = resolveGraph(config, opts);
const output = isAgent ? "agent" : opts.output; const output = isAgent ? "agent" : opts.output;
await cmdList(backend, { await cmdList(backend, {
...ids, ...ids,
@@ -390,7 +368,6 @@ program
category: opts.category, category: opts.category,
after: opts.after, after: opts.after,
before: opts.before, before: opts.before,
enableGraph,
output, output,
}); });
}); });
+6 -6
View File
@@ -107,22 +107,22 @@ describe("CLI Integration — help and version", () => {
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
}); });
it("add help has --graph flag", () => { it("add help has --output flag", () => {
const result = run(["add", "--help"]); const result = run(["add", "--help"]);
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--graph"); expect(result.stdout).toContain("--output");
}); });
it("search help has --graph flag", () => { it("search help has --rerank flag", () => {
const result = run(["search", "--help"]); const result = run(["search", "--help"]);
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--graph"); expect(result.stdout).toContain("--rerank");
}); });
it("list help has --graph flag", () => { it("list help has --category flag", () => {
const result = run(["list", "--help"]); const result = run(["list", "--help"]);
expect(result.exitCode).toBe(0); expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--graph"); expect(result.stdout).toContain("--category");
}); });
}); });
+15 -15
View File
@@ -42,7 +42,7 @@ describe("cmdAdd", () => {
userId: "alice", userId: "alice",
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "text", output: "text",
}); });
expect(mockBackend.add).toHaveBeenCalledOnce(); expect(mockBackend.add).toHaveBeenCalledOnce();
@@ -55,7 +55,7 @@ describe("cmdAdd", () => {
messages: JSON.stringify([{ role: "user", content: "I love Python" }]), messages: JSON.stringify([{ role: "user", content: "I love Python" }]),
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "text", output: "text",
}); });
expect(mockBackend.add).toHaveBeenCalledOnce(); expect(mockBackend.add).toHaveBeenCalledOnce();
@@ -67,7 +67,7 @@ describe("cmdAdd", () => {
userId: "alice", userId: "alice",
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "json", output: "json",
}); });
expect(output).toContain("results"); expect(output).toContain("results");
@@ -79,7 +79,7 @@ describe("cmdAdd", () => {
userId: "alice", userId: "alice",
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "quiet", output: "quiet",
}); });
expect(output).not.toContain("dark mode"); expect(output).not.toContain("dark mode");
@@ -101,7 +101,7 @@ describe("cmdAdd deduplicates PENDING", () => {
userId: "alice", userId: "alice",
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "text", output: "text",
}); });
expect(output.match(/Queued/g)?.length).toBe(1); expect(output.match(/Queued/g)?.length).toBe(1);
@@ -114,7 +114,7 @@ describe("cmdAdd deduplicates PENDING", () => {
userId: "alice", userId: "alice",
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "json", output: "json",
}); });
const data = JSON.parse(output); const data = JSON.parse(output);
@@ -130,7 +130,7 @@ describe("cmdAdd deduplicates PENDING", () => {
userId: "alice", userId: "alice",
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "agent", output: "agent",
}); });
const data = JSON.parse(output); const data = JSON.parse(output);
@@ -148,7 +148,7 @@ describe("cmdSearch", () => {
threshold: 0.3, threshold: 0.3,
rerank: false, rerank: false,
keyword: false, keyword: false,
enableGraph: false,
output: "text", output: "text",
}); });
expect(output).toContain("Found 2"); expect(output).toContain("Found 2");
@@ -162,7 +162,7 @@ describe("cmdSearch", () => {
threshold: 0.3, threshold: 0.3,
rerank: false, rerank: false,
keyword: false, keyword: false,
enableGraph: false,
output: "json", output: "json",
}); });
expect(output).toContain("memory"); expect(output).toContain("memory");
@@ -177,7 +177,7 @@ describe("cmdSearch", () => {
threshold: 0.3, threshold: 0.3,
rerank: false, rerank: false,
keyword: false, keyword: false,
enableGraph: false,
output: "text", output: "text",
}); });
expect(errOutput).toContain("No memories found"); expect(errOutput).toContain("No memories found");
@@ -205,7 +205,7 @@ describe("cmdList", () => {
userId: "alice", userId: "alice",
page: 1, page: 1,
pageSize: 100, pageSize: 100,
enableGraph: false,
output: "table", output: "table",
}); });
expect(output).toContain("dark mode"); expect(output).toContain("dark mode");
@@ -218,7 +218,7 @@ describe("cmdList", () => {
userId: "alice", userId: "alice",
page: 1, page: 1,
pageSize: 100, pageSize: 100,
enableGraph: false,
output: "text", output: "text",
}); });
expect(errOutput).toContain("No memories found"); expect(errOutput).toContain("No memories found");
@@ -316,7 +316,7 @@ describe("agent mode", () => {
userId: "alice", userId: "alice",
immutable: false, immutable: false,
noInfer: false, noInfer: false,
enableGraph: false,
output: "agent", output: "agent",
}); });
const parsed = JSON.parse(output.trim()); const parsed = JSON.parse(output.trim());
@@ -336,7 +336,7 @@ describe("agent mode", () => {
threshold: 0.3, threshold: 0.3,
rerank: false, rerank: false,
keyword: false, keyword: false,
enableGraph: false,
output: "agent", output: "agent",
}); });
const parsed = JSON.parse(output.trim()); const parsed = JSON.parse(output.trim());
@@ -361,7 +361,7 @@ describe("agent mode", () => {
userId: "alice", userId: "alice",
page: 1, page: 1,
pageSize: 100, pageSize: 100,
enableGraph: false,
output: "agent", output: "agent",
}); });
const parsed = JSON.parse(output.trim()); const parsed = JSON.parse(output.trim());
-6
View File
@@ -64,7 +64,6 @@ describe("createDefaultConfig", () => {
expect(config.platform.baseUrl).toBe("https://api.mem0.ai"); expect(config.platform.baseUrl).toBe("https://api.mem0.ai");
expect(config.platform.apiKey).toBe(""); expect(config.platform.apiKey).toBe("");
expect(config.defaults.userId).toBe(""); expect(config.defaults.userId).toBe("");
expect(config.defaults.enableGraph).toBe(false);
}); });
}); });
@@ -105,9 +104,4 @@ describe("setNestedValue", () => {
expect(config.defaults.userId).toBe("bob"); expect(config.defaults.userId).toBe("bob");
}); });
it("coerces boolean for enable_graph", () => {
const config = createDefaultConfig();
expect(setNestedValue(config, "defaults.enable_graph", "true")).toBe(true);
expect(config.defaults.enableGraph).toBe(true);
});
}); });
+1 -1
View File
@@ -4,7 +4,7 @@ build-backend = "hatchling.build"
[project] [project]
name = "mem0-cli" name = "mem0-cli"
version = "0.2.3" version = "0.2.4"
description = "The official CLI for mem0 — the memory layer for AI agents" description = "The official CLI for mem0 — the memory layer for AI agents"
readme = "README.md" readme = "README.md"
license = "Apache-2.0" license = "Apache-2.0"
+1 -1
View File
@@ -1,3 +1,3 @@
"""mem0 CLI — the command-line interface for the mem0 memory layer.""" """mem0 CLI — the command-line interface for the mem0 memory layer."""
__version__ = "0.2.3" __version__ = "0.2.4"
-38
View File
@@ -267,8 +267,6 @@ def add(
categories: str | None = typer.Option( categories: str | None = typer.Option(
None, "--categories", help="Categories (JSON array or comma-separated)." None, "--categories", help="Categories (JSON array or comma-separated)."
), ),
graph: bool = typer.Option(False, "--graph", help="Enable graph memory extraction."),
no_graph: bool = typer.Option(False, "--no-graph", help="Disable graph memory extraction."),
output: str = typer.Option( output: str = typer.Option(
"text", "--output", "-o", help="Output format: text, json, quiet.", rich_help_panel="Output" "text", "--output", "-o", help="Output format: text, json, quiet.", rich_help_panel="Output"
), ),
@@ -295,13 +293,6 @@ def add(
backend, config = _get_backend_and_config(api_key, base_url) backend, config = _get_backend_and_config(api_key, base_url)
ids = _resolve_ids(config, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id) ids = _resolve_ids(config, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id)
if no_graph:
graph_enabled = False
elif graph:
graph_enabled = True
else:
graph_enabled = config.defaults.enable_graph
cmd_add( cmd_add(
backend, backend,
text, text,
@@ -313,7 +304,6 @@ def add(
no_infer=no_infer, no_infer=no_infer,
expires=expires, expires=expires,
categories=categories, categories=categories,
enable_graph=graph_enabled,
output=output, output=output,
) )
@@ -357,12 +347,6 @@ def search(
help="Specific fields to return (comma-separated).", help="Specific fields to return (comma-separated).",
rich_help_panel="Search", rich_help_panel="Search",
), ),
graph: bool = typer.Option(
False, "--graph", help="Enable graph in search.", rich_help_panel="Search"
),
no_graph: bool = typer.Option(
False, "--no-graph", help="Disable graph in search.", rich_help_panel="Search"
),
output: str = typer.Option( output: str = typer.Option(
"text", "--output", "-o", help="Output: text, json, table.", rich_help_panel="Output" "text", "--output", "-o", help="Output: text, json, table.", rich_help_panel="Output"
), ),
@@ -396,13 +380,6 @@ def search(
backend, config = _get_backend_and_config(api_key, base_url) backend, config = _get_backend_and_config(api_key, base_url)
ids = _resolve_ids(config, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id) ids = _resolve_ids(config, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id)
if no_graph:
graph_enabled = False
elif graph:
graph_enabled = True
else:
graph_enabled = config.defaults.enable_graph
cmd_search( cmd_search(
backend, backend,
query, query,
@@ -413,7 +390,6 @@ def search(
keyword=keyword, keyword=keyword,
filter_json=filter_json, filter_json=filter_json,
fields=fields, fields=fields,
enable_graph=graph_enabled,
output=output, output=output,
) )
@@ -480,12 +456,6 @@ def list_cmd(
before: str | None = typer.Option( before: str | None = typer.Option(
None, "--before", help="Created before (YYYY-MM-DD).", rich_help_panel="Filters" None, "--before", help="Created before (YYYY-MM-DD).", rich_help_panel="Filters"
), ),
graph: bool = typer.Option(
False, "--graph", help="Enable graph in listing.", rich_help_panel="Filters"
),
no_graph: bool = typer.Option(
False, "--no-graph", help="Disable graph in listing.", rich_help_panel="Filters"
),
output: str = typer.Option( output: str = typer.Option(
"table", "--output", "-o", help="Output: text, json, table.", rich_help_panel="Output" "table", "--output", "-o", help="Output: text, json, table.", rich_help_panel="Output"
), ),
@@ -511,13 +481,6 @@ def list_cmd(
backend, config = _get_backend_and_config(api_key, base_url) backend, config = _get_backend_and_config(api_key, base_url)
ids = _resolve_ids(config, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id) ids = _resolve_ids(config, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id)
if no_graph:
graph_enabled = False
elif graph:
graph_enabled = True
else:
graph_enabled = config.defaults.enable_graph
cmd_list( cmd_list(
backend, backend,
**ids, **ids,
@@ -526,7 +489,6 @@ def list_cmd(
category=category, category=category,
after=after, after=after,
before=before, before=before,
enable_graph=graph_enabled,
output=output, output=output,
) )
-3
View File
@@ -26,7 +26,6 @@ class Backend(ABC):
infer: bool = True, infer: bool = True,
expires: str | None = None, expires: str | None = None,
categories: list[str] | None = None, categories: list[str] | None = None,
enable_graph: bool = False,
) -> dict: ... ) -> dict: ...
@abstractmethod @abstractmethod
@@ -44,7 +43,6 @@ class Backend(ABC):
keyword: bool = False, keyword: bool = False,
filters: dict | None = None, filters: dict | None = None,
fields: list[str] | None = None, fields: list[str] | None = None,
enable_graph: bool = False,
) -> list[dict]: ... ) -> list[dict]: ...
@abstractmethod @abstractmethod
@@ -63,7 +61,6 @@ class Backend(ABC):
category: str | None = None, category: str | None = None,
after: str | None = None, after: str | None = None,
before: str | None = None, before: str | None = None,
enable_graph: bool = False,
) -> list[dict]: ... ) -> list[dict]: ...
@abstractmethod @abstractmethod
+5 -14
View File
@@ -64,7 +64,6 @@ class PlatformBackend(Backend):
infer: bool = True, infer: bool = True,
expires: str | None = None, expires: str | None = None,
categories: list[str] | None = None, categories: list[str] | None = None,
enable_graph: bool = False,
) -> dict: ) -> dict:
payload: dict[str, Any] = {} payload: dict[str, Any] = {}
@@ -91,11 +90,9 @@ class PlatformBackend(Backend):
payload["expiration_date"] = expires payload["expiration_date"] = expires
if categories: if categories:
payload["categories"] = categories payload["categories"] = categories
if enable_graph:
payload["enable_graph"] = True
payload["source"] = "CLI" payload["source"] = "CLI"
return self._request("POST", "/v1/memories/", json=payload) return self._request("POST", "/v3/memories/add/", json=payload)
def _build_filters( def _build_filters(
self, self,
@@ -106,7 +103,7 @@ class PlatformBackend(Backend):
run_id: str | None = None, run_id: str | None = None,
extra_filters: dict | None = None, extra_filters: dict | None = None,
) -> dict | None: ) -> dict | None:
"""Build a filters dict for v2 API endpoints. """Build a filters dict for v3 API endpoints.
Entity IDs are ANDed (all provided IDs must match). Entity IDs are ANDed (all provided IDs must match).
Extra filters (date ranges, categories) are also ANDed. Extra filters (date ranges, categories) are also ANDed.
@@ -152,7 +149,6 @@ class PlatformBackend(Backend):
keyword: bool = False, keyword: bool = False,
filters: dict | None = None, filters: dict | None = None,
fields: list[str] | None = None, fields: list[str] | None = None,
enable_graph: bool = False,
) -> list[dict]: ) -> list[dict]:
payload: dict[str, Any] = {"query": query, "top_k": top_k, "threshold": threshold} payload: dict[str, Any] = {"query": query, "top_k": top_k, "threshold": threshold}
@@ -171,11 +167,9 @@ class PlatformBackend(Backend):
payload["keyword_search"] = True payload["keyword_search"] = True
if fields: if fields:
payload["fields"] = fields payload["fields"] = fields
if enable_graph:
payload["enable_graph"] = True
payload["source"] = "CLI" payload["source"] = "CLI"
result = self._request("POST", "/v2/memories/search/", json=payload) result = self._request("POST", "/v3/memories/search/", json=payload)
return ( return (
result result
if isinstance(result, list) if isinstance(result, list)
@@ -197,12 +191,11 @@ class PlatformBackend(Backend):
category: str | None = None, category: str | None = None,
after: str | None = None, after: str | None = None,
before: str | None = None, before: str | None = None,
enable_graph: bool = False,
) -> list[dict]: ) -> list[dict]:
payload: dict[str, Any] = {} payload: dict[str, Any] = {}
params = {"page": str(page), "page_size": str(page_size)} params = {"page": str(page), "page_size": str(page_size)}
# Build filters for v2 API — entity IDs and date filters go inside "filters" # Build filters — entity IDs and date filters go inside "filters"
extra: dict[str, Any] = {} extra: dict[str, Any] = {}
if category: if category:
extra["categories"] = {"contains": category} extra["categories"] = {"contains": category}
@@ -220,11 +213,9 @@ class PlatformBackend(Backend):
) )
if api_filters: if api_filters:
payload["filters"] = api_filters payload["filters"] = api_filters
if enable_graph:
payload["enable_graph"] = True
payload["source"] = "CLI" payload["source"] = "CLI"
result = self._request("POST", "/v2/memories/", json=payload, params=params) result = self._request("POST", "/v3/memories/", json=payload, params=params)
return ( return (
result result
if isinstance(result, list) if isinstance(result, list)
@@ -39,7 +39,6 @@ def cmd_config_show(*, output: str = "text") -> None:
"agent_id": config.defaults.agent_id or None, "agent_id": config.defaults.agent_id or None,
"app_id": config.defaults.app_id or None, "app_id": config.defaults.app_id or None,
"run_id": config.defaults.run_id or None, "run_id": config.defaults.run_id or None,
"enable_graph": config.defaults.enable_graph,
}, },
"platform": { "platform": {
"api_key": redact_key(config.platform.api_key), "api_key": redact_key(config.platform.api_key),
@@ -73,10 +72,6 @@ def cmd_config_show(*, output: str = "text") -> None:
"defaults.run_id", "defaults.run_id",
config.defaults.run_id or f"[{DIM_COLOR}](not set)[/]", config.defaults.run_id or f"[{DIM_COLOR}](not set)[/]",
) )
table.add_row(
"defaults.enable_graph",
str(config.defaults.enable_graph).lower(),
)
table.add_row("", "") table.add_row("", "")
# Platform # Platform
@@ -62,7 +62,6 @@ def cmd_add(
no_infer: bool, no_infer: bool,
expires: str | None, expires: str | None,
categories: str | None, categories: str | None,
enable_graph: bool = False,
output: str = "text", output: str = "text",
) -> None: ) -> None:
"""Add a memory.""" """Add a memory."""
@@ -145,7 +144,6 @@ def cmd_add(
infer=not no_infer, infer=not no_infer,
expires=expires, expires=expires,
categories=cats, categories=cats,
enable_graph=enable_graph,
) )
except Exception as e: except Exception as e:
ts.error_msg = str(e) ts.error_msg = str(e)
@@ -226,7 +224,6 @@ def cmd_search(
keyword: bool, keyword: bool,
filter_json: str | None, filter_json: str | None,
fields: str | None, fields: str | None,
enable_graph: bool = False,
output: str = "text", output: str = "text",
) -> None: ) -> None:
"""Search memories.""" """Search memories."""
@@ -269,7 +266,6 @@ def cmd_search(
keyword=keyword, keyword=keyword,
filters=filters, filters=filters,
fields=field_list, fields=field_list,
enable_graph=enable_graph,
) )
except Exception as e: except Exception as e:
print_error(err_console, str(e)) print_error(err_console, str(e))
@@ -356,7 +352,6 @@ def cmd_list(
category: str | None, category: str | None,
after: str | None, after: str | None,
before: str | None, before: str | None,
enable_graph: bool = False,
output: str = "table", output: str = "table",
) -> None: ) -> None:
"""List memories.""" """List memories."""
@@ -385,7 +380,6 @@ def cmd_list(
category=category, category=category,
after=after, after=after,
before=before, before=before,
enable_graph=enable_graph,
) )
except Exception as e: except Exception as e:
print_error(err_console, str(e)) print_error(err_console, str(e))
-9
View File
@@ -36,7 +36,6 @@ class DefaultsConfig:
agent_id: str = "" agent_id: str = ""
app_id: str = "" app_id: str = ""
run_id: str = "" run_id: str = ""
enable_graph: bool = False
@dataclass @dataclass
@@ -60,7 +59,6 @@ SHORT_KEY_ALIASES: dict[str, str] = {
"agent_id": "defaults.agent_id", "agent_id": "defaults.agent_id",
"app_id": "defaults.app_id", "app_id": "defaults.app_id",
"run_id": "defaults.run_id", "run_id": "defaults.run_id",
"enable_graph": "defaults.enable_graph",
} }
@@ -91,8 +89,6 @@ def load_config() -> Mem0Config:
config.defaults.agent_id = defaults.get("agent_id", "") config.defaults.agent_id = defaults.get("agent_id", "")
config.defaults.app_id = defaults.get("app_id", "") config.defaults.app_id = defaults.get("app_id", "")
config.defaults.run_id = defaults.get("run_id", "") config.defaults.run_id = defaults.get("run_id", "")
config.defaults.enable_graph = defaults.get("enable_graph", False)
telemetry = data.get("telemetry", {}) telemetry = data.get("telemetry", {})
config.telemetry.anonymous_id = telemetry.get("anonymous_id", "") config.telemetry.anonymous_id = telemetry.get("anonymous_id", "")
@@ -121,10 +117,6 @@ def load_config() -> Mem0Config:
if env_run_id: if env_run_id:
config.defaults.run_id = env_run_id config.defaults.run_id = env_run_id
env_graph = os.environ.get("MEM0_ENABLE_GRAPH")
if env_graph:
config.defaults.enable_graph = env_graph.lower() in ("true", "1", "yes")
return config return config
@@ -139,7 +131,6 @@ def save_config(config: Mem0Config) -> None:
"agent_id": config.defaults.agent_id, "agent_id": config.defaults.agent_id,
"app_id": config.defaults.app_id, "app_id": config.defaults.app_id,
"run_id": config.defaults.run_id, "run_id": config.defaults.run_id,
"enable_graph": config.defaults.enable_graph,
}, },
"platform": { "platform": {
"api_key": config.platform.api_key, "api_key": config.platform.api_key,
+2 -13
View File
@@ -224,24 +224,13 @@ class TestCLIIsolated:
class TestCLINewFeatures: class TestCLINewFeatures:
"""Tests for MCP parity features: --graph, --limit, entities delete.""" """Tests for MCP parity features: --limit, entities delete."""
def test_add_help_has_graph(self): def test_search_help_has_limit(self):
result = _run(["add", "--help"])
assert result.returncode == 0
assert "--graph" in result.stdout
def test_search_help_has_graph_and_limit(self):
result = _run(["search", "--help"]) result = _run(["search", "--help"])
assert result.returncode == 0 assert result.returncode == 0
assert "--graph" in result.stdout
assert "--limit" in result.stdout assert "--limit" in result.stdout
def test_list_help_has_graph(self):
result = _run(["list", "--help"])
assert result.returncode == 0
assert "--graph" in result.stdout
def test_delete_entity_via_delete_flag(self): def test_delete_entity_via_delete_flag(self):
"""delete --entity should appear in help output.""" """delete --entity should appear in help output."""
result = _run(["delete", "--help"]) result = _run(["delete", "--help"])
-79
View File
@@ -997,85 +997,6 @@ class TestEntitiesDeleteCommand:
mock_backend.delete_entities.assert_not_called() mock_backend.delete_entities.assert_not_called()
class TestEnableGraph:
def test_add_with_graph(self, mock_backend):
console, _buf = _make_console()
err_console, _err_buf = _make_err_console()
with (
patch("mem0_cli.commands.memory.console", console),
patch("mem0_cli.commands.memory.err_console", err_console),
):
cmd_add(
mock_backend,
"test",
user_id="alice",
agent_id=None,
app_id=None,
run_id=None,
messages=None,
file=None,
metadata=None,
immutable=False,
no_infer=False,
expires=None,
categories=None,
enable_graph=True,
output="text",
)
call_kwargs = mock_backend.add.call_args
assert call_kwargs.kwargs.get("enable_graph") is True
def test_search_with_graph(self, mock_backend):
console, _buf = _make_console()
err_console, _err_buf = _make_err_console()
with (
patch("mem0_cli.commands.memory.console", console),
patch("mem0_cli.commands.memory.err_console", err_console),
):
cmd_search(
mock_backend,
"test",
user_id="alice",
agent_id=None,
app_id=None,
run_id=None,
top_k=10,
threshold=0.3,
rerank=False,
keyword=False,
filter_json=None,
fields=None,
enable_graph=True,
output="text",
)
call_kwargs = mock_backend.search.call_args
assert call_kwargs.kwargs.get("enable_graph") is True
def test_list_with_graph(self, mock_backend):
console, _buf = _make_console()
err_console, _err_buf = _make_err_console()
with (
patch("mem0_cli.commands.memory.console", console),
patch("mem0_cli.commands.memory.err_console", err_console),
):
cmd_list(
mock_backend,
user_id="alice",
agent_id=None,
app_id=None,
run_id=None,
page=1,
page_size=100,
category=None,
after=None,
before=None,
enable_graph=True,
output="table",
)
call_kwargs = mock_backend.list_memories.call_args
assert call_kwargs.kwargs.get("enable_graph") is True
class TestEventCommands: class TestEventCommands:
def test_event_list_table(self, mock_backend): def test_event_list_table(self, mock_backend):
console, buf = _make_console() console, buf = _make_console()
-45
View File
@@ -121,46 +121,6 @@ class TestConfig:
assert config.defaults.agent_id == "" assert config.defaults.agent_id == ""
assert config.defaults.app_id == "" assert config.defaults.app_id == ""
assert config.defaults.run_id == "" assert config.defaults.run_id == ""
assert config.defaults.enable_graph is False
def test_enable_graph_save_and_load(self, isolate_config):
config = Mem0Config()
config.defaults.enable_graph = True
save_config(config)
loaded = load_config()
assert loaded.defaults.enable_graph is True
def test_enable_graph_env_var_true(self, isolate_config, monkeypatch):
monkeypatch.setenv("MEM0_ENABLE_GRAPH", "true")
loaded = load_config()
assert loaded.defaults.enable_graph is True
def test_enable_graph_env_var_false(self, isolate_config, monkeypatch):
config = Mem0Config()
config.defaults.enable_graph = True
save_config(config)
monkeypatch.setenv("MEM0_ENABLE_GRAPH", "false")
loaded = load_config()
assert loaded.defaults.enable_graph is False
def test_backward_compat_no_enable_graph_key(self, isolate_config):
"""Old config files without 'enable_graph' key should default to False."""
import json
from mem0_cli.config import CONFIG_FILE, ensure_config_dir
ensure_config_dir()
data = {
"version": 1,
"defaults": {"user_id": "alice"},
"platform": {"api_key": "m0-test", "base_url": "https://api.mem0.ai"},
}
with open(CONFIG_FILE, "w") as f:
json.dump(data, f)
loaded = load_config()
assert loaded.defaults.enable_graph is False
assert loaded.defaults.user_id == "alice"
class TestNestedAccess: class TestNestedAccess:
@@ -192,11 +152,6 @@ class TestNestedAccess:
assert set_nested_value(config, "defaults.user_id", "bob") assert set_nested_value(config, "defaults.user_id", "bob")
assert config.defaults.user_id == "bob" assert config.defaults.user_id == "bob"
def test_set_defaults_enable_graph(self):
config = Mem0Config()
assert set_nested_value(config, "defaults.enable_graph", "true")
assert config.defaults.enable_graph is True
class TestResolveIds: class TestResolveIds:
def test_cli_flag_overrides_default(self): def test_cli_flag_overrides_default(self):
+10
View File
@@ -1269,6 +1269,16 @@ See the [TypeScript SDK migration guide](https://docs.mem0.ai/migration/ts-v2-to
<Tab title="CLI"> <Tab title="CLI">
<Update label="2026-04-22" description="Python v0.2.4 / Node v0.2.4">
**New Features:**
- **V3 API Routes:** Migrated `add`, `search`, and `list` commands from v1/v2 to v3 API endpoints — `POST /v3/memories/add/`, `POST /v3/memories/search/`, `POST /v3/memories/`. Aligns both CLIs with the Python and TypeScript SDKs which already use v3 ([#4916](https://github.com/mem0ai/mem0/pull/4916))
**Breaking Changes:**
- **`--graph` / `--no-graph` removed:** The `enable_graph` config option, `--graph` and `--no-graph` CLI flags, and `MEM0_ENABLE_GRAPH` environment variable have been removed from both CLIs. Graph memory is now a project-level setting on the Platform ([#4916](https://github.com/mem0ai/mem0/pull/4916))
</Update>
<Update label="2026-04-11" description="Python v0.2.3 / Node v0.2.3"> <Update label="2026-04-11" description="Python v0.2.3 / Node v0.2.3">
**Bug Fixes:** **Bug Fixes:**