Compare commits

...

1395 Commits

Author SHA1 Message Date
Saket Aryan 5723136bed fix: add repository field to Node packages for npm provenance (#4671) 2026-04-02 16:20:38 +05:30
Saket Aryan b5345f8498 ci: add CD workflows for Node SDK packages with OIDC trusted publishing (#4670) 2026-04-02 16:11:06 +05:30
Chaithanya Kumar 6577ae7616 fix(openclaw): graceful startup without API key (#4669)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 16:10:49 +05:30
Saket Aryan 5e00d5c452 chore(cli): bump version for Python CLI to 0.2.0 and Node CLI to 0.1.1 (#4668) 2026-04-02 13:36:21 +05:30
Kartik 3b152a3e85 fix(openclaw): updating the config of the openclaw plugin here (#4667) 2026-04-02 13:30:03 +05:30
Chaithanya Kumar beca7cc873 fix(openclaw): dream gate correctness — cheap-first ordering, session isolation, verified completion (#4666)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 13:29:49 +05:30
Saket Aryan 30f242dc4c feat(cli): update brand color palette from purple to golden (#4664) 2026-04-02 04:03:28 +05:30
Kartik 1bfaaf8750 chore: release (#4657) 2026-04-01 23:44:09 +05:30
Chaithanya Kumar c250ccfb5c feat(openclaw): skills-based memory architecture with batched extraction (#4624)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-01 23:29:15 +05:30
Saket Aryan e2b439c42a fix(cd): restrict PyPI publish to main SDK tags only (#4654) 2026-04-01 21:42:50 +05:30
Saket Aryan c788d771d3 feat(cli): add CD workflow and bump version to 0.2.0b1 (#4653) 2026-04-01 21:29:10 +05:30
Saket Aryan 2acf9571b3 feat(cli): add event commands, --json/--agent flag, agent output sanitization, and edge-case hardening for CLI SDKs (#4649)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-01 21:04:59 +05:30
Saharsh Patel 713dba5d0a fix: replace .single() with .maybeSingle() in SupabaseDB.get() to handle missing rows (#4599) 2026-04-01 18:41:24 +05:30
wobushixiaoj 8ae7a06220 fix: pass dimensions parameter to OpenAI embeddings API (#4632)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-01 18:40:17 +05:30
Prithvi Monangi f94ea06588 fix(configs): migrate CassandraConfig and AzureMySQLConfig to pydantic v2 ConfigDict (#4646) 2026-04-01 15:02:22 +05:30
Noah Stapp 215d8b5a71 fix: only list authorized collections when listing MongoDB collections (#3888) 2026-03-31 23:09:36 +05:30
Genaro Sanchez 82525dbf0f docs: update Twitter references to X (formerly Twitter) (#4432) 2026-03-31 22:44:04 +05:30
Saket Aryan 32c1ccba5b ci(cli): add CI pipelines for Node and Python CLI SDKs (#4640)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-31 22:30:31 +05:30
Saket Aryan 9bebcf45f7 fix(cli): fix critical crashes, improve error messages, UX and validation in Node.js and Python SDKs (#4636) 2026-03-31 20:28:48 +05:30
Rakhee Singh 93bd4e248c fix(deepseek): forward response_format to OpenAI-compatible API (#4635) 2026-03-31 18:06:53 +05:30
Kabir Kohli af19495f66 feat(cli): add email verification code login to mem0 init (#4623)
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2026-03-31 16:57:02 +05:30
Rakhee Singh dbac83218f fix(vllm): forward response_format to OpenAI-compatible API (#4608)
Co-authored-by: rasingh5 <rasingh@demandbase.com>
2026-03-31 13:25:45 +05:30
Prathamesh 3618aeff22 Link fix (#4631) 2026-03-30 21:36:00 -07:00
Prathamesh 25e25aaa2f redesign docs introduction page with compact 3x2 grid layout (#4598) 2026-03-30 16:14:02 -07:00
HUANG XIAO 5d30af9560 feat(bedrock): add MiniMax provider support for AWS Bedrock (#4609) 2026-03-30 20:40:24 +05:30
Krishna Chaitanya 47969aaa6b fix(ts): extract JSON from chatty LLM responses in fact retrieval (#4533)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-30 20:33:11 +05:30
Br1an 7213e1b1a6 fix: reset graph database in Memory.reset() (#4185)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-30 18:45:38 +05:30
Br1an 97291aa119 fix: make AsyncMemory.from_config a regular classmethod (#4183) 2026-03-30 16:19:37 +05:30
Kartik b8a5ca1b70 chore: bump mem0ai and mem0-ts versions to 1.0.9/2.4.4 and update changelog (#4585) 2026-03-28 22:42:21 +05:30
Utkarsh 431cba20e9 fix(ts): work around Qdrant Cloud "Illegal host" error (#4565)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-28 21:50:36 +05:30
Kartik 4482228383 chore: fix failing CI lint errors (#4584) 2026-03-28 21:45:47 +05:30
Himanshu ae49edec44 fix(memory): centralize entity cleanup and skip malformed LLM relation dicts (#4515) 2026-03-28 21:23:05 +05:30
Kartik 4b7f51d194 docs: add installation and configuration guide for email automation with Mem0 open source (#4567) 2026-03-28 21:12:31 +05:30
Kartik 41abb571a2 feat: adding oss version of companion cookbook (#4564) 2026-03-28 21:11:59 +05:30
Anchi Li e280665578 fix: remove README.md from wheel shared-data (#4052) 2026-03-28 20:57:51 +05:30
Br1an 27a6e7863e fix: rebuild FAISS index on vector deletion (#4178) 2026-03-28 20:56:39 +05:30
Kartik ae8e03c6b7 docs: add content-writing cookbook to operations (#4566) 2026-03-28 20:52:54 +05:30
Dan Siwiec 376be3b6d4 [docs] fix python quickstart code snippet #3770 (#3771) 2026-03-28 20:40:10 +05:30
Kartik cd2dd7cc54 refactor: update default Gemini and Vertex AI embedder model to gemini-embedding-001 (#4571) 2026-03-28 20:39:27 +05:30
Agam Pandey ece654811a fix(docs): add Token prefix to Events API code examples (#3927) 2026-03-28 20:38:42 +05:30
Saket Aryan 13d42a99e9 docs: improve CLI dev workflow and prioritize Node.js installation (#4579) 2026-03-27 17:50:03 -07:00
Saket Aryan 3225e30859 feat: add official mem0 CLI (Python & TypeScript) (#4575) 2026-03-28 05:03:01 +05:30
Kartik 88fd0e77d0 docs: add navigation links for platform features in docs (#4572) 2026-03-28 01:43:44 +05:30
Kartik c2698f2b71 fix: preserve original actor_id during memory update (#4570) 2026-03-28 00:02:58 +05:30
Utkarsh e87240d4d9 fix(vector_stores): handle vector=None in Milvus and Qdrant update methods (#4568)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 21:50:54 +05:30
Gopal Bagaswar 68cf4e118d feat: add reasoning_effort parameter support for reasoning models (#4461) 2026-03-27 17:54:44 +05:30
Utkarsh 12624555b4 fix(memory): set updated_at on creation and preserve pre-existing created_at (#4499)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-27 16:59:44 +05:30
Kartik e8004b93db chore: updating the sdk version and changelog (#4561)
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-26 22:21:31 +05:30
Gabriel Stein 4e414e4015 fix(plugin): make Cursor plugin fully functional (#4547) 2026-03-26 09:46:42 -07:00
mintlify[bot] 16455789d4 Fix short SEO description in integration guide template (#4551) 2026-03-26 09:39:54 -07:00
Utkarsh 3ac4e047de fix: merge multiple filter operators for same key (#4559)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:28:37 +05:30
Utkarsh a2ffca3266 fix: prevent SQL injection in Databricks vector store (#4558)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:22:34 +05:30
Felippe Mercurio 3a9fcdbec2 fix(oss): make pgvector pg import compatible with ESM (#4544) 2026-03-26 20:07:39 +05:30
Utkarsh 515f87b6bd docs: fix OSS REST API endpoint discrepancies (#4555)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:05:23 +05:30
zaid khan 5edc0cc99f fix: update parameter added to the update function which is exposed t… (#3799)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 20:02:33 +05:30
Utkarsh 7fff26f374 docs: fix LLM reranker config examples and field names (#4539)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 19:58:05 +05:30
VictorECDSA 1aecfadf45 fix: add timestamps for DELETE operations in history (#4492) 2026-03-26 18:52:10 +05:30
Chaithanya Kumar 7e06aeeada fix(openclaw): improve credential detection in extraction instructions (#4552)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 17:28:18 +05:30
Utkarsh 2a59c9fd99 fix: handle chatty LLM responses in JSON parsing (#4525)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 16:39:17 +05:30
Chaithanya Kumar 669ed184e4 fix(openclaw): prevent extraction of standalone timestamps as memories (#4550)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-26 15:19:50 +05:30
Gabriel Stein 3c2683c1b5 feat: add Mem0 plugin for Claude Code and Cursor (#4518) 2026-03-25 14:45:59 -07:00
Himanshu f06e2d744d Fix/OpenAI embedding dimensions 4153 (#4481) 2026-03-25 19:59:28 +05:30
lamost423 f9e30304d7 fix: sanitize hyphens in Neo4j Cypher relationship names (#4154) 2026-03-25 19:58:27 +05:30
Varun Chawla 7e3b727528 Fix: prevent double embedding in mem0.add (fixes #3723) (#3996)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 18:14:48 +05:30
Lev Neiman 13c7f84eec MCP: add Streamable HTTP transport endpoint (#4122)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 16:36:37 +05:30
Utkarsh 924ac00c52 feat: expose infer param in MCP add_memories tool (#4517)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 15:53:50 +05:30
Utkarsh 2a36960f4c fix: prevent in-place mutation of metadata in _create_memory (#4529)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-25 13:56:30 +05:30
Himanshu 2e0f91e70d fix(bedrock): omit topP for Anthropic Converse; use AWSBedrockConfig in LlmFactory (#4469) 2026-03-25 11:22:52 +05:30
Saket Aryan d1b4b304c7 chore: replace local MCP and Smithery with cloud MCP server (#4532) 2026-03-25 04:58:00 +05:30
Saket Aryan 2868bfe749 docs: remove OpenMemory references from docs, README, and issue templates (#4520) 2026-03-25 04:24:13 +05:30
Kartik 5431badfd4 fix: preserve custom metadata when updating memory (#4495) 2026-03-24 10:58:21 +05:30
Utkarsh bda5b726bd fix: avoid sending both temperature and top_p to Anthropic API (#4471)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 21:00:41 +05:30
Kartik 16bcc91716 chore: remove benchmark submission issue template and label matcher (#4514) 2026-03-23 20:32:55 +05:30
Kartik ba63ea4528 docs: add Vibecoding guide with Mem0 integration (#4511) 2026-03-23 19:36:21 +05:30
Utkarsh 5332741961 fix: clean up graph store data on Memory.delete() (#4505)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:27:38 +05:30
Utkarsh d8a6960b4a fix: align Databricks docs with config and fix query mode selection (#4477)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 19:20:11 +05:30
Himanshu 316dc67a0a fix(ts-oss): register pgvector in VectorStoreFactory (#3367) (#4502) 2026-03-23 18:52:19 +05:30
Kartik 65156d5176 chore: update issue templates and workflows for improved labeling and formatting (#4501) 2026-03-23 16:34:51 +05:30
Kartik c5e8216362 docs: add issue templates for bug, feature, benchmark, documentation, and update contact links (#4500) 2026-03-23 15:37:23 +05:30
Himanshu ecedbc11d9 fix(qdrant): do not remove local path on init (#4473) (#4475) 2026-03-23 14:58:06 +05:30
Utkarsh 9aadfa3221 fix: add missing limit, threshold, infer, memory_type, and prompt params to REST API (#4496)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 14:48:13 +05:30
Br1an cc45561abd fix: accept default /tmp/chroma path in ChromaDbConfig validator (#4179) 2026-03-23 14:12:49 +05:30
Varun Chawla 7cebaba0a2 fix: upgrade MongoDB vector store from deprecated knnVector to GA vectorSearch (#3995)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-23 12:20:17 +05:30
mintlify[bot] 5dabf24809 Fix 4 broken placeholder links in template files (#4478) 2026-03-21 17:29:24 -07:00
Himanshu ec326f0f92 fix(mcp): operator precedence in search_memory filter (#4470) (#4474) 2026-03-21 21:27:35 +05:30
Kartik eb780f4880 refactor: add vector validation to OpenSearchDB to ensure non‑null, non‑empty, and correct‑dimension vectors (#4472) 2026-03-21 20:36:26 +05:30
Aditya Paul c39d5ada4d fix: Bug: Zod Schema Incompatible with OpenAI Structured Outputs API (#3462) 2026-03-21 19:48:31 +05:30
Utkarsh 06c25eb00b fix: use root LLM config as fallback for graph store instead of hardcoded OpenAI default (#4466)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:38:49 +05:30
longway 7a09663156 fix(qdrant): implement enhanced metadata filtering operators (#4127)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-03-21 19:37:26 +05:30
mintlify[bot] 267bcf2931 (docs): add missing SEO metadata to turbopuffer page (#4468)
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
2026-03-21 19:31:53 +05:30
Utkarsh bf9a5703b1 feat: integrate turbopuffer as vector database provider (#4428)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 19:28:25 +05:30
darrenxu 884e740b53 fix(graph): soft-delete graph relationships instead of hard DELETE (#4188)
Signed-off-by: sxu75374 <imshuaixu@gmail.com>
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-21 17:32:59 +05:30
Utkarsh 824032a81d feat: add NemoClaw + Mem0 plugin setup scripts and quickstart (#4464)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:52:42 +05:30
Kartik abdb07c204 fix: handle None content and empty candidates in GeminiLLM parsing (#4462) 2026-03-21 13:52:13 +05:30
Utkarsh 7b26df728d docs: add Claude Code setup instructions for OpenMemory (#4430)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-21 13:51:47 +05:30
Varun Chawla 30661ab427 Fix: add pgvector support to NodeJS OSS VectorStoreFactory (fixes #3491) (#3997)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-20 20:47:04 +05:30
Failfail2603 7ad5d6f442 fix: use toCamelCase in redis get method for the payload (#3172) 2026-03-20 20:28:41 +05:30
Utkarsh 305ce7b6b3 feat: add Apache AGE graph store support (#4448)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 20:27:48 +05:30
Matt Van Horn 4437c3e8a8 fix: add missing _parse_response to AzureOpenAIStructuredLLM (#4434)
Co-authored-by: Matt Van Horn <455140+mvanhorn@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 20:14:47 +05:30
Himanshu 54bdbde6e6 feat: add MiniMax LLM provider (#4132) (#4431)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-20 19:18:36 +05:30
Kartik 2b9558335b fix: raise ValueError when deleting nonexistent memory (#4455) 2026-03-20 18:28:58 +05:30
Utkarsh 2520edb404 feat: add optional API key authentication to REST API server (#4442)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-20 16:15:25 +05:30
mintlify[bot] f05e50d940 Improve SEO metadata across documentation pages (#4447) 2026-03-20 02:52:39 -07:00
dhilip_binny 401754ca65 fix: prevent embedding corruption in Valkey and Redis when vector is None (#4336) (#4362)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-20 15:07:17 +05:30
Kartik 73038900f5 fix: wrap vector and payload in lists for Langchain.update (#4446) 2026-03-20 14:42:17 +05:30
Kartik 6663b738d5 refactor: fix webhook create/update serialization, add payload types, and MEMORY_CATEGORIZED event (#4429)
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2026-03-19 22:26:17 +05:30
Kartik 88abb29de9 fix: handle truncated code blocks in removeCodeBlocks function (#4421) 2026-03-19 18:18:11 +05:30
Kartik 66e6f58fc6 chore: delete obsolete e2e tests (#4419) 2026-03-19 18:11:32 +05:30
Kartik 22c2545d61 feat(test): integration test for ts-sdk (#4395) 2026-03-19 18:11:09 +05:30
Utkarsh 410b79c750 fix: handle control characters in LLM JSON responses (#4420)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 17:31:07 +05:30
Anisha Mahuli 08de18f860 replace hardcoded US/Pacific timezone references with timezone.utc (#4404)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-19 17:29:29 +05:30
Utkarsh 46b4b2e9c8 fix: preserve http_auth in _safe_deepcopy_config for OpenSearch (#3580) (#4418)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-19 16:37:56 +05:30
Kartik a029cc9d43 fix: add LLM provider detection and defaults to memory config (#4400) 2026-03-19 15:26:16 +05:30
Kartik 348f44b632 fix(reranker): support nested llm config in LLMReranker for non-OpenAI providers (#4405) 2026-03-19 15:26:00 +05:30
Saket Aryan 0c4d0290cb fix(docs): add redirect rules for legacy and moved documentation pages (#4413) 2026-03-19 13:42:18 +05:30
Utkarsh b971b61cbb feat(openclaw): improve extraction quality with noise filtering, deduplication, and better instructions (#4302)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-19 03:51:30 +05:30
Kartik ffd1b96916 fix(ts-sdk): externalize all peerDependencies in tsup config (#4408) 2026-03-18 23:49:16 +05:30
dhilip_binny 4ffe1eaa4e fix: forward tools parameter to Gemini API in GoogleLLM (#4380) (#4386) 2026-03-18 23:48:26 +05:30
Atharva Jaiswal a172de9c22 fix: pass encoding_format='float' in OpenAI embeddings for proxy compatibility (#4058) 2026-03-18 23:46:44 +05:30
Kartik 7539463f50 refactor: improve Ollama embedder, normalize model names, add error handling, update tests (#4403) 2026-03-18 23:34:35 +05:30
Anisha Mahuli 577a5a2feb fix(oss): normalize malformed LLM fact output before embedding (#4224)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-18 20:19:29 +05:30
darrenxu d7a34c24dd fix(ollama): pass tools to client.chat and parse tool_calls from response (#4176)
Signed-off-by: sxu75374 <imshuaixu@gmail.com>
Signed-off-by: Small <imshuaixu@gmail.com>
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-03-18 19:19:36 +05:30
Kartik 214d2a1d0d chore: remove the integration/mirofish path from docs (#4399) 2026-03-18 16:40:30 +05:30
Kartik f0eb9e091f docs: add MiroFish integration and swarm memory cookbook documentation (#4373) 2026-03-18 16:31:04 +05:30
Kartik 3cdcb6564c chore: end to end test coverage for ts sdk (#4357) 2026-03-17 21:13:52 +05:30
Utkarsh 336fbce60a feat(mem0-ts): add LM Studio embedder and LLM support (#4354)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 18:21:35 +05:30
Huvee 9eb5b9ed29 fix(qdrant): handle 401/403 in ensureCollection for scoped JWTs (#4356) 2026-03-17 18:21:00 +05:30
Utkarsh 8230a5dac7 fix: cast vector_distance to float in Redis search (#4377)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-17 16:34:46 +05:30
Saket Aryan 9864584c21 feat: add openclaw checks CI workflow (#4368) 2026-03-17 12:06:13 +05:30
Saket Aryan 9eea060db9 docs: fix mintlify build failing (#4363) 2026-03-16 23:02:54 +05:30
Kartik 15218d4a7f chore: bump mem0-ts to 2.4.1, pyproject to 1.0.6, update changelog with bug fixes (#4361)
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2026-03-16 22:49:31 +05:30
Kartik 69001d7b1f chore(docs): adding skills.sh installation command in the readme. (#4350) 2026-03-16 22:05:15 +05:30
dhilip_binny 35fe30aabd fix: ensure JSON instruction in prompts for json_object response format (#3559) (#4271) 2026-03-16 21:57:57 +05:30
Kartik 11a7d8378c chore: update langchain dependencies to v1.0.0 (#4353) 2026-03-16 21:43:14 +05:30
Kartik b4b73deada fix(oss): OllamaLLM now respects configured url instead of always falling back to localhost (#4320) 2026-03-16 21:42:46 +05:30
Utkarsh bfe730aa38 fix(openclaw): add SQLite resilience for OSS mode initialization (#4337)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-16 21:36:27 +05:30
Kartik 82d67430dd fix: remove destructive vector_store.reset() from delete_all() (#4349) 2026-03-16 20:55:29 +05:30
Kartik 8fcf2b0b29 fix: skip telemetry vector store init when MEM0_TELEMETRY is disabled (#4351) 2026-03-16 20:55:04 +05:30
Anisha Mahuli 2e5e290434 fix: key error when llm omits entities key tool call (#4313) 2026-03-16 20:52:26 +05:30
Saket Aryan 06ee1b588c fix(openclaw): point plugin extension entry to built output for npm compatibility (#4340) 2026-03-15 04:41:11 +05:30
Saket Aryan dc6122ec3d chore(openclaw): add tsup build pipeline with ESM output and type declarations (#4335) 2026-03-15 00:41:07 +05:30
Saket Aryan df79a43925 chroe(ts-sdk): fix lints (#4334) 2026-03-14 23:39:23 +05:30
Utkarsh a6242710df chore(ts-sdk): bump mem0ai version to 2.4.0 (#4332)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-03-14 23:34:56 +05:30
d 🔹 4e1e4c0c5a fix(ts): extract content from code blocks instead of deleting it (#4317)
Co-authored-by: d 🔹 <258577966+voidborne-d@users.noreply.github.com>
2026-03-14 23:34:43 +05:30
Kartik fa5c85f9f6 chore: bump protobuf dependency to 5.29.6 and extend upper bound to 7.0.0 (#4326) 2026-03-14 10:58:58 -07:00
Muhammed Ajmal M 7c29eb2645 fix: incorrect database param (#3913) 2026-03-14 01:54:20 -07:00
Anisha Mahuli 6f079c313f fix OpenAI embedder baseurl (#4275) 2026-03-13 01:38:59 -07:00
Giulio Leone e95090e116 fix: add missing 'json' keyword to graph memory prompts (fixes #4248) (#4249) 2026-03-13 01:38:24 -07:00
Utkarsh 861cbb7289 fix(openclaw): use absolute URL for architecture image in README (#4311) 2026-03-12 11:06:51 -07:00
Kartik 54aa760720 feat(skills): add Mem0 Platform Claude Code skill (#4309) 2026-03-12 10:00:39 -07:00
Utkarsh 59c3b050bd fix(oss): auto-detect embedding dimension to fix Qdrant mismatch with non-OpenAI embedders (#4297)
Co-authored-by: utkarsh240799 <utkarsh240799@users.noreply.github.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-03-12 21:45:09 +05:30
Utkarsh 5b3acf416b fix(ts-sdk): resolve SQLite db paths correctly in OSS mode (#4307) 2026-03-12 09:13:44 -07:00
Kartik 63f587c922 fix(docs): use filters param for search in LiveKit integration (#4300) 2026-03-12 18:05:02 +05:30
Kartik 21df43c699 fix(docs): correct Deploy with Docker Compose card link (#4296) 2026-03-11 00:15:54 -07:00
Utkarsh 0118198143 chore(openclaw): bump version to 0.3.0 (#4283) 2026-03-09 21:50:13 -07:00
Utkarsh 36537c8326 fix(ts-sdk): replace sqlite3 with better-sqlite3 to fix native binding resolution (#4270) 2026-03-09 11:00:36 -07:00
Utkarsh 7482c48692 fix(openclaw): migrate platform search to mem0 v2 API (#4276) 2026-03-09 09:48:55 -07:00
Utkarsh e219961f9d docs(openclaw): clarify userId is user-defined (#4277) 2026-03-09 09:47:15 -07:00
Utkarsh 3ffe43f99f feat(openclaw): add per-agent memory isolation for multi-agent setups (#4245) 2026-03-09 08:53:49 -07:00
liviaellen 72e2c5c24b Fix handle malformed entity dicts and None LLM response in memgraph_memory (#4238) 2026-03-07 12:05:32 -08:00
Saket Aryan 34c797d285 fix: disable ph telemetry still calls posthog (#4203) 2026-03-04 03:55:34 +05:30
Saket Aryan a0d8a02b94 chore(ts-sdk): bump axios to 1.13.6 (#4177) 2026-03-02 13:24:35 +05:30
Saket Aryan 93c720301e docs: update delete_all to reflect filter validation breaking change (#4103) 2026-02-25 21:10:35 +05:30
mgoulart db15d5c629 fix(oss): validate LLM fact output via FactRetrievalSchema before embedding (#4083) 2026-02-22 19:57:48 -08:00
mem0-bot[bot] aa4a944b51 fix: Bug: Openclaw Extension OSS Mode lacks threshold restrictions (#4106) (#4115) 2026-02-22 18:44:11 -08:00
Prathamesh ab5e930cf7 Update OpenClaw integration architecture diagram (#4079) 2026-02-19 16:42:43 -08:00
Deshraj Yadav 0e76de7bd5 Add source openclaw (#4082) 2026-02-19 16:35:41 -08:00
Mragank Shekhar 5a93643f12 docs: add memory_categorize webhook event type (#4077) 2026-02-19 15:09:11 +05:30
Mragank Shekhar a140829395 chore: update user facing timestamp for a memory (#4066) 2026-02-18 03:27:46 +05:30
Zlo7 a6810819ca OpenClaw plugin: fix auto-recall injection and auto-capture message drop (#4065) 2026-02-17 13:26:45 -08:00
Saket Aryan a02205e519 chore: remove legacy v0.x docs and version dropdown (#4060) 2026-02-16 13:25:05 -08:00
Saket Aryan 69a832dc58 chore: add update project options (#3947) 2026-02-03 10:55:43 +05:30
Deshraj Yadav 70baa46cb1 fix: add OpenClaw to docs navigation (#3965) 2026-02-02 10:17:19 -08:00
Deshraj Yadav 3d3e875d21 Feature: Add OpenClaw plugin and documentation (#3964) 2026-02-02 10:04:08 -08:00
Saket Aryan dba7f0458a (version-bump): update the project version to v1.0.2 (#3902) 2026-01-13 13:01:38 +05:30
Saket Aryan 27e5db5831 (fix): mongodb distribution name, azure ai search, and workflow trigger (#3900) 2026-01-13 12:52:49 +05:30
Saket Aryan 2c90eedfff chore: do a disk cleanup in gh actions to fix memo build (#3899) 2026-01-13 11:42:54 +05:30
Noah Stapp a1db0f6362 Add DriverInfo metadata to MongoDB vector store (#3648) 2026-01-12 21:07:42 -08:00
Saket Aryan 90a7b1afa0 feat(ts-sdk): add support for keyword arguments in add and search methods (#3895) 2026-01-10 21:19:55 +05:30
Saket Aryan 69a552d8a8 fix(docs): Improve light mode support for introduction page and organize thumbnails (#3880) 2026-01-03 21:21:24 +05:30
Saket Aryan 417ebffadd (ts-sdk-update): Update for TypeScript SDK v2.2. (#3865) 2025-12-29 15:01:18 +05:30
Saket Aryan 1dc07d3550 (docs): update to use the v2 URL Patterns in delete user route (#3864) 2025-12-29 14:51:42 +05:30
Saket Aryan 65e22e34d9 chore: remove unnecessary dependencies from Vercel AI SDK to reduce package size (#3856) 2025-12-26 22:24:40 +05:30
Parth Sharma e08f44c5f2 [docs] link to fix api key redirect (#3843) 2025-12-18 00:20:29 +05:30
Parth Sharma 16d989bbcd [docs] Series of docs for mem0-mcp (#3831) 2025-12-15 22:03:23 +05:30
Swarnaprakash Udayakumar 0f8654bd40 Add Strands agent (with AWS ElastiCache and Neptune) example mention in Joint blog post by Mem0 and AWS (#3824) 2025-12-13 14:00:29 +05:30
Parth Sharma 654089fcfc [docs] Filters fix in docs (#3815) 2025-12-11 18:38:08 +05:30
Parth Sharma 222c6ceea1 (docs-fix): fix broken redirect in python and node quickstart (#3826) 2025-12-11 15:50:20 +05:30
Parth Sharma 84bd6e3b97 fix(docs): Correct API authentication header from Bearer to Token (#3820) 2025-12-11 00:32:59 +05:30
Parth Sharma 5676bebd5f docs: migration guide v1 (#3822) 2025-12-10 23:45:19 +05:30
Parth Sharma f14132db44 [docs] Gemini-3 demo with mem0-mcp (#3810) 2025-12-09 23:43:49 +05:30
Parth Sharma 903c3635cc [docs] Add memory and v2 docs fixup (#3792) 2025-11-27 23:41:51 +05:30
Parth Sharma cc2894aaec [docs] Docs redirect to platform (#3769) 2025-11-22 10:17:58 +05:30
Deshraj Yadav 97cbff77ef Add events API docs and spec updates (#3752) 2025-11-14 20:53:31 -08:00
Parth Sharma e29220efda [docs] new redirect for entity doc (#3750) 2025-11-14 08:51:25 -08:00
Prateek Chhikara 3b84a234e1 Updates to python sdk (#3749) 2025-11-13 14:22:39 -08:00
Parth Sharma 2bca30ebe6 [docs] Minor Docs fixes ( enhancements , restructure ) (#3748) 2025-11-13 11:58:36 -08:00
Parth Sharma 3297ec1a46 [doc] Partition Memories by Entity , features doc and cookbook (#3735) 2025-11-13 10:26:41 -08:00
Parth Sharma 61e2a40d55 [docs] python quickstart fix (#3742) 2025-11-13 10:18:55 -08:00
Parth Sharma 9f921e27cb [docs] add callouts and comparision to clear the problem of when to use Infer=True/False (#3738) 2025-11-10 14:23:49 -08:00
Parth Sharma 568e97d013 [docs] graph memory docs fix (#3728) 2025-11-07 09:41:35 -08:00
Parth Sharma ac5660e26d [docs] LLM.txt + Context menu to make our Docs LLM friendly (#3726) 2025-11-07 09:38:12 -08:00
Parth Sharma 76abd5117d [docs] API References and search Doc fix (#3712) 2025-11-04 13:53:37 -08:00
Prateek Chhikara 978babd3db Docs Update (#3706) 2025-11-03 11:03:33 -08:00
Parth Sharma 2b0a457198 [docs] Custom categories Documentation fix (#3702) 2025-11-03 10:11:04 -08:00
Parth Sharma 6a7277070f [docs] Add Redirects to the new docs to fix broken links (#3701) 2025-11-01 06:19:47 -07:00
Parth Sharma 4c53930e47 [docs] complete redirects (#3700) 2025-10-31 20:26:30 -07:00
Parth Sharma 84687fc3d2 [fix] list' object has no attribute 'id' - Id fault with chroma pinecone and other providers (#3693) 2025-10-31 16:46:10 +05:30
Parth Sharma 3ca939e210 [docs] redirect with fixed ci fails - langchain (#3699) 2025-10-31 16:45:15 +05:30
Parth Sharma 5f5e64b44b [docs] tab icon cleanup (#3679) 2025-10-28 09:42:19 +01:00
Parth Sharma 5cb6b31690 [docs] Cookbook name cleanup (#3678) 2025-10-28 09:20:23 +01:00
Parth Sharma ee8955d08b [docs] OSS Features , Overview , Brushup (#3676) 2025-10-27 12:31:40 -07:00
Parth Sharma 80c9139c5b [docs] zoom effect fix (#3670) 2025-10-27 08:56:32 +05:30
Parth Sharma 7be32641c7 [docs] Essential cookbook added and overall revamp to the structure of cookbooks (#3668) 2025-10-27 00:19:29 +05:30
Parth Sharma 2c18355dd2 [docs] platform core concept revamp and overview update (#3664) 2025-10-26 15:15:13 +05:30
Parth Sharma 61faf71064 [docs] Template moulding in docs/platform and index improvement (#3663) 2025-10-25 14:05:18 -07:00
Parth Sharma ac9598a67f [docs] Added Templates and Contribution Guidelines (#3662) 2025-10-25 12:58:40 -07:00
Parth Sharma f98a17c716 [docs] Welcome page thumbnail and reranker fix (#3660) 2025-10-25 12:20:52 -07:00
Vedant Thakkar 639d26e1ac feat(api): add vector store configuration endpoints (#3583) 2025-10-23 18:36:25 +05:30
Parth Sharma f7d7c53001 Added improved docs index and overview pages and quickstart (#3603) 2025-10-22 10:30:45 -07:00
Frederik Berg ec1a60bf8d Add delete_memories MCP tool for targeted deletion (#3616) 2025-10-22 10:08:28 -07:00
Frederik Berg 77c71a134a Fix REST API infer parameter ignored (#3607) 2025-10-22 10:08:15 -07:00
Frederik Berg 2692e49d50 Fix: Add missing filter methods to AsyncMemory (#3624)
Co-authored-by: Claude <noreply@anthropic.com>
2025-10-22 16:08:04 +05:30
Ronak Bhalgami eb2f8a3738 fix: Prevent Mock object issues in graph memory tests (#3627)
Co-authored-by: parshvadaftari <daftariparshva@gmail.com>
2025-10-22 03:53:12 +05:30
Prateek Chhikara 4a30745592 Add redirect to new apis doc page (#3639) 2025-10-21 13:55:14 -07:00
Rahul Sharma 3b1a4c2e68 Fix condition check for memories_result type in AsyncMemory class (#3621) 2025-10-22 01:55:28 +05:30
Mrinank Bhowmick 5227b0a062 Fix embedder config schema to support embeddingDims and url parameters (#3633) 2025-10-21 09:30:44 -07:00
Prateek Chhikara 8031f0bf8f Changes to docs (#3637) 2025-10-20 16:13:34 -07:00
Prateek Chhikara dd3e5363dd Update docs (#3636) 2025-10-20 16:12:11 -07:00
Frederik Berg d5a130b785 Fix memory deletion not removing from vector store (#3610) 2025-10-18 14:17:03 -07:00
Frederik Berg 8ede1df10a Fix list_memories endpoint Pydantic validation error (#3608) 2025-10-18 14:16:49 -07:00
Parshva Daftari 8ba18bf8bc [fix] docs for search memories (#3622) 2025-10-18 13:27:15 -07:00
Ronak Bhalgami de224dd26d feat: Add configurable embedding similarity threshold for graph store node matching (#3593) 2025-10-18 13:03:30 -07:00
Faizan Habib 9ef644b95e Add Apache Cassandra vector store support (#3578) 2025-10-17 23:48:49 +05:30
Aashis kumar 7afbaae7a3 Fix condition check for memories_result type in Memory class (#3596) 2025-10-17 02:17:18 +05:30
Tarun Jain 1090784302 [feat add]FastEmbed embedding for local embeddings (#3552) 2025-10-16 22:52:22 +05:30
Parshva Daftari 394203d1b5 Mem0 1.0.0 (#3545) 2025-10-16 15:50:20 +05:30
Kabir Kohli 8f5151c344 asycn mode default change (#3585) 2025-10-16 04:58:53 +05:30
Parshva Daftari 41cfb3ab1a [Update] Default LLM (#3587) 2025-10-15 11:19:52 -07:00
G Karthik Koundinya a40314c971 feat: Add Azure AI Search vector store support for TypeScript SDK (#3549) 2025-10-15 11:19:10 -07:00
Vedant Thakkar ea22e8d9cd feat: Allow custom model and params with huggingface_base_url (#3574) 2025-10-14 17:33:18 +05:30
Alex Kondratev ce8a285003 Validate embedding_dims in kuzu, fix #3556 (#3558) 2025-10-11 03:55:03 +05:30
Parshva Daftari 4559623501 [fix] milvus db bug and added tests (#3566) 2025-10-11 02:21:35 +05:30
Deshraj Yadav 37c86aa3c0 Update main script and remove stale code (#3561) 2025-10-09 15:30:20 -07:00
Mrinank Bhowmick 335a7d7862 Fix TypeScript build error (#3535) 2025-10-09 11:09:31 -07:00
Mrinank Bhowmick 64571002f5 fixed hardcoded embeddingDims (#3537) 2025-10-09 11:09:20 -07:00
Vishaal LS 9000576173 fix: handle non-serializable objects in config deepcopy (#3464) (#3544) 2025-10-09 19:35:08 +05:30
Josh Hayes 922471f43b fix: Databricks Vector Store (#3546) 2025-10-09 19:19:07 +05:30
Saket Aryan b93ce5548b (docs): add v2 filter documentations (#3469) 2025-10-07 14:12:58 +05:30
Alex Kondratev ee0202764b Tool call support for LangchainLLM (#3542) 2025-10-06 00:03:44 +05:30
Saket Aryan 8ba032e029 (fix): added version=v2 as default param in ai sdk add calls (#3540) 2025-10-05 02:16:02 +05:30
yashikabadaya fbf3bd640c support dependency openai 2.x (#3533) 2025-10-03 21:50:49 +05:30
dog-last 51ce6f1347 Bug fix of thinking llm in vllm (#3510) 2025-10-03 19:13:08 +05:30
Parshva Daftari 346d89d244 Added azure mysql for mem0 (#3531) 2025-10-02 21:41:03 +05:30
Vishaal LS 1104b52d99 docs: add detailed explanation for output_format v1.1 parameter (#3517) 2025-10-01 14:19:34 -07:00
Parshva Daftari e19b748ad0 Refactor docs and fix get memories playground (#3527) 2025-10-01 10:40:27 -07:00
Matan Cohen 517a266d74 Fix bug in weaviate search method (#3521) 2025-10-01 14:24:03 +05:30
Frederik Berg cbf56477be Fix: Serialize response to JSON in add_memories MCP tool (#3523) 2025-09-30 15:28:50 -07:00
Vishaal LS 58cc44ff38 fix: handle missing 'data' key in memory payload during search operations (#3524) 2025-09-30 15:10:43 -07:00
Vishaal LS 445286a138 fix: update license information in README and pyproject.toml (#3522) 2025-09-30 13:58:08 -07:00
Deshraj Yadav d68ed11d58 Update Docs (#3520) 2025-09-30 08:41:36 -07:00
Parshva Daftari 135883935f refactor: v2 search and update examples (#3508) 2025-09-26 22:55:53 +05:30
Parshva Daftari ed5a1e9fc6 Update version to 0.1.118 (#3505) 2025-09-26 02:03:37 +05:30
Karthikeya Kollu dc883b0f9e [test] Add comprehensive test suite for SQLiteManager (#3494) 2025-09-25 22:30:03 +05:30
Saket Aryan 5616844b9c docs-fix: Quickstart cURL example fixed (#3503) 2025-09-25 20:44:39 +05:30
Saket Aryan 6e1d02c137 feat(ai-sdk): added file support for multimodal capabilities with memory context (#3500) 2025-09-25 10:32:06 +05:30
Parshva Daftari a199ee4ff8 Refactored example title for aws (#3492) 2025-09-22 18:28:28 +05:30
Parshva Daftari 88ae952483 [DOCS] Changing 1.0 to 1.0.0 (#3486) 2025-09-20 20:07:23 +05:30
Abdullah Irfan 9df392b26a Fixed s3 vectors memory initialization issue from configuration (#3481) 2025-09-20 12:43:18 +05:30
Parshva Daftari ead210ffe4 Added weaviate db test (#3483) 2025-09-18 14:40:44 -07:00
Andrew Carbonetto a015e2ff4a Add Neptune-DB graph store with vector store (#3443)
Signed-off-by: Andrew Carbonetto <andrew.carbonetto@improving.com>
Co-authored-by: Siddhartha Sahu <dev@sdht.in>
2025-09-19 02:54:33 +05:30
Brinlee Kidd d4e98dba38 feat: implement structured exception classes with error codes and sug… (#3279) 2025-09-19 02:31:35 +05:30
Parshva Daftari ac72eb5ecc Aspen theme for 1.x (#3473) 2025-09-18 21:03:17 +05:30
Andy Kwok 6b5582f474 Feat: Mem0 vector store backend integration for Neptune Analytics (#3453)
Signed-off-by: Andy Kwok <andy.kwok@improving.com>
2025-09-17 19:26:03 +05:30
Parshva Daftari d38e3f1962 Fix json parsing with new memories (#3456) 2025-09-12 17:29:34 +05:30
◢ 徇 ◤ a0685f3e8c fix: correct typo in knowledge graph extraction guidelines (#3449) 2025-09-12 15:07:37 +05:30
Parshva Daftari d48b1832c7 Fixes ollama and updates openai dependency (#3452) 2025-09-12 01:39:39 +05:30
Saket Aryan 21d69307dc docs: Update Search V2/Get All V2 Filters (#3450) 2025-09-11 19:10:19 +05:30
Andrew Carbonetto 9e5810dfb7 Fix bedrock anthropic models to use system field (#3438)
Signed-off-by: Andrew Carbonetto <andrew.carbonetto@improving.com>
2025-09-11 02:55:45 +05:30
Swarnaprakash Udayakumar e3f0277cb9 feat(vector-store): Add Valkey vector store support (#3272) 2025-09-10 04:01:53 +05:30
Prateek Chhikara e64488b598 updates to the category docs (#3437) 2025-09-09 11:47:09 -07:00
Parshva Daftari f5e0fb9e4b Added support for chromadb cloud (#3436) 2025-09-09 22:22:09 +05:30
Ranjith kumar 77b4b6a2b9 fix: 🐛 replace hardcoded llm provider with provider from config (#3423) 2025-09-05 22:38:04 +05:30
Josh Hayes 9477184582 databricks bug fixes (#3416) 2025-09-05 16:22:09 +05:30
Gabe Goodhart b27879bfd4 fix: Use ConfigDict instead of class-based Config (#3409)
Signed-off-by: Gabe Goodhart <ghart@us.ibm.com>
2025-09-04 20:18:25 +05:30
Saket Aryan f0e8c3f760 feat: Add metadata param to TS-SDK in client.update (#3415) 2025-09-04 03:22:12 +05:30
Parshva Daftari 5e8d5e4664 Release 0.1.117 (#3411) 2025-09-03 23:09:00 +05:30
Shili Cao c8d864c1b6 fix: add missing provider for baidu vector db (#3405) 2025-09-03 15:24:33 +05:30
Prateek Chhikara 617aabe5b3 [FIX] Graph Docs page was missing on the side bar (#3402) 2025-09-02 13:32:23 -07:00
Prateek Chhikara 163dafb216 Add version param in search v2 API documentation (#3401) 2025-09-02 13:24:22 -07:00
Srishti Gureja cc15a22bf9 support store for openai (#3399) 2025-09-02 21:23:59 +05:30
Parshva Daftari 64cbe84089 Updated favicon logo (#3398) 2025-09-02 21:10:04 +05:30
Parshva Daftari c8f9f20dff Updated integration docs (#3392) 2025-09-02 04:26:43 +05:30
John Lockwood 97fd320bbf Fix/new mem mistaken for current fixing #2875 (#2876) 2025-09-01 19:46:01 +05:30
Saket Aryan 748620f29b fix(Vercel AI SDK): Streaming not working properly (#3386) 2025-08-30 21:58:30 +05:30
Srishti Gureja 0b2aa36e98 Bugfix: Pick AWS region from the environment variable correctly (#3384) 2025-08-30 01:43:02 +05:30
Sheharyar Ahmad 3d0ece1bcf Refactor PGVector to Use Internal Connection Pools and Context Managers (#3373) 2025-08-29 19:06:39 +05:30
Rupam Jana 458d7ab8a3 replace query_vector args in search method of mongodb vector_stores (#3379) 2025-08-29 19:05:25 +05:30
Tushar Chandra 84af5ad265 docs: fix typo in docs/platform/advanced-memory-operations.mdx (#3348) 2025-08-27 17:35:45 -07:00
VikramIyer125 6237a6acb9 Adding weaviate, faiss, pgvector, chroma, redis, elasticsearch, milvus vector store to openmemory (#3366)
Co-authored-by: Vikram Iyer <vikramiyer@mac.local.meter>
2025-08-27 01:47:39 +05:30
VikramIyer125 8c8368781d Adding custom connection to weaviate connection client to enable client connection to local container (#3360)
Co-authored-by: Vikram Iyer <vikramiyer@Vikrams-MacBook-Pro.local>
2025-08-25 23:54:02 +05:30
Padarn Wilson 3b2d0ad0eb Fix missing commas in Kuzu graph INSERT queries (#3358) 2025-08-24 12:33:51 +05:30
Andy Kwok 9337a873ec Fix: Missing app_id on Neptune Analytics client (#3278)
Signed-off-by: Andy Kwok <andy.kwok@improving.com>
2025-08-23 21:06:19 +05:30
Srishti Gureja 76411e2591 docs fix: remove user_id from from_config (#3320) 2025-08-23 17:11:56 +05:30
Andy Kwok d523070cbc fix: Inconsistent created and updated properties on graph (#3220)
Signed-off-by: Andy Kwok <andy.kwok@improving.com>
2025-08-23 16:51:13 +05:30
Enzo Biondo c72bfc3285 Add Amazon S3 Vectors Support (#3237) 2025-08-23 16:44:42 +05:30
Parshva Daftari ee00bd5731 Fix typescript docs (#3357) 2025-08-22 14:23:26 -07:00
VikramIyer125 f914dca659 Add export_openmemory.sh migration script (#3352)
Co-authored-by: Vikram Iyer <vikramiyer@Vikrams-MacBook-Pro.local>
2025-08-22 20:19:41 +05:30
VikramIyer125 b64792590e Add memory export / import feature (#3345)
Co-authored-by: Vikram Iyer <vikramiyer@Vikrams-MacBook-Pro.local>
2025-08-21 23:27:28 +05:30
Parshva Daftari a7ac8bf13b Updated discord and dashboard image (#3344) 2025-08-20 14:11:26 -07:00
David A. Torres 4487785cec feature: add Azure Identity for Azure OpenAI and Azure AI Search authentication (#3262) 2025-08-21 02:00:27 +05:30
NiLAy e4c5582808 fix-migration-collection-override (#3100)
Co-authored-by: parshvadaftari <daftariparshva@gmail.com>
2025-08-21 00:11:28 +05:30
Vương Hữu Hưng (Hans) ff399e5528 Fix: Ollama checking model exists (#2682) 2025-08-19 16:29:26 +05:30
Parshva Daftari e7013764f7 Update aws bedrock (#3334) 2025-08-19 02:20:34 +05:30
Parshva Daftari c8ee17b884 Fix dependency and tests and updated docstring (#3337) 2025-08-19 02:12:24 +05:30
AkisAya 346b913ace feat: add es headers config (#3088) 2025-08-19 01:15:23 +05:30
Parshva Daftari 49ad64708b Updated databricks docs (#3336) 2025-08-18 13:24:32 -05:00
Josh Hayes 3a1eff425b feat(vector-store): Add Databricks Mosaic AI vector store support (#3325) 2025-08-18 22:20:33 +05:30
Parshva Daftari ebb411b11a Refactor docs (#3335) 2025-08-18 20:45:09 +05:30
Archie Sengupta 8e8f13a48e feat(Vercel AI SDK): add a param in config called host (#2634)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
2025-08-17 16:21:06 +05:30
Siddhartha Sahu a6a3928091 Add support for graph memory using Kuzu (#2934) 2025-08-16 02:22:31 +05:30
Ankush Malaker a883b56aa8 AsyncMemory._add_to_vector_store bugfix when no facts found (#3313) 2025-08-15 22:15:00 +05:30
Deshraj Yadav 246d9e8f69 Update llms.txt file (#3321) 2025-08-14 14:51:03 -07:00
Deshraj Yadav 192db1844c Update Docs (#3315) 2025-08-13 21:37:15 -07:00
Saket Aryan 5e895c240a Update version to 0.1.116 (#3312) 2025-08-14 00:06:34 +05:30
Parshva Daftari b4bd7b48df Fixing Json import for the psycopg and psycopg2 (#3310) 2025-08-13 23:52:58 +05:30
Parshva Daftari 1bc31b6ae3 Added sanitation for better relationship mapping (#3300) 2025-08-13 11:06:05 -05:00
Parshva Daftari 7159221d2e Restrict package version (#3305) 2025-08-12 16:53:21 -05:00
Parshva Daftari c89dc72f79 Fix failing tests (#3304) 2025-08-12 15:23:54 -05:00
Parshva Daftari 2145ffdb1c Updated docs for agent_id and run_id (#3294) 2025-08-12 15:03:39 -05:00
Parshva Daftari 23d31a830b Added support for python 3.12 (#3295) 2025-08-12 15:01:41 -05:00
Andrew Carbonetto ab099312d5 Add neptune example notebook and documentation (#3224)
Signed-off-by: Andrew Carbonetto <andrew.carbonetto@improving.com>
2025-08-12 15:00:18 -05:00
Andy Kwok 8557533b8f DOC: Fix missing Neptune Analytics mention on doc (#3264)
Signed-off-by: Andy Kwok <andy.kwok@improving.com>
Co-authored-by: Andrew Carbonetto <andrew.carbonetto@improving.com>
2025-08-12 14:59:28 -05:00
Aymen 72e5a4fcc4 docs: Remove unused and missing OS module from examples (#3090) 2025-08-12 14:58:43 -05:00
Bruce Wang b199186163 Fix: refer to 61~62, self.config.graph_store.lm.config should be prioritized for usage (#3043) 2025-08-12 14:56:24 -05:00
Anirudh S 9e9dcd70e1 refactor: Update batch update method documentation to clarify optiona… (#2982) 2025-08-12 14:55:53 -05:00
Stefan Gajanovic dbe909c352 added simple sanitizer methods for nodes and realtionships (#3021) 2025-08-12 14:54:12 -05:00
YuriyTW d65a39c125 refactor: Improve async handling in AsyncMemory class for better performance (#3250) 2025-08-12 22:43:00 +05:30
Parshva Daftari b60a208c2f Fixes n_embeddings use and error for memgraph (#3296) 2025-08-11 12:57:29 -05:00
cnScarb c2792c6558 docs: fix search method return value handling in integration and example docs (#3208) 2025-08-11 12:16:01 -05:00
Parshva Daftari 2307dc8613 Fix/supported llm params (#3290) 2025-08-08 14:39:42 -07:00
John Lockwood 4c748423fc Feat/llm monitoring callback (#2877) 2025-08-08 14:26:51 -07:00
DrJsPBs 26732771eb Fix Neo4j Cypher syntax error with agent_id filtering (#3158)
Co-authored-by: parshvadaftari <daftariparshva@gmail.com>
2025-08-08 09:08:13 -07:00
Parshva Daftari 148bbf0a5c Add sslmode pgvector (#3265) 2025-08-06 10:28:02 -07:00
Parshva Daftari 6e8c6c1cb7 Refactored base class config for llms (#3241) 2025-08-05 15:42:06 -07:00
Parshva Daftari f59ef3f2e2 Update docker compose (#3258) 2025-08-05 15:40:14 -07:00
Vimpas 7a0dc7391e feat: Add db_name field to MilvusDBConfig and MilvusDB initialization (#3229) 2025-08-05 11:16:34 -07:00
Saket Aryan 7fac6311f4 ai-sdk/docs: V5 Migration Docs (#3277) 2025-08-05 09:49:45 -07:00
Parshva Daftari 0e18f54d36 Vercel AI SDK migration to V5 (#3223)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
2025-08-05 20:27:57 +05:30
Parshva Daftari 42e60d6724 Added mulit id filters support for all vectorstores (#3269) 2025-08-04 14:52:57 -07:00
Parshva Daftari 57a16aeb4b Updated psycopg -> 3 (#3271) 2025-08-04 14:46:44 -07:00
lazakrisz 5ea2d56d88 fix: add RedisCloud search module check (#3192)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
2025-08-04 22:08:07 +05:30
Parshva Daftari 7d1d0ca806 fixes memgraph async attirbute error (#3209) 2025-08-01 13:19:08 -07:00
Parshva Daftari 89b67e0834 Refactoring from gemini to google ai (#3244) 2025-08-01 11:56:42 -07:00
Parshva Daftari fbe8a2e90f Fix indexing when using Qdrant cloud (#3228) 2025-08-01 11:55:11 -07:00
Enam Biswas 907328aafe feat (pinecone): Add namespace support and improve type safety (#3216) 2025-08-01 11:53:34 -07:00
Antaripa Saha 0e03d69ed1 Personalized Search Example Docs (#3259) 2025-08-01 17:02:58 +05:30
Prateek Chhikara 0412e62cb1 Update field in docs (#3254) 2025-07-30 12:22:56 -07:00
Antaripa Saha 724c553a2e Personalized Search using Tavily + Mem0 (#3232) 2025-07-29 15:20:11 +05:30
Saket Aryan 08e7ae02de docs: Async Add Announcement (#3231) 2025-07-29 00:26:59 +05:30
Colsrch d0f61d5995 fix: Ignore memgraph index duplicate creation errors (#3203) 2025-07-25 01:07:23 +05:30
Parshva Daftari 4433666117 Fix failing tests (#3162) 2025-07-25 00:58:45 +05:30
Saket Aryan 37ee3c5eb2 docs: Update for new Project API and deprecation notices (#3212) 2025-07-24 15:16:47 +05:30
Dev Khant c8892bb1fe Update Changelog (#3211) 2025-07-24 10:17:51 +05:30
Antaripa Saha 1a8d175570 Content Writing Example Rewrite (#3142) 2025-07-16 11:47:50 +05:30
Antaripa Saha 0560d87160 Multiagent Learning System with LlamaIndex (#3063) 2025-07-16 11:47:17 +05:30
Antaripa Saha 9f3fd06334 Agno Mem0Tools update (#3139) 2025-07-16 11:46:51 +05:30
Antaripa Saha c1ca366edd Multi-LLM Research Team powered by memory (#3160) 2025-07-16 11:46:31 +05:30
askdevai-bot cba3217280 docs: Add comprehensive LLM-friendly documentation (#3154) 2025-07-16 06:06:31 +05:30
Parshva Daftari 77ea103b5d Updated livekit 1.0 integration (#3073) 2025-07-16 00:27:14 +05:30
Saket Aryan bcc5f42941 Restore and update handle_post_message implementation (#3152) 2025-07-14 21:25:45 +05:30
Saket Aryan 3bc5090371 Update personalized deep research example with GitHub link (#3136) 2025-07-10 18:01:33 -07:00
Antaripa Saha de0513fc9f AWS Bedrock Integration and spell checks (#3124) 2025-07-08 10:16:44 -07:00
Saket Aryan ec9b0688d8 Add structured_data_schema to MemoryOptions interface (#3125) 2025-07-08 10:07:44 -07:00
Saket Aryan 0f5612b96d Add JavaScript examples for memory export API (#3119) 2025-07-08 13:46:26 +05:30
Saket Aryan 842903b1b1 feat: Memory Exports (#3117) 2025-07-08 11:34:49 +05:30
Dev Khant 70d6f9231b Abstraction for Project in MemoryClient (#3067) 2025-07-08 11:33:20 +05:30
Varun Mohanta aae5989e78 Fix: Changed keyword from assisstant to secretary (#2937) 2025-07-08 10:57:25 +05:30
Saket Aryan 6866e56d7a Add metadata field to memory update schema (#3115) 2025-07-07 09:49:13 -07:00
Antaripa Saha 2992c298cb Security Link updated (#3108) 2025-07-05 10:25:35 -07:00
Akshat Jain 4491e7f9f4 Fix: Memgraph Graph Generation Issue (#3109) 2025-07-05 10:06:13 -07:00
Deshraj Yadav c0a930a7d3 Update version to 0.1.114 (#3107) 2025-07-04 16:28:39 -07:00
Andrew Carbonetto 05c404d8d3 Add Amazon Neptune Analytics graph_store configuration & integration (#2949) 2025-07-04 16:26:21 -07:00
Deshraj Yadav 7484eed4b2 Fix CI issues related to missing dependency (#3096) 2025-07-03 18:52:50 -07:00
Mingxiangyu 2c496e6376 Fix the error that occurs when VLLM is called (#3076) 2025-07-03 14:41:10 -07:00
Jainish a20b68fcec Fixes: Mem0 Setup, Logging, Docs (#3080) 2025-07-03 14:40:39 -07:00
Sakshi Srivastava eb7c712aa6 Fix: Add missing OpenAI import in vLLM module (#3091) 2025-07-03 14:37:20 -07:00
Saket Aryan 7476c39257 Add Gemini Model Support to Vercel AI SDK Provider (#3094) 2025-07-03 09:54:11 -07:00
Saket Aryan 5b0f1a7cf8 feat: Add Gemini support to TypeScript SDK (#3093) 2025-07-03 09:53:52 -07:00
Antaripa Saha b336cdf018 Image fixes (#3089) 2025-07-02 11:40:55 -07:00
Antaripa Saha 60e4e8a662 Google AI ADK Integration Docs (#3086) 2025-07-02 10:23:37 -07:00
Chaithanya Kumar 6d4a78b7c7 Enhance documentation: Add group chat feature to the list of platform… (#3077) 2025-07-02 11:13:01 +05:30
Antaripa Saha d39a1d5541 Openai agents sdk added (#3081) 2025-07-01 17:02:59 -07:00
Parshva Daftari 044ad4f131 Reverting the changes of pip install (#3010) 2025-07-01 15:49:39 +05:30
Antaripa Saha 75482fdb29 Docs SOC2 and HIPAA update (#3075) 2025-07-01 01:21:53 -07:00
Antaripa Saha 6c69599db9 Docs Update Images (#3072) 2025-07-01 00:36:30 -07:00
Kade Shockey b79bfb7c1e MongoDB Vector Store misaligned strings and classes (#3064) 2025-07-01 11:12:28 +05:30
Dev Khant 5a1083b709 Fix: Gemini embedder config and version bump -> 0.1.113 (#3070) 2025-06-30 13:31:51 +05:30
Dev Khant ac085db500 version bump -> 0.1.112 (#3058) 2025-06-27 15:19:57 +05:30
Dev Khant 2cc253341c Fix mongodb config name (#3052) 2025-06-26 23:06:21 +05:30
Dev Khant e3e2da6d45 Fix: Gemini Embeddings and LLM (#3050) 2025-06-26 21:05:00 +05:30
Dev Khant acf7a30d32 Doc: Add async_mode (#3037) 2025-06-25 10:51:49 -07:00
Saket Aryan a4f6751741 fix(ui-backend): resolve provider name format inconsistency in form configuration (#3041) 2025-06-25 09:48:22 -07:00
Ryan Rozich 6f3fbd087d fix: Fix memory categorization by updating dependencies and correcting API usage (#3005)
Co-authored-by: Claude <noreply@anthropic.com>
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
2025-06-25 22:02:26 +05:30
Antaripa Saha a98842422b doc: Broken links fixed in docs (#3034) 2025-06-25 17:18:29 +05:30
Antaripa Saha aaf879322c Platform feature docs revamp (#3007) 2025-06-25 00:57:08 -07:00
Laith Al-Saadoon 8139b5887f fix: bedrock llm, embeddings, tools, temporary creds (#3023) 2025-06-24 20:46:06 +05:30
Saket Aryan b4b27f099e Add immutable param to add method and bump version (#3022) 2025-06-24 05:03:35 +05:30
Dev Khant dc877fd3ba version bump -> 0.1.111 (#3016) 2025-06-23 21:52:03 +05:30
Akshat Jain 2bb0653e67 Add: Json Parsing to solve Hallucination Errors (#3013) 2025-06-23 21:50:16 +05:30
Akshat Jain eb24b92227 Add : Openmemory Local Support using New Library (#3014)
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2025-06-23 20:45:46 +05:30
Akshat Jain a5ec286fd4 Add: Openmemory Augment support (#3015) 2025-06-23 20:45:01 +05:30
NiLAy 89499aedbe Feature/vllm support (#2981) 2025-06-23 13:18:38 +05:30
Akshat Jain 386d8b87ae Fix: Migrate Gemini Embeddings (#3002)
Co-authored-by: Dev-Khant <devkhant24@gmail.com>
2025-06-23 13:16:10 +05:30
Akshat Jain c173ec32d0 Improve Docs: Agent Id - Mem0 OSS Graph Memory (#2969) 2025-06-21 23:34:28 +05:30
Akshat Jain dd6f6f7a2e Fix: Add MCP Client Integration Guide and update installation commands (#2956) 2025-06-20 22:09:10 +05:30
Dev Khant b6684b96f7 version bump -> 0.1.110 (#3001) 2025-06-20 20:30:51 +05:30
Akarsha Sehwag 1fa0f0a157 fix(opensearch): update logger warning (#2999) 2025-06-20 20:28:51 +05:30
Saket Aryan 2754f45387 Make V2 Add as Default (#2997) 2025-06-20 16:56:42 +05:30
Parshva Daftari ecd4d91046 Fix failing CI pipeline (#2979) 2025-06-20 15:19:11 +05:30
Prateek Chhikara a5a247b161 Update client.update() method documentation in OpenAPI specification (#2990) 2025-06-19 14:04:12 -07:00
Dev Khant d47cb8d284 Doc: Fix example in quickstart page (#2986) 2025-06-19 13:51:20 +05:30
Dev Khant fa15db089d Update Changelog (#2985) 2025-06-19 12:32:33 +05:30
Shili Cao d35065c887 Feature: baidu vector db integration (#2929) 2025-06-19 11:12:12 +05:30
Prateek Chhikara cdee6a4ff0 Enhance update method to support metadata (#2976) 2025-06-18 10:07:18 -07:00
Dev Khant 9eb4e77c75 Fix pinecone for async memory (#2975) 2025-06-18 01:37:45 +05:30
Akshat Jain c700d790db Fix Build CI Failure (#2973) 2025-06-17 09:39:19 -07:00
Antaripa Saha a90b572389 Memory agent powered by voice (Cartesia + Agno) (#2970) 2025-06-17 18:54:53 +05:30
i-sun 62c330e5b3 feat(LM Studio): Add response_format param for LM Studio to config (#2502) 2025-06-17 17:55:18 +05:30
Akshat Jain c70dc7614b Fix: Add Google Genai library support (#2941) 2025-06-17 17:47:09 +05:30
Fenil Faldu e0003247c3 feat: add AgentOps integration (#2898) 2025-06-17 11:38:39 +05:30
Saket Aryan 888ee766c5 TS SDK - filter memories param (#2971) 2025-06-17 10:54:35 +05:30
Saket Aryan c7e91171a0 Added Param output_format in AI SDK (#2960) 2025-06-15 06:42:56 +05:30
Dev Khant 18c870ec79 version bump -> 0.1.108 (#2958) 2025-06-14 21:58:12 +05:30
Dev Khant 3e5f68ee90 Add logger in Opensearch (#2957) 2025-06-14 21:55:22 +05:30
Fabian Valle a0cd4065d9 +MongoDB Vector Support (#2367)
Co-authored-by: Divya Gupta <divya.gupta@mongodb.com>
2025-06-14 17:57:06 +05:30
John Lockwood 7c0c4a03c4 Feat/add python version test envs (#2774) 2025-06-14 17:43:16 +05:30
John Lockwood a8ace18607 Fix/pin pinecone issue #2772 (#2773) 2025-06-14 17:38:32 +05:30
Dev Khant df43f904d1 deploy minor version -> 0.1.107rc2 (#2953) 2025-06-13 12:09:54 +05:30
Prateek Chhikara a5a07d711b Updates in client to support summary (#2951) 2025-06-13 12:04:38 +05:30
Dev Khant a40268dd51 Fix: Migration in storage and version bump -> -0.1.107 (#2943) 2025-06-11 21:48:43 +05:30
Akshat Jain c59752c6d6 Update Categorisation Flow (#2922)
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2025-06-11 21:24:15 +05:30
Saket Aryan aa334fb569 Updated Docs for OMM Hosted Version (#2945) 2025-06-11 08:19:03 -07:00
Antaripa Saha 40a5e87022 Livekit Docs Update (#2933) 2025-06-09 10:20:41 -07:00
Akshat Jain 4dec9ace88 Update support for unique user IDs (#2921) 2025-06-07 21:20:40 +05:30
Dev Khant e1dc27276b Formatting and version bump -> 0.1.107 (#2927) 2025-06-07 12:27:22 +05:30
Saket Aryan 9a12ea7b3c Version Bump/Formatting (#2923) 2025-06-06 21:49:03 +05:30
Mrinank Bhowmick e10a509645 Added cloudflare vector-store (#2607) 2025-06-06 21:35:40 +05:30
Prateek Chhikara fe3f10adb8 Add Wildcard Character Support Documentation for v2 Memory APIs (#2919) 2025-06-06 12:23:04 +05:30
Akshat Jain 53c91fb107 Doc : Update Readme Docs for OpenMemory environment setup (#2913) 2025-06-05 21:52:04 +05:30
Prateek Chhikara ecc596b11f fix error of wrong exception (#2911) 2025-06-04 16:56:52 -07:00
Prateek Chhikara be37fca1bb Added threshold to search (#2899) 2025-06-03 02:58:21 -07:00
Dev Khant 849452cc93 version bump -> 0.1.104 (#2897) 2025-06-03 01:15:16 +05:30
Dev Khant 1f2df450bb Fix: GET_ALL for faiss and opensearch (#2896) 2025-06-03 01:10:07 +05:30
Dev Khant 06d86996f2 version bump -> 0.1.103 (#2894) 2025-06-02 22:26:37 +05:30
Dev Khant bb14cc42a0 Doc: update for enable_graph and Version bump -> 0.1.103 (#2893) 2025-06-02 22:23:22 +05:30
Saket Aryan fbee8d5c20 Added Async Mode Param (#2882) 2025-05-30 09:06:41 -07:00
Saket Aryan 855c322da6 deps(ts-sdk): Updates Google SDK Peer Dependency Version (#2878) 2025-05-30 09:51:01 +05:30
Prateek Chhikara 240acca3de Fix: Improve clarity and conciseness of Graph Memory features documen… (#2874) 2025-05-29 13:47:16 -07:00
Saket Aryan 7ef1378304 Fixed Broken Links (#2871) 2025-05-29 21:20:30 +05:30
Frank Zhao 9622ac7dff feat: support openai compatible llm provider by adding baseUrl to config (#2674)
Signed-off-by: frank-zsy <syzhao1988@126.com>
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2025-05-27 00:25:23 +05:30
Dev Khant 8a280b4a54 version bump -> 0.1.102 (#2805) 2025-05-26 23:24:51 +05:30
Antaripa Saha 1ba9c71f54 Add support for sarvam-m model (#2802) 2025-05-26 23:19:37 +05:30
Saket Aryan 5c6fbcaab0 Feature (OpenMemory): Add support for LLM and Embedding Providers in OpenMemory (#2794) 2025-05-25 01:01:23 -07:00
Olivier Blin b339cab3c1 Fix: Typos in openmemory MCP tool description (#2793) 2025-05-24 15:17:00 -07:00
Dev Khant a952df0953 Doc: Add NOT filter for Search and GetAll V2 (#2785) 2025-05-23 23:29:21 +05:30
Dev Khant 6cebddebbe Doc: Mastra and Raycast (#2781) 2025-05-23 16:10:47 +05:30
Chaithanya Kumar b3d340f59c Fix: Prevent saving prompt artifacts as memory when no new facts are … (#2744)
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2025-05-23 15:05:07 +05:30
Dev Khant 78e2efc0f2 Doc: update messages in api reference (#2777) 2025-05-23 14:41:13 +05:30
Saket Aryan d21970efcc feat(ai-sdk): Added Support for Google Provider in AI SDK (#2771) 2025-05-23 00:37:58 +05:30
Prateek Chhikara 816039036d Improve documentation on role-based memory attribution rules (#2770) 2025-05-22 12:07:18 -07:00
Dev Khant faf1a34f70 Doc: announce claude 4 (#2769) 2025-05-22 22:48:04 +05:30
Prateek Chhikara 6986153c90 Improve documentation on role-based memory attribution rules (#2768) 2025-05-22 09:39:50 -07:00
Saket Aryan 8048e0b32f fix(ts-sdk): Fixed Types from Message Interface (#2763) 2025-05-22 21:56:45 +05:30
Dev Khant af1cfd8139 Doc: Update output of Org/Proj creation APIs (#2761) 2025-05-22 15:05:23 +05:30
Dev Khant f5c3804f79 Doc: Update API Reference (#2760) 2025-05-22 11:54:44 +05:30
Dev Khant 443816365a Doc: Feature docs changes (#2756) 2025-05-22 10:59:18 +05:30
Dev Khant 097959d5cc Remove support for passing string as input in the client.add() (#2749) 2025-05-22 10:16:32 +05:30
Tomaz Bratanic bad6e12972 Add neo4j example (#2738) 2025-05-21 17:58:11 -07:00
Dev Khant d85fcda037 Formatting (#2750) 2025-05-22 01:17:29 +05:30
Dev Khant dff91154a7 Doc: Update memory export (#2741) 2025-05-21 13:14:34 +05:30
Dev Khant c3f3f82a3e Migrate to Hatch and version bump -> 0.1.101 (#2727) 2025-05-20 22:58:51 +05:30
Saket Aryan 70af43c08c improvement(OMM): Added CurL Command to Easy Install OMM (#2731) 2025-05-20 20:18:07 +05:30
Tomaz Bratanic 1786d907f7 Add neo4j base label config (#2675) 2025-05-19 18:22:20 -07:00
Prateek Chhikara 12a268da30 Added docs for criteria based filtering (#2726) 2025-05-19 14:52:43 -07:00
Chaithanya Kumar 0aefdf5251 Refactored collaborative task agent documentation to enhance clarity and simplified (#2725) 2025-05-19 09:58:22 -07:00
Antaripa Saha df72245b6b Update Index of Healthcare Example in docs (#2722) 2025-05-19 02:18:14 -07:00
Dev Khant fe872d0776 Update Changelog (#2720) 2025-05-19 12:54:15 +05:30
Dev Khant 052d31939d version bump -> 0.1.100 (#2719) 2025-05-19 12:27:38 +05:30
Antaripa Saha 1c44b675d9 Healthcare assistant using Mem0 and Google ADK (#2705) 2025-05-18 07:50:06 -07:00
Chaithanya Kumar a1c9a63074 # feat: Add Group Chat Memory Feature support to Python SDK enhancing mem0 (#2669) 2025-05-16 11:08:36 -07:00
Saket Aryan 931df14e25 fix(OMM): Memories not appearing in MCP clients added from Dashboard (#2704)
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2025-05-16 22:11:22 +05:30
heng 1b0d8bdd2e improvement(OMM)- fix the sse failed to connect issue (#2696) 2025-05-16 15:57:46 +05:30
Saket Aryan 5c67a5e6bc improvement(OSS): Fix AOSS and AWS BedRock LLM (#2697)
Co-authored-by: Prateek Chhikara <prateekchhikara24@gmail.com>
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-05-16 04:49:29 +05:30
GongRzhe 267e5b13ea Update README.md (#2687) 2025-05-15 00:16:05 -07:00
Saket Aryan a22287a3ba improvement(OpenMemory MCP): Improves Docker Compose commands (#2681)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-05-14 13:44:08 +05:30
Saket Aryan da59412150 Remove OpenMemory Directory from pyproject and Update Link (#2678) 2025-05-13 21:50:56 +05:30
Saket Aryan c41719ff9a Fix Backend Link in OpenMemory (#2677) 2025-05-13 08:36:59 -07:00
Deshraj Yadav f51b39db91 Add OpenMemory (#2676)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2025-05-13 08:30:59 -07:00
Saket Aryan 8d61d73d2f Added ElizaOS Example (#2670) 2025-05-12 10:05:11 -07:00
Saket Aryan 10acf78618 Added Missing Param in AI SDK and Updated Demo Application (#2667) 2025-05-12 04:22:23 +05:30
Tomaz Bratanic caeae60dda Add weights to Neo4j model (#2657) 2025-05-10 14:51:34 -07:00
Dev Khant d7b8497b24 Doc: update azure ai (#2661) 2025-05-09 20:03:34 +05:30
Dev Khant a96e1d58f7 Support for AWS Bedrock Embeddings (#2660) 2025-05-09 19:44:35 +05:30
Tomaz Bratanic 0d895b28ae Improve neo4j queries (#2654) 2025-05-08 11:11:46 -07:00
Saket Aryan 84910b40da Added support for graceful failure in cases services are down. (#2650) 2025-05-08 16:03:26 +05:30
Prateek Chhikara 0e7c34f541 Renamed unknown node type (#2649) 2025-05-07 23:19:58 -07:00
Prateek Chhikara 2b58775c17 updated docs (#2647) 2025-05-07 14:09:48 -07:00
Dev Khant 326f33757b Update Client (#2640) 2025-05-08 00:09:43 +05:30
Tomaz Bratanic c01221d4aa Add support for neo4j database (#2644) 2025-05-07 10:54:18 -07:00
Tomaz Bratanic 73d9ccac69 remove warnings and refresh schema from neo4j (#2643) 2025-05-07 10:16:39 -07:00
Wonbin Kim 5bbd0d9ca9 Fix duplicated metadata issue while adding or updating memories (#2592) 2025-05-07 21:10:32 +05:30
John Lockwood 641be2878d Fix/new memories wrong type (#2635) 2025-05-07 17:35:24 +05:30
Prateek Chhikara eb7f5a774c Update Documentation: Clarify Dual-Identity Memory Management (#2642) 2025-05-06 23:08:32 -07:00
Saket Aryan 6e9f8cf218 Added New Param, output_format (#2639) 2025-05-06 22:56:48 +05:30
Dev Khant 02a2b59555 Doc: update timestamp (#2638) 2025-05-06 17:39:31 +05:30
Dev Khant ec1d7a45d3 Fix all lint errors (#2627) 2025-05-06 01:16:02 +05:30
Saket Aryan 725a1aa114 Updated deleteUsers to use V2 API Endpoints (#2624) 2025-05-05 23:23:13 +05:30
Dev Khant d41f19b9ce Update delete_users() (#2623) 2025-05-05 23:21:06 +05:30
Saket Aryan a0fe9ca5b2 Fix AI SDK Filters (#2625) 2025-05-05 19:38:58 +05:30
Dev Khant c81e2efbb0 Support for HF Inference (#2619) 2025-05-05 11:20:34 +05:30
Dev Khant e9f5a882f5 Fix proxy for Mem0 (#2616) 2025-05-03 15:17:09 +05:30
Deshraj Yadav 7117a94fbf Remove unnecessary dependencies from base package (#2613) 2025-05-02 15:28:36 -07:00
Saket Aryan 63e22382de Updated TS client to use proper types for deleteUsers (#2612) 2025-05-02 23:11:40 +05:30
Prateek Chhikara 7b3abd06d0 Added dataset (#2611) 2025-05-02 10:19:58 -07:00
Prateek Chhikara e056acb6a8 docs change (#2606) 2025-05-01 14:03:04 -07:00
Saket Aryan c09dfc3646 Vercel AI SDK / Graph Memory (#2601)
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2025-05-01 23:40:51 +05:30
Dev Khant 6a1ece13dc Doc: Fix README links (#2602) 2025-05-01 22:00:56 +05:30
Dev Khant b74cd9162f Doc: fix timestamp (#2599) 2025-05-01 16:54:47 +05:30
Saket Aryan 42c98e5717 Bumped Anthropic SDK Version (#2598) 2025-04-30 14:02:59 -07:00
Dev Khant ad98f542f8 Fix mem0-migrations issue (#2597) 2025-05-01 01:14:57 +05:30
Saket Aryan 0fce700e65 Removed Grok3 Announcement (#2593) 2025-04-29 08:23:13 -07:00
Prateek Chhikara 393a4fd5a6 Docs Update (#2591) 2025-04-29 08:15:25 -07:00
Saket Aryan 6d13e83001 Fix Ping Method for using the default org_id and project_id (#2590) 2025-04-28 12:53:52 +05:30
Dev Khant 1d916c9dd1 update changelog (#2588) 2025-04-26 16:40:56 +05:30
Dev Khant 07ddd7cb4b version bump -> 0.1.94 (#2587) 2025-04-26 16:22:20 +05:30
darkhaniop f412f8bb0d Doc: add "memory" in EC "Custom config" section and fix typos in the json config sample (#2574) 2025-04-25 19:51:33 +05:30
Dev Khant 64c3d34deb Reset function for VectorDBs (#2584) 2025-04-25 00:01:53 +05:30
Dev Khant ff6ae478f1 Doc: fix v2 search (#2583) 2025-04-23 18:11:36 +05:30
Saket Aryan cc5686bd0d Added Timestamp (#2579) 2025-04-23 12:29:27 +05:30
Dev Khant c958664185 Doc: Update timestamp and expiration_date (#2581)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-04-22 14:09:18 -07:00
Dev Khant d43ca06992 Doc: add timestamp (#2580) 2025-04-23 01:05:16 +05:30
Katarina Supe ba2e479902 Add Memgraph integration (#2537) 2025-04-22 16:27:24 +05:30
Dev Khant cd5c3035ab version bump -> 0.1.93 (#2576) 2025-04-21 09:25:00 +05:30
Dev Khant 09ac3618a8 Doc: fix agno link (#2573) 2025-04-19 10:59:50 +05:30
Dev Khant 3ee4768c14 Init embedding_model_dims in all vectordbs (#2572) 2025-04-19 10:53:01 +05:30
Prateek Chhikara 78912928bc Changes to client (#2562) 2025-04-17 11:28:39 -07:00
Dev Khant 8bd0d2dc24 Doc: fix curl for v2 get_all (#2567) 2025-04-17 23:10:54 +05:30
Saket Aryan f0bdd2c341 Add Support for Custom Instructions (#2565) 2025-04-17 14:38:07 +05:30
Antaripa Saha bf0c4adc0c Fitness Checker powered by memory (#2561) 2025-04-16 08:37:23 -07:00
Dev Khant 2cca50db80 Doc: update changelog (#2559) 2025-04-16 16:43:15 +05:30
Dev Khant b8e4d0980a Memory Reset (#2558) 2025-04-16 16:36:45 +05:30
Dev Khant 3613e2f14a Fix user_id functionality (#2548) 2025-04-16 13:32:33 +05:30
Dev Khant 541030d69c Update capture_event (#2527) 2025-04-16 10:42:36 +05:30
Dev Khant f77a084d1b silence faiss info logs (#2557) 2025-04-16 09:57:32 +05:30
Saket Aryan 33abf772ce Adds Azure OpenAI Embedding Model (#2545) 2025-04-15 22:02:30 +05:30
Saket Aryan c3c9205ffa TypeScript OSS: Langchain Integration (#2556) 2025-04-15 20:08:41 +05:30
Gábor Tóth 9f204dc557 Update openai.mdx (#2503) 2025-04-14 21:08:49 +05:30
Antaripa Saha 0e98773efb Voice Assistant using Elevenlabs (#2555) 2025-04-14 20:48:10 +05:30
Dev Khant 4431bd7d51 Doc: update changelog (#2553) 2025-04-14 15:58:16 +05:30
Dev Khant 0354ab0d6b Doc: reformat navbar page URLs (#2551) 2025-04-14 06:09:12 +05:30
Antaripa Saha 6dfc193296 movie recommendation using grok3 (#2547) 2025-04-12 08:53:18 -07:00
Dev Khant 9be6850b9d Doc: Add keywords AI (#2546) 2025-04-12 17:49:59 +05:30
Deshraj Yadav d33547a77a User/dyadav/fix telemetry issue (#2541) 2025-04-11 13:36:26 -07:00
Vir Kothari d77bed2d5d Update YT chrome extension example doc (#2540) 2025-04-11 22:21:24 +05:30
Dev Khant 9c89e0ec95 Doc; Update xAI doc (#2539) 2025-04-11 21:37:18 +05:30
Saket Aryan ca1ee2d2d7 Patch to fix Azure OpenAI (#2538) 2025-04-11 21:28:38 +05:30
Saket Aryan 05f9607282 Adds Azure OpenAI LLM to Mem0 TS SDK (#2536) 2025-04-11 20:09:20 +05:30
Achraf Dev d9236de4ed feat: add mistral AI as LLM provider (#2496)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
2025-04-11 20:02:44 +05:30
Dev Khant 942727fec6 Fix EmbedderFactory.create() in GraphMemory (#2535) 2025-04-11 13:56:05 +05:30
Manthan Gupta 72396e307d Fix: memory exclusion example in doc (#2520) 2025-04-11 13:50:48 +05:30
Dev Khant 881cf5b5a6 update changelog (#2534) 2025-04-11 13:45:34 +05:30
Dev Khant 5327d6e50d version bump -> 0.1.89 (#2533) 2025-04-11 13:40:47 +05:30
Dev Khant 15a3e20371 Store user_id in vectordb (#2466) 2025-04-11 13:37:34 +05:30
Dev Khant 19d7beef43 Add support for Langchain VectorStores (#2518) 2025-04-11 13:37:18 +05:30
Vir Kothari 8b789adb15 Add YT assistant chrome extension (#2485) 2025-04-10 22:14:57 +05:30
Antaripa Saha fd065fe9cc Personal Study Buddy (#2531) 2025-04-10 08:12:39 -07:00
Antaripa Saha b5127f7c62 personal assistant (#2530) 2025-04-10 20:24:18 +05:30
Dev-Khant 37d9fed690 doc: update agno 2025-04-10 15:49:36 +05:30
Dev Khant 31861e9acb Doc: Add agno example (#2529) 2025-04-10 15:46:46 +05:30
Dev Khant 07462adc9a Formatting (#2526) 2025-04-10 11:42:25 +05:30
Dev Khant 616313b8b5 Add async support (#2492)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-04-10 11:16:44 +05:30
Dev Khant 44f2490667 Doc: modify 2 examples to show OpenAIResponses API (#2525) 2025-04-10 10:24:10 +05:30
Dev Khant 3cc794fb98 version bump -> 0.1.88 (#2524) 2025-04-10 01:02:05 +05:30
Dev Khant ff6b251c66 Handle HF logging (#2523) 2025-04-10 01:01:00 +05:30
Sergio Toro 55df395fd6 fix: extract entities tool_calls some times is an array (#2481) 2025-04-10 00:03:52 +05:30
Dev Khant 244e60cea6 update python changelog (#2522) 2025-04-09 23:38:10 +05:30
Dev Khant b480c71f0f version bump -> 0.1.87 (#2521) 2025-04-09 23:34:15 +05:30
Saket Aryan 309c8c18a6 Add user_id in TS OSS SDK (#2514) 2025-04-09 10:24:56 -07:00
Dev Khant f4d8647264 Doc: update memory export (#2519) 2025-04-09 17:34:16 +05:30
Dev Khant f95c4cbbe5 Update MAKEFILE (#2517) 2025-04-09 12:04:39 +05:30
Dev Khant 00c7cc432c Remove redundant lines (#2516) 2025-04-09 11:02:37 +05:30
ytkimirti 91abc03880 Add Upstash Vector support (#2493) 2025-04-09 10:06:07 +05:30
Saket Aryan 9100e95175 Fix Batch API docs (#2512) 2025-04-07 23:54:16 +05:30
Dev Khant cdb8dcdb9e Add embeding_dims param to FAISS (#2513) 2025-04-07 23:29:55 +05:30
Dev-Khant 2a79add7a5 hotfix: _create_procedural_memory 2025-04-07 16:07:26 +05:30
Dev Khant 9dfa9b4412 Add langchain embedding, update langchain LLM and version bump -> 0.1.84 (#2510) 2025-04-07 15:27:26 +05:30
Dev Khant 5509066925 Doc: changelog update (#2509) 2025-04-07 11:51:52 +05:30
Dev Khant 3712522b14 Fix langchain llm and update changelog (#2508) 2025-04-07 11:49:39 +05:30
Dev Khant 93f34e4116 Formatting and version bump -> 0.1.82 (#2507) 2025-04-07 11:31:16 +05:30
Dev Khant 39e5cbfacc Support for langchain LLMs (#2506) 2025-04-07 11:28:30 +05:30
Dev Khant d30c78c5eb Doc: update output_format (#2498) 2025-04-03 10:46:06 +05:30
Dev Khant 039756abbe Doc: pipecat integration (#2494) 2025-04-02 18:44:04 +05:30
Mrinank Bhowmick cb38c2dae7 Feature/google as new llm and embedder in mem0-ts (#2468)
Co-authored-by: Saket Aryan <94069182+whysosaket@users.noreply.github.com>
2025-04-02 15:56:18 +05:30
Pranav Puranik 4dc9f6fad6 adding lmstudio and together in docs (#2489) 2025-04-02 11:21:27 +05:30
Anusha Yella 1b1f02eb57 fix/failing-unit-tests (#2486) 2025-04-02 10:36:02 +05:30
Dev Khant 1db1105cad Set output_format='v1.1' and update docs (#2480) 2025-04-02 10:35:29 +05:30
Saket Aryan 91a28c7f04 Docs: Flowise Integration Documentation for Mem0 Memory Setup (#2482) 2025-04-01 10:16:53 -07:00
Dev Khant a78e1894b1 Doc: Add llms.txt (#2484) 2025-04-01 22:06:20 +05:30
Sergio Toro 83338295d8 feat: add development docker compose (#2411) 2025-04-01 16:19:30 +05:30
Dev Khant aff70807c6 update faqs (#2477) 2025-04-01 00:40:50 +05:30
Dev Khant 648c86b53d doc: fix lmstudio link (#2476) 2025-04-01 00:28:02 +05:30
Dev Khant de0b2de9c2 doc: fix links (#2475) 2025-03-31 22:20:55 +05:30
Saket Aryan e3f460fba4 Added Mastra Example (#2473) 2025-03-30 11:56:00 -07:00
Saket Aryan fe7b336922 Update Demo Mem0AI (#2471) 2025-03-30 12:49:54 +05:30
Saket Aryan 1033ab227c Introduce Ping in Mem0 Client (#2472) 2025-03-30 12:49:36 +05:30
Saket Aryan 5ed15c3bcd AI SDK Updates (#2470) 2025-03-30 11:10:30 +05:30
Deshraj Yadav c1f5a655ba Minor fixes in procedural memory (#2469) 2025-03-29 17:20:58 -07:00
Deshraj Yadav 72bb631bb5 Add support for procedural memory (#2460) 2025-03-29 15:58:12 -07:00
Dev Khant 2bf9286071 update for faiss doc (#2464) 2025-03-29 13:51:01 +05:30
Dev Khant 884b597312 bump version -> 0.1.79 (#2463) 2025-03-29 13:46:00 +05:30
Dev Khant f1471bcc55 update changelog (#2462) 2025-03-29 13:39:11 +05:30
Dev Khant 9ae23f9c88 Add Faiss Support (#2461) 2025-03-29 13:35:36 +05:30
Dev Khant cbecbb7b64 doc: update email example (#2459) 2025-03-29 00:11:02 +05:30
Dev Khant f8ad0c2b2c Doc: Add example for email processing (#2458) 2025-03-29 00:09:22 +05:30
Dev Khant d0da9a1ae0 Doc: Add changelog (#2455) 2025-03-28 12:18:17 +05:30
Saket Aryan bc4ab3db77 Add Infer Property (#2452) 2025-03-27 21:18:42 +05:30
Dev Khant 0eb53b9f27 doc: update API reference for expiration_date (#2450) 2025-03-27 12:24:26 +05:30
Prateek Chhikara 3cef3ed95e Added Evaluation folder (#2448) 2025-03-26 12:37:54 -07:00
Dev Khant 45e5f2af93 Doc: Update API reference section and Add elevenlabs example (#2447) 2025-03-27 00:13:09 +05:30
Prateek Chhikara 32ba13b3ea Updated multimodal docs (#2446) 2025-03-26 09:56:16 -07:00
Parshva Daftari 69a91d5cbb Mem0 livekit example (#2442) 2025-03-26 16:06:23 +05:30
Dev Khant 2004427acd tools fix and formatting (#2441) 2025-03-26 11:25:03 +05:30
Saket Aryan 2517ccd489 fix(deployments): Add package.json file to fix deployment errors (#2440) 2025-03-26 10:31:09 +05:30
Saket Aryan 9d0300f774 Update Vercel AI SDK to support tools call (#2383) 2025-03-26 10:30:44 +05:30
Saket Aryan 366d263e0b docs(supabase-ts): Update Docs for Supabase TS (#2439) 2025-03-26 10:11:01 +05:30
Pranav Puranik 4321d24284 Open AI env var fix (#2384) 2025-03-26 08:43:33 +05:30
Dev Khant 9cb2a13f3b fix for Azure AI and version bump -> 0.1.76 (#2438) 2025-03-25 18:10:36 +05:30
Dev Khant 5ec7889d9a embedchain version bump -> 0.1.128 (#2437) 2025-03-25 13:18:34 +05:30
Dev Khant b54845bcc9 Add feeback method to client and doc changes (#2435) 2025-03-25 11:39:19 +05:30
Saket Aryan 1ae2747ff8 Add Supabase History DB to run Mem0 OSS on Serverless (#2429) 2025-03-24 16:14:29 -07:00
Parshva Daftari 953a5a4a2d Azure openai fixes (#2428) 2025-03-25 00:34:21 +05:30
Saket Aryan 2b49c9eedd Supabase Vector Store (#2427) 2025-03-25 00:15:50 +05:30
Anusha Yella 9db5f62262 fix-azure-ai-search-test-cases (#2422) 2025-03-24 15:20:02 +05:30
Dev Khant a1bd4285db version bump -> 0.1.75 (#2426) 2025-03-24 15:17:00 +05:30
Dev Khant e77a10a8da Add LM Studio support (#2425) 2025-03-24 13:32:26 +05:30
Gaurav Agerwala e4307ae420 Fix: Export ollama (#2421)
Co-authored-by: Gaurav Agerwala <ice@Gauravs-MacBook-Pro.local>
2025-03-23 02:06:23 +05:30
Saket Aryan 7c89d00079 Adds Langchain Community Package (#2417) 2025-03-22 10:44:40 +05:30
Dev Khant 563eaae5ee Openai Agents SDK voice demo (#2416) 2025-03-22 01:09:37 +05:30
Dev Khant 6733f78f81 Doc: Support for expiration date in ADD (#2419) 2025-03-21 23:38:25 +05:30
Saket Aryan c11637bd2f Update Node SDK Docs for Update Method (#2418) 2025-03-21 21:23:57 +05:30
Dev-Khant ff30cb8ddd version bump -> 0.1.74 2025-03-21 13:06:40 +05:30
Parshva Daftari 2e853c3d22 Updated VDB Docs (#2409) 2025-03-20 23:47:57 +05:30
Dev Khant 3cc7013fde fix pinecone (#2414) 2025-03-20 23:47:09 +05:30
Dev Khant 8e6a08aa83 Support for hybrid search in Azure AI vector store (#2408)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-03-20 22:57:00 +05:30
Wonbin Kim 8b9a8e5825 URGENT Hotfix - update default Elasticsearch search query (#2413) 2025-03-20 20:50:18 +05:30
Dev Khant afc630272d bump version -> 0.1.73 (#2412) 2025-03-20 19:30:29 +05:30
Parshva Daftari e33008e3a4 Add: Pinecone integration (#2395) 2025-03-20 12:57:32 +05:30
Mauricio A 7b516328a8 Feature/fix opensearch vector mapping (#2399) 2025-03-20 09:37:57 +05:30
Dev Khant 6d5889d98f version bump -> 0.1.72 (#2405) 2025-03-20 00:10:27 +05:30
Parshva Daftari ee66e0c954 Reverting the tools commit (#2404) 2025-03-20 00:09:00 +05:30
Prateek Chhikara 1aed611539 Added graph memory (#2403) 2025-03-19 09:51:15 -07:00
Saket Aryan 6c2b131d6e Added Feedback in SDK (#2393) 2025-03-19 09:11:45 -07:00
Gaurav Agerwala 2ffe9922f3 Added support for Ollama in TS SDK (#2345)
Co-authored-by: Dev Khant <devkhant24@gmail.com>
2025-03-19 21:38:19 +05:30
Dev Khant 540ada489b version bump -> 0.1.71 (#2402) 2025-03-19 21:36:06 +05:30
Dev Khant 65cffa0369 Fix: made tools support for graph (#2400) 2025-03-19 21:29:36 +05:30
Dev Khant 9f937943ba Doc: update oss quickstart page (#2401) 2025-03-19 17:27:09 +05:30
Dev-Khant 51a68bf7c5 Doc: update azure ai vector store 2025-03-18 14:32:12 +05:30
Dev-Khant 92541d8955 Doc: azure ai vector search 2025-03-18 14:17:56 +05:30
Dev Khant 0e0be18ecc Fix azure ai vector store (#2396) 2025-03-18 14:13:19 +05:30
Wonbin Kim 66d3f9b93c Support Custom Prompt for Memory Action Decision (#2371) 2025-03-18 10:43:01 +05:30
Wonbin Kim b8f40f728f Support Custom Search Query for Elasticsearch (#2372) 2025-03-18 10:34:34 +05:30
Prateek Chhikara 00a2ea9ff0 Added export instructions to docs (#2394) 2025-03-17 17:41:56 -07:00
Prateek Chhikara 9545836469 Added docs for add-v2 (#2381) 2025-03-17 15:39:17 -07:00
Saket Aryan 3acd9e20da Fix Redis Search (#2392) 2025-03-17 15:30:40 -07:00
Dev Khant d48ecd52ef update poetry lock file (#2391) 2025-03-18 01:11:05 +05:30
Saket Aryan 2fbea7705b Add Intercom to Docs (#2390) 2025-03-17 12:37:56 -07:00
Dev Khant d7a26bd0c3 Add infer param and version bump (#2389)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-03-18 01:04:58 +05:30
Farzad Sunavala e25dc4b504 bugfix: update Azure AI Search Config (#2380) 2025-03-17 22:19:46 +05:30
Parshva Daftari dab3349990 Neo4j embeddings error (#2377) 2025-03-17 21:57:23 +05:30
Saket Aryan 6db87e8d07 Make DEMO UI Responsive (#2382) 2025-03-14 20:00:28 -07:00
Saket Aryan faf811ee2d Added Custom Categories in Mem0-TS (#2370) 2025-03-14 22:07:46 +05:30
Anusha Yella ee80a43810 Remove tools from LLMs (#2363) 2025-03-14 17:42:48 +05:30
Dev Khant 4be426f762 version bump -> 0.1.68 (#2369) 2025-03-12 21:22:49 +05:30
Farzad Sunavala ba9c61938b feat: enhance Azure AI Search Integration with Binary Quantization, Pre/Post Filter Options, and user agent header (#2354) 2025-03-12 21:20:25 +05:30
Parshva Daftari 65f826e064 Fix langchain neo4j deprecation warning (#2350) 2025-03-12 15:30:45 +05:30
Saket Aryan b43363cdf3 OpenAI Inbuilt Tools (#2362) 2025-03-11 15:33:49 -07:00
Prateek Chhikara 89e786a88e Added agentic tool in docs (#2361) 2025-03-11 13:36:20 -07:00
Saket Aryan 2d5062bd40 Updated Demo (#2360) 2025-03-12 01:49:08 +05:30
Parshva Daftari b89628322d WeaviateDB Integration (#2339) 2025-03-11 00:12:17 +05:30
Dev Khant 6e4fb22a7c version bump -> 0.1.67 (#2357) 2025-03-10 23:51:30 +05:30
Dev Khant 9c0954133f Improve multimodal functionality (#2297) 2025-03-10 23:33:18 +05:30
Dev Khant e9a0be66d8 Doc: Fix examples (#2355) 2025-03-10 20:08:58 +05:30
Dev Khant 192c33f190 Doc: update examples page (#2348) 2025-03-10 12:04:44 +05:30
Dev Khant 75ca528666 Doc: Fix examples page (#2346) 2025-03-10 11:36:02 +05:30
Saket Aryan e30e4967ae Added Cloudflare Worker Compatible Configs (#2343) 2025-03-09 13:11:47 -07:00
Prateek Chhikara d3911b92cf Docs Update (#2337) 2025-03-08 10:30:42 -08:00
Dev Khant 92cfc1c8ef Doc: update examples name (#2342) 2025-03-08 23:56:00 +05:30
Dev Khant 33fcc53e4b Doc: Update name of deepresearch example (#2338) 2025-03-08 12:25:06 +05:30
Dev Khant e761a1e865 Doc: Deepresearch example (#2336) 2025-03-08 01:02:51 +05:30
Dev Khant f2ce92ebcc Update multimodal example (#2335) 2025-03-08 00:06:45 +05:30
Dev Khant bbb812e0a0 version bump -> 0.1.66 (#2334) 2025-03-07 23:56:40 +05:30
Parshva Daftari 9a302cef30 [ Fix ] for the vertex_ai_vector_search documentation (#2323) 2025-03-07 23:54:07 +05:30
Dev Khant ae729da4d1 Doc: Multimodality usecase (#2333) 2025-03-07 23:52:27 +05:30
Dev Khant 655ae794b6 Examples: Add multimodal app (#2328) 2025-03-07 23:36:32 +05:30
Dev Khant 1aef468ebe Handle empty field in new_memories_with_actions (#2330) 2025-03-07 16:51:10 +05:30
Dev Khant 78baf7495d Doc: Document editing with Mem0 (#2325) 2025-03-07 16:40:41 +05:30
Dev Khant 6cf7ac3e30 Fixes for new_memories_with_actions (#2326) 2025-03-07 13:13:00 +05:30
Dev Khant 07d2f11081 Catch json error for new_memories_with_action (#2324) 2025-03-07 13:12:00 +05:30
Prateek Chhikara c6fbba6a4d Changed multimodal prompt to extract better text from images (#2322) 2025-03-06 11:57:20 -08:00
Dev Khant b701a50b51 Doc: Update chrome extension placement (#2321) 2025-03-06 23:58:23 +05:30
Dev Khant 5865d79de7 Doc: Chrome extension (#2319) 2025-03-06 21:41:34 +05:30
Dev Khant 41a42da774 Doc: Mem0 mcp with cursor (#2318) 2025-03-06 07:46:14 -08:00
Saket Aryan 6d7ef3ae45 Multimodal Support NodeSDK (#2320) 2025-03-06 17:50:41 +05:30
yanzz 2c31a930a3 Update README.md (#2187) 2025-03-05 12:41:43 -08:00
Dev Khant c7e2a71cd5 Update cd.yml 2025-03-06 00:19:12 +05:30
Dev Khant 4237b9220b CD changes (#2316) 2025-03-06 00:10:57 +05:30
Dev-Khant cabe29c7c7 version bump -> 0.1.65 2025-03-06 00:02:18 +05:30
Mini256 80b7202db6 fix: fix sample code on README.md (#2312) 2025-03-06 00:00:13 +05:30
Rafael Nico T. Maniquiz 8c6d16a6f0 Fix Embedding Dimension Parameter Not Being Passed (#2304) 2025-03-05 20:23:36 +05:30
Dev Khant dd1f2989bc revert cd changes (#2315) 2025-03-05 17:33:17 +05:30
Dev Khant 540ec1b816 fix cd (#2314) 2025-03-05 17:14:37 +05:30
Dev Khant 728ef98d6e Doc: Update doc for both user and agent (#2313) 2025-03-05 17:05:28 +05:30
Dev Khant 329d0cc945 version bump -> 0.1.64 (#2310) 2025-03-05 16:17:19 +05:30
Dev Khant 0234c85be5 Fix CD (#2309) 2025-03-05 16:10:59 +05:30
Dev Khant eca1e06711 Doc: Update add memories (#2306) 2025-03-05 01:57:44 -08:00
Saket Aryan 2611343cbe Updated Docs to add Mem0 Demo Link/ Updated Mem0 Demo (#2305) 2025-03-05 01:11:33 -08:00
Dev Khant 8bde881e2c Add AWS lambda issue to FAQ (#2303) 2025-03-04 23:43:28 -08:00
Saket Aryan 6fdc63504a Graph Support for NodeSDK (#2298) 2025-03-04 23:22:50 -08:00
anchit-nishant 23dbce4f59 Added support for google vector search - (matching engine) (#2177) 2025-03-05 11:45:47 +05:30
Deshraj Yadav 7c8628eadc Update pyproject.toml (#2301) 2025-03-04 14:22:12 -08:00
Deshraj Yadav 20c03eaa92 [Misc] Clean up unnecessary checks in chromadb vector store integration (#2284) 2025-03-04 14:21:27 -08:00
Saket Aryan aa7ab9736d Add Mem0 Demo (#2291) 2025-03-04 10:04:59 -08:00
Dev Khant f7500c925e fix multimodal functionality and version bump -> 0.1.62 (#2296) 2025-03-04 17:51:27 +05:30
Dev Khant 8b53b1473a Add contribution docs (#2294) 2025-03-04 15:47:38 +05:30
Dev Khant c611e3e0e7 Docs: Add dify integration (#2293) 2025-03-04 14:29:25 +05:30
Taranjeet Singh bc4c15962a Fix: improve url of node js sdk (#2292) 2025-03-03 23:08:44 -08:00
Dev-Khant 6e65730b0e version bump -> 0.1.61 2025-03-03 23:32:41 +05:30
Dev Khant 8452dd598f Integrate Supabase VectorDB (#2290) 2025-03-03 23:16:24 +05:30
Dev Khant 2556c5fe88 Doc: Update examples in LLMs, VectorDBs and Embedding models pages (#2288) 2025-03-03 13:21:19 +05:30
Dev Khant a4340b2336 Fix Qdrant Tests (#2287) 2025-03-03 10:46:56 +05:30
Dev Khant f4dc5f6c71 version bump -> 0.1.60 (#2280) 2025-03-01 13:11:38 +05:30
Deshraj Yadav 32ebdaef2f [Bug Fix] Fix issue with chromadb not working with 0.6.0 and onwards (#2279) 2025-03-01 13:09:59 +05:30
Dev Khant 4318663697 Make api_version=v1.1 default and version bump -> 0.1.59 (#2278)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-03-01 11:36:20 +05:30
Saket Aryan 5606c3ffb8 Update Docs (#2277) 2025-02-28 16:37:05 -08:00
Saket Aryan c1aba35884 Mem0 TS Spec/Docs Update (#2275) 2025-02-28 21:43:51 +05:30
Dev Khant d9b48191de Doc: Update embeddings config page (#2276) 2025-02-28 21:43:00 +05:30
Dev Khant e06f95cea4 fix deprecation warning: qdrant and version bump -> 0.1.58 (#2274) 2025-02-28 16:41:45 +05:30
Dev Khant b131c4bfc4 Update max_token and formatting (#2273) 2025-02-28 15:59:34 +05:30
Wonbin Kim 6acb00731d Add config option for vertex embedding tasks (#2266) 2025-02-28 15:20:05 +05:30
Dev Khant 8143f86be6 Fix proxy pytests (#2272) 2025-02-28 15:05:26 +05:30
Dev-Khant 8d07469ba7 bump version -> 0.1.57 2025-02-28 10:55:12 +05:30
Saket Aryan 434b555a29 Updated docs for typescript package (#2269) 2025-02-27 18:22:28 -08:00
Saket Aryan f8071a753b Update AI SDK Example (#2271) 2025-02-27 18:11:59 -08:00
Saket Aryan d200691e9b Added Mem0 TS Library (#2270) 2025-02-27 15:19:17 -08:00
Dev Khant ecff6315e7 User_id creation for client and formatting (#2264) 2025-02-28 00:00:11 +05:30
Dev Khant ff4510f83d Doc: add param fields in v2 get_all (#2268) 2025-02-27 15:49:57 +05:30
Dev Khant 308e79bb68 Docs: Update v2 GET ALL endpoint (#2267) 2025-02-27 02:11:28 -08:00
Dev-Khant 48176bd194 doc: fix xai tile 2025-02-26 13:56:09 +05:30
Dev Khant 5cebe9ab52 Rename xai.mdx to xAI.mdx 2025-02-26 13:45:32 +05:30
Dev Khant 371848cfbc Doc: fix xai (#2263) 2025-02-26 13:43:38 +05:30
Dev Khant 8e3ed634d1 version bump -> 0.1.56 (#2262) 2025-02-26 13:35:25 +05:30
Dev Khant e9bc4cdc95 Add Grok Support (#2260) 2025-02-26 13:34:01 +05:30
Dev Khant a236aa2315 Doc: fix integrations page (#2261) 2025-02-26 13:18:52 +05:30
Dev Khant 5660fffa96 Doc: update example on quickstart (#2255) 2025-02-26 00:04:38 +05:30
Dev Khant eba6f77330 Doc: update anthropic model (#2254) 2025-02-25 00:59:14 +05:30
Dev Khant b5d00e9b6c Docs: set api_key to env (#2252) 2025-02-24 13:42:39 +05:30
Dev Khant 1be0d70d02 Doc: api_key changes (#2251) 2025-02-24 10:23:28 +05:30
Taranjeet Singh edb53209ef improvement: Update multimodal docs. (#2250) 2025-02-23 16:43:23 -08:00
Taranjeet Singh 7443e58a9d improvement: Update webhook docs. (#2249) 2025-02-23 16:10:21 -08:00
Dev Khant 7f25caba47 version bump -> 0.1.55 (#2248) 2025-02-23 18:07:11 +05:30
Dev Khant c42934b7fb Formatting and Client changes (#2247) 2025-02-23 00:39:26 +05:30
Dev Khant 17887b5959 Docs: Add immutable param to ADD (#2246) 2025-02-22 23:37:30 +05:30
Dev Khant 5d47f4f060 Doc: Update quickstart and Webhook page (#2245) 2025-02-22 01:24:10 +05:30
Dev-Khant 600c9fae63 update webhook js doc 2025-02-21 21:32:54 +05:30
Dev Khant 2d0c8fe94e embedchain: version bump -> 0.1.127 (#2244) 2025-02-21 17:51:47 +05:30
Dev Khant 369d5325f9 version bump -> 0.1.54 (#2243) 2025-02-21 16:03:18 +05:30
Dev Khant 29d63306a4 Webhook API reference and update/delete function change (#2242) 2025-02-21 16:01:53 +05:30
Deshraj Yadav 96628d7791 Update docs for running REST API Server (#2241) 2025-02-21 01:26:57 -08:00
Deshraj Yadav 244fd2231d Add support for Mem0 REST API Server in OSS package (#2240) 2025-02-21 01:05:55 -08:00
Dev Khant 3db028c719 Docs: update webhook (#2238) 2025-02-21 00:48:59 +05:30
Dev Khant c86b1e4d4c Docs: update webhooks (#2237) 2025-02-21 00:42:02 +05:30
Dev Khant 5f5b738745 version bump -> 0.1.53 (#2236) 2025-02-20 23:58:54 +05:30
Dev Khant acaf47ed54 Project_id mandatory for Webhooks (#2232)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-02-20 23:57:29 +05:30
cola 9734b2db7e delete same vector in retrieved_old_memory (#2201) 2025-02-20 10:10:09 -08:00
Saket Aryan 3fe31e1c31 Fix Build Errors (#2235) 2025-02-20 22:47:15 +05:30
Seetha Rama Guptha f4c0f98fde Adding Native OpenSearch support for Mem0 (#2211) 2025-02-20 11:42:12 +05:30
Dev Khant 6e781f616c Doc: Update Redis config (#2233) 2025-02-20 11:24:00 +05:30
Prateek Chhikara dcba83186a Updated docs (#2231) 2025-02-19 18:37:01 -08:00
Saket Aryan a6d305f8d0 Update Docs for Mem0 AI SDK (#2230) 2025-02-19 14:39:49 -08:00
Lennex Zinyando db512950b9 Docs updates (#2229) 2025-02-19 13:48:48 -08:00
Dev-Khant d4df9f6dfe fix webhook doc 2025-02-20 01:27:26 +05:30
Dev Khant 0e6b20982a Docs: webhook announcement (#2228) 2025-02-19 15:52:13 +05:30
Dev Khant 92e1a9b433 Docs: Update webhook (#2227) 2025-02-19 14:06:46 +05:30
Dev-Khant 3be356a0e9 Webhook doc update 2025-02-19 13:46:00 +05:30
Dev Khant 760cd54ddf Webhook Support (#2225) 2025-02-19 00:04:01 -08:00
Deshraj Yadav 1436da18b1 Update docs (#2222) 2025-02-18 17:52:32 -08:00
Prateek Chhikara cc9acb7493 Added support of vision input 2025-02-18 11:47:13 -08:00
Dev Khant cbee71a63e Proper error msg for API Key validation (#2220) 2025-02-18 23:59:59 +05:30
Dev Khant b052a86424 Update API reference to remove Org/Proj (#2221) 2025-02-18 22:34:58 +05:30
Saket Aryan 95f5fb3ab4 Fix Vercel AI SDK Build Errors (#2219)
Co-authored-by: Dev Khant <devkhant24@gmail.com>
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2025-02-18 16:03:36 +05:30
Dev Khant 04d7f2e48c Fix deprecation warning of output_format for ADD and Version Bump (#2216) 2025-02-18 10:50:56 +05:30
Lennex Zinyando be46f4eb40 [Docs] Docs update (#2199) 2025-02-17 10:30:40 -08:00
Dev Khant f16a580ef6 Fix Azure OpenAI test (#2217) 2025-02-17 19:21:12 +05:30
Dev Khant 095189d39a Update client in README (#2215) 2025-02-17 00:14:23 +05:30
Dev Khant 064d28626b Doc: Update search output (#2208) 2025-02-14 06:34:47 +05:30
Dev Khant cd31c5897a increase timeout and version bump (#2205) 2025-02-14 06:00:40 +05:30
Prateek Chhikara 0bb6137877 updated custom categories docs (#2207) 2025-02-13 11:55:04 -08:00
Saket Aryan 2c63f4d866 Added Typescript Docs and fixed a broken url (#2204) 2025-02-12 09:48:23 -08:00
Prateek Chhikara 3984b90f62 docs update (#2196) 2025-02-06 11:59:18 -08:00
Prateek Chhikara b08b50cbc6 added enhanced search params (#2195) 2025-02-05 12:34:52 -08:00
Dev Khant 90096d6954 Doc: Add config params for Memory() (#2193) 2025-02-05 12:44:41 +05:30
Prateek Chhikara 7581974805 updated docs and resolved some bugs (#2186) 2025-02-01 12:29:46 -08:00
Prateek Chhikara a8fafb9368 updated docs for mem0 architecture diagram (#2185) 2025-02-01 11:43:17 -08:00
Dev Khant 75a4a253f7 Doc: Fix full stack link (#2184) 2025-02-01 10:53:37 +05:30
Dev Khant f9995d144f Update README.md 2025-02-01 00:37:08 +05:30
junmo1215 8d3c8c695d Fix query filter in azure ai search (#2171) 2025-01-31 15:38:06 +05:30
Dev Khant 9f27b88843 Update README.md 2025-01-31 12:00:24 +05:30
Dev Khant d2f0e23dc8 Update README.md 2025-01-31 10:42:41 +05:30
Dev Khant 6e23a6f00e Update README.md 2025-01-30 23:51:28 +05:30
Dev Khant a06c9a99ae Doc changes and Storage fix (#2181) 2025-01-30 10:16:33 -08:00
Dev Khant 63fbd2dc2c Doc: update banner link (#2180) 2025-01-28 12:41:44 +05:30
Dev Khant 203943919b version bump - 0.1.48 (#2174) 2025-01-23 17:47:24 +05:30
Dev Khant 04bbad67ac DeepSeek Integration (#2173) 2025-01-23 17:45:03 +05:30
Dev Khant e1b527b73f Doc: update delete_users (#2169) 2025-01-22 13:28:12 +05:30
Dev Khant 625846caf8 Doc: update delete_users (#2168) 2025-01-22 13:25:13 +05:30
Dev Khant c1bd4e19fe version bump -> 0.1.47 (#2167) 2025-01-22 13:20:20 +05:30
Dev Khant 8d172d6139 Update delete_users (#2166) 2025-01-22 13:18:26 +05:30
Dev Khant a5355f7488 version bump -> 0.1.46 (#2164) 2025-01-21 10:07:34 +05:30
Yunsung Lee f13f3b9283 Fix/es query filter (🚨 URGENT) (#2162) 2025-01-21 10:03:50 +05:30
Deshraj Yadav 56351d1f8d Fix async client update_project method (#2155) 2025-01-19 09:05:59 +05:30
Dev Khant a9d1383909 Fix pytests (#2157) 2025-01-18 15:06:49 -08:00
Dev Khant 80c9c6a577 Doc: Update V2 Search/GetAll docs (#2158) 2025-01-18 10:43:03 +05:30
Dev Khant e4e5511642 Doc: Update API reference (#2154) 2025-01-18 01:06:22 +05:30
Dev Khant a4b085553a Code formatting (#2153) 2025-01-16 12:33:56 +05:30
Prateek Chhikara e12273c7cb changes to docs for custom categories (#2146) 2025-01-15 12:43:15 -08:00
Saket Aryan ee2b5adfc0 Fix lib/utils issue (#2151) 2025-01-15 09:49:38 -08:00
Dev Khant 205a03a5f2 Doc: Add update_project API (#2148) 2025-01-15 08:52:20 +05:30
Dev Khant 7be029a26f Doc: Custom instructions/Categories (#2147) 2025-01-15 07:51:16 +05:30
Dev-Khant 0bd177b30c version bump -> 0.1.44 2025-01-15 05:55:26 +05:30
Dev Khant 82359774b7 Custom instructions API improvements (#2140)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-01-15 05:54:23 +05:30
Dev Khant 3fa4b80deb Doc: Update ES and version bump (#2142) 2025-01-13 20:14:31 +05:30
Dev-Khant e96fd5d269 update makefile 2025-01-13 20:07:48 +05:30
Yunsung Lee 927644d712 Feat/mem0 support es (#2125) 2025-01-13 19:35:38 +05:30
Dev Khant 7397279872 HNSW support for pgvector (#2139) 2025-01-11 10:16:42 -08:00
Dev Khant 6851fac327 update api-reference for get_all (#2138) 2025-01-11 15:30:54 +05:30
Dev-Khant 254524a624 version bump -> 0.1.42 2025-01-11 13:42:17 +05:30
Dev Khant 7f0d766c09 Add support: Custom instruction/categories for projects (#2134) 2025-01-11 13:38:20 +05:30
spike-spiegel-21 ac8cf59473 entities added in proxy (#2135) 2025-01-11 01:47:42 +05:30
Dev Khant 9c4acdcba7 Doc: MemoryExport update (#2132) 2025-01-10 00:00:18 +05:30
Dev Khant a6b9721ede version bump -> 0.1.41 (#2131) 2025-01-09 20:50:47 +05:30
Dev Khant a8f3ec25b7 Code formatting and doc update (#2130) 2025-01-09 20:48:18 +05:30
Dev Khant 21854c6a24 Add support: MemoryExport API (#2129)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2025-01-09 20:43:01 +05:30
Dev-Khant 09bf7ad916 update doc 2025-01-09 18:05:14 +05:30
haarishmk26 0cc528f3b1 Commit tracking (#2127) 2025-01-09 17:40:11 +05:30
AkisAya cbd845fe41 fix VectorStoreBase abstract methods params (#2068)
Co-authored-by: Dev Khant <devkhant24@gmail.com>
2025-01-09 17:30:16 +05:30
gmdorfman 2e782b0963 feature/fixed-where-clause-default (#2042) 2025-01-09 17:21:27 +05:30
Hieu Lam 4c31c65649 Fix not working with Gemini models (#2021) 2025-01-09 17:19:26 +05:30
Mike c90f87e657 feat: allow boto3 to use its native credential finding functionality (#1536) 2025-01-09 16:59:55 +05:30
Dev Khant c63c0aca9d version bump -> 0.1.40 (#2122) 2025-01-06 16:18:41 +05:30
非法操作 d4dbed9dbd fix request mem0 without org_id raise error (#2121) 2025-01-06 16:16:13 +05:30
Dev-Khant e9188a51fe update README 2025-01-06 11:38:57 +05:30
Dev Khant d893033dcf version bump -> 0.1.39 (#2120) 2025-01-03 22:29:20 +05:30
Mayank 78a2ef41d7 [graph_memory]: improve delete/add graph memory (#2073) 2025-01-03 22:21:05 +05:30
Dev Khant 542153ad4f Update embedchain package and fix for mem0 package (#2117) 2024-12-29 00:00:40 +05:30
Dev Khant 49802137fa fix graph dependecies (#2116) 2024-12-28 23:47:55 +05:30
Prateek Chhikara 0091b31762 Updated docs about cases where memories will not be created (#2110) 2024-12-23 22:09:38 +05:30
Dev Khant 339a990510 Doc: Update API reference (#2108) 2024-12-23 17:02:49 +05:30
Saket Aryan 4c2a903618 (Update) Vercel AI SDK v0.0.10 (#2104) 2024-12-21 11:45:14 +05:30
Dev Khant 8851969169 Doc: show Token <api-key> in API-reference (#2101) 2024-12-20 16:09:34 +05:30
Dev Khant 3a5e5851dd Doc: modify Update memory API (#2099) 2024-12-20 11:13:54 +05:30
Dev Khant 716824860b Openai proxy: allow system prompt from user (#2097) 2024-12-19 15:00:53 +05:30
Dev Khant 64c80e3fbc Doc: Update V1 search params (#2096) 2024-12-19 13:08:55 +05:30
Dev Khant 9032e68917 Doc: Add Org/Proj Id to API reference (#2095) 2024-12-19 12:35:42 +05:30
Dev Khant e7da9eb00c Doc: update custom categories (#2094) 2024-12-18 13:03:50 +05:30
Dev Khant 6b99457381 Doc: Update Batch API-Reference (#2092) 2024-12-17 13:11:57 +05:30
Saket Aryan be4188c6b4 (docs) Updated docs as for using agent_id (#2090) 2024-12-16 16:02:29 +05:30
Dev Khant 4d08c16bd8 Update validate_api_key (#2089) 2024-12-15 11:34:12 +05:30
Saket Aryan 763f804277 (Update) Vercel AI SDK Memory Saving Algo (#2082) 2024-12-15 11:14:16 +05:30
Pranav Puranik 400b1f4eac Fixing the fact extraction prompt (#2037) 2024-12-13 06:42:25 +05:30
Dev-Khant 6bc86d7d2c Add keywords param to docs 2024-12-09 11:57:14 +05:30
Dev Khant df68a6b397 Doc: Update batch delete (#2078) 2024-12-08 13:45:40 +05:30
Dev-Khant b739cbbc2e update note in custom_categories 2024-12-06 13:47:12 +05:30
Dev Khant f645e50c5c update custom categories doc (#2074) 2024-12-06 13:03:33 +05:30
Feizhi Cai 2eff703e72 Update embedchain README.md (#2060) 2024-12-03 20:47:35 +05:30
Dev Khant dd06333732 Doc update (#2065) 2024-11-28 17:06:05 +05:30
Dev Khant 847e1cc986 Add support for batch update/delete (#2064) 2024-11-28 15:42:35 +05:30
Prateek Chhikara af29ecc93f Added langchain tools in the docs (#2063) 2024-11-27 18:10:44 -08:00
Dev Khant 52eaddbd8a add page and page_size in api-reference (#2061) 2024-11-27 16:10:31 +05:30
Dev-Khant 06d1757038 update get_all v2 2024-11-27 10:57:27 +05:30
Dev Khant 9900b4f5e4 Support categories filtering for GET_ALL API (#2058) 2024-11-27 10:48:06 +05:30
Saket Aryan 25ef5dda53 (Docs) Updated Docs to Include V2 Paginated/Non-Paginated Output (#2056) 2024-11-26 13:57:04 +05:30
Dev-Khant 2e1d257f36 version bump-> 0.1.33 2024-11-26 13:17:15 +05:30
Saket Aryan 4b34751bda (Docs) Updated docs for new paginated output format (#2055) 2024-11-26 12:53:07 +05:30
Dev Khant ba9c2e68f9 Doc: show output_format as param in API reference (#2053) 2024-11-26 00:24:41 +05:30
Dev Khant 86d3e36ace pass page and page_size in query params (#2052) 2024-11-26 00:00:49 +05:30
Dev-Khant 7284317cef revert pagination change and version bump 2024-11-22 19:48:27 +05:30
Dev-Khant 1508a5a418 version bump 2024-11-22 19:31:39 +05:30
Dev Khant 9b55b717e0 Handle pagination for GET_ALL (#2044) 2024-11-22 19:30:28 +05:30
Saket Aryan 8c087fcabc (Docs Update) update mem0ai usage examples to use ES6 imports (#2041) 2024-11-21 23:35:07 +05:30
Mayank 4b8e32830a [improvement]: Graph nodes extraction improved (#2035) 2024-11-21 12:27:39 +05:30
Prateek Chhikara 62ca0ddbe2 Version bump (#2038) 2024-11-20 10:21:30 -08:00
Mayank bcb41f85c9 [docs]: LlamaIndex ReAct agent tutorial added (#2036) 2024-11-20 23:38:51 +05:30
Mayank 751f5d5a19 [bug_improvement]: Update hash changed and Vector base class improved (#2034) 2024-11-20 23:18:34 +05:30
Mayank 5ab09ffd5a [Redis]: Vector database added. (#2032) 2024-11-20 17:12:16 +05:30
Saket Aryan 13374a12e9 (Feature) Vercel AI SDK (#2024) 2024-11-19 23:53:58 +05:30
Dev Khant a02597ed59 Update embedder docs to show openai key is used for LLM (#2033) 2024-11-18 16:25:23 +05:30
Dev-Khant 8a56f0ed4a API Reference: add categories to input params for v1 search 2024-11-15 21:23:33 +05:30
Dev Khant fd7fab4e08 Doc: add example for filtering through categories and metadata (#2031) 2024-11-15 13:45:46 +05:30
Dev-Khant e909e3e76c update announcement message on doc 2024-11-15 01:02:47 +05:30
Dev Khant 1ebe5b643d Add CrewAI Usage doc (#2029) 2024-11-15 00:59:52 +05:30
Mayank c0b9a10224 [llama_index_docs]: Added few blocks (#2023) 2024-11-14 21:08:32 +05:30
Dev Khant 802231c105 Update Org doc (#2026) 2024-11-14 01:15:01 +05:30
Saket Aryan 0d5085454b (Docs) Updated Docs to include mem0-node (#2022) 2024-11-11 10:06:57 -08:00
Mayank 6d535951df [graph_improvement]: Unique Id removed from update prompt (#2020) 2024-11-08 13:43:24 -08:00
Dev Khant eaf295756e Version bump and upgrade chromadb version (#2019) 2024-11-08 16:07:21 +05:30
Mayank 11894c64b3 [docs]: llama index docs added (#2018) 2024-11-07 01:24:09 -08:00
Xiang Wang b9e22beecb refine prompt of graph memory extract entities for search (#2013) 2024-11-07 00:23:16 -08:00
Dev-Khant 6f051036d9 fix api-reference for nodejs 2024-11-07 12:52:30 +05:30
Dev Khant 3731965537 Version bump and client fixes (#2017) 2024-11-07 11:36:56 +05:30
Dev-Khant 549e5e3ce8 doc fix 2024-11-07 10:59:53 +05:30
Dev-Khant 48bbfcbb2c version bump -> 0.1.28 2024-11-07 10:46:56 +05:30
Dev Khant 4cc91d7505 Add support for Org/Proj ID (#2014) 2024-11-07 10:46:08 +05:30
Dev Khant 77b0912808 Fixes for API reference (#2010) 2024-11-05 23:16:31 +05:30
Saket Aryan 6a00643bfa Docs: Integration/Vercel AI SDK (#2009) 2024-11-04 07:19:51 -08:00
Dev-Khant 2e74667cc6 Docs: reposition audio in data sources 2024-11-03 12:39:26 +05:30
Dev Khant d2b653ab10 Add audio as data source to Docs (#2007) 2024-11-03 12:35:17 +05:30
Dev Khant 2c94e6b817 version bump -> 0.1.27 (#2006) 2024-11-02 22:52:43 +05:30
Dev Khant a6ac4a6698 Replace UUID with indexes to reduce hallucinations (#2004) 2024-11-02 12:40:55 +05:30
Dev Khant e7cc8b9552 update ADD response (#2005) 2024-11-02 12:40:24 +05:30
Dev Khant f6290a0e48 Modify docs to update response format (#2002) 2024-11-01 13:22:20 +05:30
Xiang Wang 6668be3d5b fix typo in the prompt of get_update_memory_messages (#2000) 2024-10-31 21:04:59 -07:00
Xiang Wang cf12148bc7 remove redudant code from graph_memory.py (#1999)
Co-authored-by: Wang Xiang <wangxiang1@ztgame.com>
2024-10-31 16:00:55 -07:00
Mayank d928ea4a2b [integration]: Together embedder added (#1995) 2024-10-30 09:51:01 -07:00
Dev Khant efd45c0c4d Remove session_id deprecation warning (#1994) 2024-10-30 15:32:40 +05:30
Dev-Khant 4896d5c66f fix vectordb doc 2024-10-29 22:28:52 +05:30
Mohamad 61a24f011a Feature - Support Azure AI Search as a Vector DB (#1967)
Co-authored-by: Sidney Phoon <sidneyphoon17@gmail.com>
2024-10-29 22:12:39 +05:30
Dev Khant 8d9eb225a8 version bump -> 0.1.25 (#1992) 2024-10-29 11:37:10 +05:30
Dev Khant 605558da9d Code formatting (#1986) 2024-10-29 11:32:07 +05:30
Mayank dca74a1ec0 [docs]: Quickstart docs changed for v1.1 responses (#1990) 2024-10-28 15:22:18 -07:00
Dev Khant fb3eef6cf5 Proper error message if api key not found (#1985) 2024-10-26 00:04:40 +05:30
Dev-Khant 10d3209e5a Doc: add link for claude models 2024-10-24 22:44:16 +05:30
Dev Khant aace88d2e7 Update docs for AsyncClient (#1984) 2024-10-24 16:48:57 +05:30
Dev-Khant 228eaa16d5 fix customer-support-chatbot notebook 2024-10-24 09:48:28 +05:30
Dev-Khant f3416aa46a version bump -> 0.1.23 2024-10-24 09:35:10 +05:30
Dev Khant eb32fb912d Fix LLM config and Doc update for anthropic (#1983) 2024-10-23 12:54:04 -07:00
Dev Khant 8c4ee7569f version-bump -> 0.1.22 (#1981) 2024-10-22 12:46:28 +05:30
Dev Khant fbf1d8c372 Support async client (#1980) 2024-10-22 12:42:55 +05:30
Dev Khant c5d298eec8 Remove unnecessary tools (#1979) 2024-10-22 11:47:16 +05:30
Abhay Shukla 078aa66b90 Implemented Gemini (#1490) (#1965) 2024-10-21 16:23:26 +05:30
Dhanush d4ffed9822 Fixed typos: gitignore & config.mdx (#1974) 2024-10-21 15:03:39 +05:30
Jian Yu, Chen ff7761aaf8 Add Support for Customizing default_headers in Azure OpenAI (#1925) 2024-10-19 15:57:28 +05:30
Prateek Chhikara f058cda152 Version bump (#1973) 2024-10-18 10:33:15 -07:00
Deshraj Yadav b6f9054567 Add npmjs package badge on README (#1972) 2024-10-17 17:33:03 -07:00
Dev Khant 5667bc1eab Add V2 get_all (#1969) 2024-10-17 11:41:59 +05:30
Prateek Chhikara 4661d55913 Updated docs to add the "Direct Memory Storage" feature (#1966) 2024-10-16 09:59:13 -07:00
Dev Khant 9c52d72dc1 Add langchain doc (#1963) 2024-10-16 15:22:52 +05:30
femto 2cd9f94ea6 add response to m.add() call (#1732) 2024-10-15 15:53:18 -07:00
Dev Khant 2b262a65b2 Update graph doc for installation (#1959) 2024-10-15 08:13:41 -07:00
Farookh Zaheer Siddiqui bd5ce7c6d2 [Docs] : Fix typos in docs (#1960) 2024-10-15 17:23:53 +05:30
Vatsal Rathod 20c3aee636 Adding fetching data functionality for reference links in the web page (#1806) 2024-10-15 16:56:35 +05:30
Parshva Daftari 721d765921 [ Fix ]TypeError when using Chat completion (#1922) 2024-10-15 16:54:07 +05:30
sarkarsaurabh27 84eb666618 Adding autogen cookbook to help provide options of integration with a multi-agent framework (#1908) 2024-10-15 16:52:18 +05:30
Mayank 3f2d5bee34 [bug]: Memory.reset() deletes collection and table without re-creating it (#1952) 2024-10-15 16:46:50 +05:30
Deshraj Yadav 9341d9f597 Make graph memory related dependencies optional (#1954) 2024-10-15 11:54:07 +05:30
Dev Khant aacc7c25d3 update python code in API reference (#1957) 2024-10-14 12:50:56 +05:30
Deshraj Yadav b59fbb0bd2 Change ping endpoint for validating api key (#1956) 2024-10-12 14:54:50 -07:00
Dev Khant ae7b1a666e Reordering of code blocks for API reference page (#1953) 2024-10-11 18:41:59 +05:30
Deshraj Yadav bf57d253a5 Update README.md (#1949) 2024-10-09 12:36:19 -07:00
Parshva Daftari c689f94c52 [Add] Error handling for update method in OSS & platform code. (#1939) 2024-10-08 15:04:59 +05:30
Dev Khant ab862d0d40 Add custom_categories in get_all docs (#1943) 2024-10-05 11:12:05 +05:30
Deshraj Yadav 29178a4c72 Update mint.json (#1940) 2024-10-04 00:50:37 -07:00
Divyanshu Prasad d107b639b3 (bug-fix) : fix VertexAI missing configurations (#1926) 2024-10-03 21:34:14 +05:30
Parshva Daftari c09c4926a7 [ Refactored ] embedding models and [ Update ] documentation for Gemini model (#1931) 2024-10-03 21:30:46 +05:30
Dev Khant 395af18d88 chore: version -> 0.1.19 (#1937) 2024-10-03 11:25:48 +05:30
k10 ecefb793fc fixes - 1911 autoindex does not require params (#1921) 2024-10-02 16:51:13 -07:00
Dev Khant c6b9035956 Update langchain dependencies and version bump for embedchain (#1935) 2024-10-02 12:35:04 +05:30
Dev Khant 3513a9def6 version bump and update langchain-community (#1934) 2024-10-01 22:25:42 +05:30
Prateek Chhikara 0d45c61aa3 Graph memory bug fix (#1932) 2024-09-30 16:55:01 -07:00
Parshva Daftari f324462cc3 Update contributing.md (#1918) 2024-10-01 00:55:48 +05:30
dbcontributions 52bd8fca5c add-missing-response_format-parameter (#1927) 2024-10-01 00:40:22 +05:30
Dev Khant c45f14e77d fix limit param in graph memory (#1930) 2024-10-01 00:15:55 +05:30
Dev Khant 0dbfcbe6d9 multiline code for openai doc (#1929) 2024-09-30 23:21:32 +05:30
Dev Khant 23279d4248 Improve openai compatibility page (#1928) 2024-09-30 12:22:12 +05:30
Dev Khant 68c7355f47 Add limit in get_all and search for Graph (#1920) 2024-09-28 01:51:38 +05:30
Pranav Puranik aaf8e6e7ff Adding Gemini (#1862) 2024-09-27 22:16:40 +05:30
Dev Khant 699741c760 fix links (#1916) 2024-09-27 01:12:21 +05:30
Dev Khant 2d3dda3a4c Fix return types for client methods (#1914) 2024-09-26 22:10:51 +05:30
dbcontributions 61dd5a5ea4 Add vertexai test cases (#1907) 2024-09-26 21:33:55 +05:30
Dev Khant 41be228e5c chore: version -> 0.1.16 (#1904) 2024-09-25 20:07:20 +05:30
Parshva Daftari 0491854298 Fixing the bug when using Huggingface Models (#1877)
Co-authored-by: parshvadaftari <parshva@192.168.1.5>
2024-09-25 20:04:40 +05:30
Parshva Daftari 44ee48e924 [ Fix ] for the failing embedchain tests (#1899) 2024-09-25 20:02:53 +05:30
Mayank 5525c4e6fe [improvement]: Duplicate embedding generation removed. (#1900) 2024-09-25 09:54:30 +05:30
Dev Khant 3914f4d6ac Fix langgraph doc (#1898) 2024-09-24 11:16:33 +05:30
Mathew Shen 8511eca03b fix(llm): consume llm base url config with a better way (#1861) 2024-09-24 10:05:09 +05:30
Dev Khant 56ceecb4e3 chore: embedchain version -> 0.1.122 (#1896) 2024-09-23 15:16:51 +05:30
Dev Khant db5cb1986a Add organizations/projects support (#1857) 2024-09-20 10:51:02 +05:30
Deshraj Yadav 6102aa76bb Remove stale code and events improvements (#1883) 2024-09-18 14:14:21 -07:00
Dev Khant fc88cae628 update milvus docs (#1876) 2024-09-18 00:40:22 +05:30
Prateek Chhikara 8c3c9e1520 Docs update (#1875) 2024-09-17 10:53:14 -07:00
Deshraj Yadav 55c54beeab [Misc] Lint code and fix code smells (#1871) 2024-09-16 17:39:54 -07:00
Anusha Kondam 0a78cb9f7a added vector store test cases (#1868)
Co-authored-by: Dev Khant <devkhant24@gmail.com>
2024-09-16 23:51:29 +05:30
Dev Khant 3502344e89 Remove auto install library for chromadb (#1870) 2024-09-16 11:22:08 +05:30
Dev Khant 30edf49aaf chore: version -> 0.1.14 (#1869) 2024-09-16 11:15:22 +05:30
Dev Khant 5b9be679a8 Migrate session_id -> run_id (#1864)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2024-09-16 09:30:21 +05:30
Dev Khant 8e2f7f2bfb Shows all responses in api-reference (#1865) 2024-09-14 10:08:59 -07:00
Dev Khant dc5a26fe95 Update docs for exisiting APIs to support organization/project (#1859) 2024-09-14 10:33:38 +05:30
Dev Khant d66654bf67 Add API-Reference docs for Organization/Project (#1858) 2024-09-14 10:33:25 +05:30
Divyanshu Prasad 959f4bb059 Add Support for Vertex AI Embeddings (#1840) 2024-09-13 17:09:25 +05:30
Anusha Kondam f9634b4bf3 add test cases for embeddings (#1829) 2024-09-13 17:06:51 +05:30
FoliageOwO 47a8e677e9 Fixed environment variables priority in OpenAILLM (#1851) 2024-09-12 11:07:15 -07:00
Anusha Yella f40a2e7603 Add CONTRIBUTING.md (#1836)
Co-authored-by: Anu <buildknowledge111@gmail.com>
2024-09-11 22:41:14 +05:30
Prateek Chhikara ac7b7aa20a Added custom prompt support (#1849) 2024-09-10 16:57:32 -07:00
Pranav Puranik 5eeeb4e38c Fixing memory adding errors (#1848) 2024-09-10 14:37:44 -07:00
Deshraj Yadav db835cdcb8 Update README.md (#1847) 2024-09-10 11:27:55 -07:00
Dev Khant e3aca7026b chore: version -> 0.1.12 (#1846) 2024-09-10 22:11:28 +05:30
k10 3bd49b57cc Feature: milvus db integration (#1821) 2024-09-10 22:06:50 +05:30
Prateek Chhikara 5b9b65c395 Doc Updates (#1843) 2024-09-09 19:06:22 -07:00
Anusha Yella bbddb98aca Update docstring (#1837)
Co-authored-by: Anu <buildknowledge111@gmail.com>
2024-09-09 10:26:04 -07:00
Prateek Chhikara b081e43b8d Modified the return statement for ADD call | Added tests to main.py and graph_memory.py (#1812) 2024-09-09 10:04:11 -07:00
k10 58f29d8781 Make anonymous telemetry optional #1765 (#1774) 2024-09-09 09:59:06 -07:00
Shlok Khemani f01e8a083e improved docs (#1834) 2024-09-09 15:59:12 +05:30
Kirk Lin 7170edd13f feat: openai default model uses gpt-4o-mini (#1526) 2024-09-09 12:58:28 +05:30
Mayank bf0cf2d9c4 [minor]: mem0ai version changed for embedchain (#1826) 2024-09-09 11:33:41 +05:30
Mayank 51c4f2aae8 [improvement]: Graph memory support for non-structured models. (#1823) 2024-09-07 13:26:43 -07:00
Dev Khant a972d2fb07 Code Formatting (#1828) 2024-09-07 22:39:28 +05:30
Dev Khant 6a54d27286 fix v2 search doc (#1830) 2024-09-07 21:26:00 +05:30
Dev Khant d32ae1a0b1 Add support for anthropic (#1819) 2024-09-07 02:12:22 +05:30
Mathew Shen 965f7a3735 feat(memory): keep memory language (#1818) 2024-09-05 21:50:53 +05:30
Mathew Shen 136b5545ec fix: get config from config value first then environment variable (#1815) 2024-09-05 15:05:52 +05:30
Mathew Shen 8099d60e0e docs: fix docstring (#1816) 2024-09-05 15:00:37 +05:30
Prateek Chhikara da2fd1a51a Bug fixes and version bump (#1811) 2024-09-04 11:25:03 -07:00
Dev Khant 18d069d10c Improve api reference for v2 search api (#1808) 2024-09-04 10:21:32 -07:00
Dev Khant 851b665c11 Add contributing doc (#1794) 2024-09-04 10:18:40 -07:00
Yuhang c674625e88 Fix bug about MemoryGraph can't find (#1810) 2024-09-04 09:54:11 -07:00
Dev Khant 23e2ed2163 version bump (#1807) 2024-09-04 11:30:01 +05:30
Dev Khant 0b1ca090f5 Add loggers for debugging (#1796) 2024-09-04 11:16:18 +05:30
Prateek Chhikara bf3ad37369 Added parallelization to memory method calls to reduce latency (#1803) 2024-09-03 18:50:16 -07:00
Dev Khant f21ca9b765 Update add method and prompts (#1775) 2024-09-03 17:12:35 -07:00
Prateek Chhikara d113037a4f Bug fixes in docs (#1802) 2024-09-03 13:16:00 -07:00
Prateek Chhikara b2f683f3cc Bug fixes in docs (#1801) 2024-09-03 13:11:11 -07:00
Pranav Puranik 83eb800fb3 Adding v1.1 code snipper for personal-travel-assistant (#1785) 2024-09-03 11:53:39 -07:00
Arthur Howard af8454811c Fixed issue 1520 - the collect_metrics options for the app is now taken into account for all actions (#1680) 2024-09-03 23:45:57 +05:30
Prateek Chhikara 8dbfe28bbb Version increment (#1800) 2024-09-03 11:08:14 -07:00
Mark Bain 5d53b0c2ca Added rank_bm25 dependency (#1790) 2024-09-03 11:00:16 -07:00
Prateek Chhikara 65056311a6 Added user_id support for graph memory 2024-09-03 09:47:35 -07:00
Jaimin Godhani d03ba0fc8a feat: Automate installation of required libraries. (#1795) 2024-09-03 12:37:57 +05:30
Jaimin Godhani 9804f078d0 feat: automates package installation (#1780) 2024-09-02 20:23:47 +05:30
Dev Khant c886764b62 version bump (#1793) 2024-09-02 20:14:01 +05:30
Mathew Shen 2262fadd5b docs(readme): add pypi related badges (#1792) 2024-09-02 20:09:16 +05:30
dbcontributions 462aaebd6c added-pre-commit-configuration (#1782) 2024-09-01 02:11:07 +05:30
k10 077d0c47f9 AzureOpenAI Embedding Model and LLM Model Initialisation from Config. (#1773) 2024-09-01 02:09:00 +05:30
Anusha Kondam ad233034ef add-reset-api-for-client (#1783) 2024-09-01 02:01:55 +05:30
Prateek Chhikara 9d0932971d Graph memory docs update (#1786) 2024-08-31 03:47:23 +05:30
Prateek Chhikara 822a8acedb Improvements to Graph Memory (#1779) 2024-08-29 22:17:08 -07:00
Jaimin Godhani 28bc4fe05b Improve: consistency in the test_memory.py (#1777) 2024-08-29 11:36:01 -07:00
Mathew Shen df5b7109f5 fix(docs): memory addition return type (#1771) 2024-08-29 15:34:14 +05:30
Mathew Shen 4bbbc904b6 docs: add openai_base_url related docs (#1766) 2024-08-29 15:20:45 +05:30
Pranav Puranik fee3c27af3 Adding proxy server settings to azure openai (#1753) 2024-08-29 15:18:50 +05:30
Prateek Chhikara deeb4f2250 Modified the location of graph memory's colab link (#1769) 2024-08-28 13:37:09 -07:00
Prateek Chhikara a80796b5ff Added Google Colab link for Graph Memory (#1764) 2024-08-27 16:24:26 -07:00
Dev Khant a279ed0694 Fixes in API-reference page (#1763) 2024-08-27 23:01:33 +05:30
Dev Khant 06d6fe7d76 version bump (#1762) 2024-08-27 21:51:19 +05:30
Dev Khant 6057cf5202 API reference docs for Search V2 (#1760) 2024-08-27 09:06:46 -07:00
Tibor Sloboda a94bd11a76 Distance metric change and PGVectorScale support (#1703) 2024-08-27 16:56:01 +05:30
Pranav Puranik e8004537c1 get_all returns dictionary (#1756) 2024-08-27 16:26:54 +05:30
Dev Khant c8a47b2f98 add api-reference for custom categories (#1749) 2024-08-27 12:31:18 +05:30
Dev Khant c545dcf412 version bump (#1757) 2024-08-27 11:39:14 +05:30
ParseDark b80925e857 [openai_api_base support] - ft/Added openai OPENAI_API_BASE llm config support (#1737) 2024-08-25 16:25:14 +05:30
Prateek Chhikara 3fb4f2655b Added graph memory video in docs (#1745) 2024-08-24 16:02:46 -07:00
Prateek Chhikara 324e17b226 Added docs for custom categories (#1744) 2024-08-24 10:10:04 -07:00
Dev Khant b3d6e645b7 Add Search V2 (#1738) 2024-08-23 23:56:18 +05:30
Dev Khant cb2f86551b Add API Reference docs (#1742) 2024-08-23 16:52:54 +05:30
Prateek Chhikara 4f5a40a84f Docs fixes (#1730) 2024-08-22 11:01:26 -07:00
Prateek Chhikara ea86dc1576 Added customized memory to docs (#1729) 2024-08-21 15:14:11 -07:00
Deshraj Yadav 7de35b4a68 [Mem0] Update docs and improve readability (#1727) 2024-08-21 00:18:43 -07:00
Max von Hippel 2d66c23116 Make home and mem0 dirs configurable so that the service can work on AWS lambda. (#1726) 2024-08-20 23:52:37 -07:00
Prateek Chhikara 515fb86497 Readme Changes (#1725) 2024-08-20 22:49:44 -07:00
Prateek Chhikara 8ea12ca24b Added neo4j dependency (#1724) 2024-08-20 17:06:32 -07:00
Prateek Chhikara 448a21f617 Version Update (#1723) 2024-08-20 16:53:44 -07:00
Prateek Chhikara a7f5fb59c3 Add langchain-community as a dependency (#1722) 2024-08-20 16:50:17 -07:00
Prateek Chhikara c64e0824da [Mem0] Integrate Graph Memory (#1718)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2024-08-20 16:37:38 -07:00
anifort 9b7a882d57 langchain_community.embeddings is depricated and replacing with langc… (#1717) 2024-08-20 16:13:17 +05:30
Dev Khant e3a3a48973 version bump (#1721) 2024-08-20 14:58:09 +05:30
Dev Khant 6a9f5341b5 User ID needed for .add() and .search() (#1719) 2024-08-20 14:37:17 +05:30
Dev Khant e31ca239a0 Add autogen docs (#1720) 2024-08-20 14:36:38 +05:30
dbcontributions 0e0d0b8fc7 Improvement/add getting api key from env (#1710) 2024-08-19 22:35:28 +05:30
Dev Khant 8476ce5d8e bump version for embedchain (#1716) 2024-08-17 10:35:13 +05:30
Deshraj Yadav 047a85711e Update mint.json file (#1713) 2024-08-16 02:04:12 -07:00
Dev Khant 32ea60a5fc Add ollama example (#1711) 2024-08-16 00:46:12 +05:30
Dev Khant 1e39a22c52 Add langgraph doc (#1712) 2024-08-16 00:41:53 +05:30
Dev Khant daa65e7b02 fix litellm doc link (#1709) 2024-08-15 21:22:19 +05:30
Dev Khant eb7a7e09eb Add configs to llm docs (#1707) 2024-08-15 21:13:00 +05:30
从零开始学AI c0232a7d97 [embedchain doc] fix typo mysql.mdx (#1678) 2024-08-15 12:01:58 +05:30
Deshraj Yadav a8ba7abb7d [Mem0] Update dependencies and make the package lighter (#1708)
Co-authored-by: Dev-Khant <devkhant24@gmail.com>
2024-08-15 11:58:07 +05:30
rajib e35786e567 added dotenv in .toml, added an example to use qdrant, fixed the code in main.py (#1653) 2024-08-14 23:18:24 +05:30
Dev Khant 214a1ddca5 add notebook link in multion page (#1705) 2024-08-14 22:18:45 +05:30
Dev Khant 10cbee943c Add configs to Embedding docs (#1702) 2024-08-14 16:10:48 +05:30
Dev Khant aba5bb052d Fix chroma get_all method (#1701) 2024-08-14 15:04:56 +05:30
dbcontributions a461091ba5 adding param and return types (#1689) 2024-08-14 01:16:55 +05:30
Dev Khant 64218db7bd Add configs to VectorDB docs (#1699) 2024-08-13 11:57:04 -07:00
Dev Khant 2180b83a8b Handle telementry exception (#1698) 2024-08-13 10:42:51 -07:00
Dev Khant f19dfe70d7 Add delete users method (#1683) 2024-08-13 22:35:04 +05:30
Samuel Devdas 31ef9135e7 Update Config params when using Local Ollama models (#1690) 2024-08-13 11:58:14 +05:30
Taranjeet Singh 5cea47947c feat: add how mem0 works in the docs (#1694) 2024-08-13 11:34:22 +05:30
Taranjeet Singh 883ffd7de0 feat: add docs about how mem0 works (#1693) 2024-08-13 11:31:47 +05:30
Dev Khant e66f277324 Embedding docs fix (#1692) 2024-08-13 11:29:04 +05:30
Dev Khant 01cfad62b1 Version bump (#1687) 2024-08-13 00:20:29 +05:30
Dev Khant 6cc4a31e91 Add support for pgvector (#1675) 2024-08-13 00:15:08 +05:30
Dev Khant 629bb5bb63 embedding doc fix (#1685) 2024-08-12 16:20:06 +05:30
Dev Khant b245309242 Add embedder docs and config changes (#1684) 2024-08-12 16:09:01 +05:30
Mitul Kataria 464a188662 Add support for configurable embedding model (#1627)
Co-authored-by: Dev Khant <devkhant24@gmail.com>
2024-08-12 15:09:18 +05:30
dbcontributions 4aae2b5cca added on_disk param to qdrant configs (#1677) 2024-08-12 15:08:44 +05:30
Deshraj Yadav 442f6d72d0 [Docs] Fix numbering in docs (#1673) 2024-08-09 00:33:15 -07:00
Deshraj Yadav d1c5c70b4c [Mem0] Add support for getting all users in python client (#1672) 2024-08-09 00:27:54 -07:00
krescent e570888437 Update vectordb.mdx (#1670) 2024-08-09 02:41:19 +05:30
Dev Khant 38965ab6bf Docs for using Ollama locally (#1668) 2024-08-09 02:40:39 +05:30
Shlok Khemani 7a2fd70184 Companion Example (#1669) 2024-08-08 21:28:39 +05:30
cat 388c70789a fix serialized_existing_memories dump field bug (#1644) 2024-08-08 15:07:49 +05:30
freshield.eth d5b3eda16c fix cookbook memories key changes from text to memory (#1652) 2024-08-08 15:07:05 +05:30
krescent 8c8f4120f4 Update factory.py (#1657) 2024-08-08 15:06:15 +05:30
Taranjeet Singh e190445492 feat: Add page for FAQ (#1667) 2024-08-08 10:46:46 +05:30
Taranjeet Singh 2dcbcdf7a5 feat: Move core features at the top (#1665) 2024-08-08 08:31:28 +05:30
Taranjeet Singh 0ccb1124bd feat: Add features doc (#1664) 2024-08-08 08:10:45 +05:30
Taranjeet Singh 874c2e96ca Improve docs and readme (#1663) 2024-08-08 07:44:49 +05:30
Taranjeet Singh 4a643a8449 feat: Improve readme (#1661) 2024-08-08 02:50:15 +05:30
Taranjeet Singh 37cacb27ec feat: Fix banner image (#1660) 2024-08-08 00:58:34 +05:30
Taranjeet Singh a14a4405db feat: change banner image (#1659) 2024-08-08 00:52:18 +05:30
Taranjeet Singh 7ba46ec5ec feat: Improve readme (#1658) 2024-08-08 00:45:15 +05:30
Dev Khant 296327793c Fix lint issues (#1656) 2024-08-07 15:08:36 +05:30
Taranjeet Singh 4af6288adb feat: update readme (#1655) 2024-08-07 10:38:57 +05:30
Taranjeet Singh de7ee38e45 feat: Improve readme structure (#1654) 2024-08-07 09:38:17 +05:30
Dev Khant 9f6ec325fb version bump (#1641) 2024-08-04 21:13:38 +05:30
Dev-Khant b10ec8c34a Fix docs and config for vector store 2024-08-04 21:10:51 +05:30
dbcontributions b6cfd960d1 Fix/ollama test cases (#1639) 2024-08-04 12:56:25 +05:30
Dev Khant 5aa7bedabe Handle chromadb dep and version bump (#1638) 2024-08-04 00:07:15 +05:30
Dev Khant 04b4807145 Support for Openrouter (#1628) 2024-08-03 22:51:03 +05:30
Dev Khant 5837991e5c Fix config for vector store (#1637) 2024-08-03 21:48:27 +05:30
Mitul Kataria 81b4431c9b Support Azure OpenAI LLM (#1581) 2024-08-03 20:31:43 +05:30
Dev Khant 504a87d799 doc fix for components (#1636) 2024-08-03 18:58:34 +05:30
Dev Khant 024089d33e Add ollama embeddings (#1634) 2024-08-03 10:55:40 +05:30
Dev Khant 1c46fddce1 Fix litellm issue (#1635) 2024-08-03 09:23:14 +05:30
dbcontributions 784b607613 Added user_id while updating memory (#1613) 2024-08-02 23:58:28 +05:30
Dev Khant 44aa16a0f8 Support Ollama models (#1596) 2024-08-02 23:45:45 +05:30
Dev Khant 3eff82082e fix readme (#1633) 2024-08-02 21:23:00 +05:30
Dev Khant d2f6fce52e Version bump for Mem0 and Embedchain (#1632) 2024-08-02 08:35:13 -07:00
Dev Khant 419dc6598c Add OpenAI proxy (#1503)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2024-08-02 20:14:27 +05:30
krescent 51092b0b64 Update ollama.py (#1619) 2024-08-02 01:42:22 +05:30
Mathew Shen 918823d805 fix: method name should be same with abstrct base class (#1592) 2024-08-02 01:31:19 +05:30
Mitul Kataria e287fb9a89 Fixed import for EmbeddingBase class for ollama and huggingface embedders (#1548) 2024-08-02 01:20:16 +05:30
Pranav Puranik ab4f872502 correcting prompt for UPDATE_MEMORY_PROMPT (#1535) 2024-08-02 01:12:11 +05:30
Kirk Lin 8be831f7ed docs: fix guidelines link (#1530) 2024-08-02 01:09:17 +05:30
Dev Khant 67c775276f update doc to have openai key (#1614) 2024-08-02 01:07:51 +05:30
Xie Yanbo 12a4934112 fix typo (#1524) 2024-08-02 01:03:24 +05:30
Pranav Puranik b386e24f5d Sets up metadata db for every llm class (#1401) 2024-08-02 00:45:28 +05:30
Dev Khant 58b6887bf5 llm doc fix (#1631) 2024-08-02 00:41:21 +05:30
Mathew Shen 2269194662 docs(memory): add field doc (#1501) 2024-08-02 00:30:22 +05:30
Ikko Eltociear Ashimine 1290c1bb2e chore: update base.py (#1480) 2024-08-02 00:28:17 +05:30
Pranav Puranik c197a5fe93 AzureOpenai access from behind company proxies. (#1459) 2024-08-02 00:23:38 +05:30
andrewghlee 563a130141 Feature/bedrock embedder (#1470) 2024-08-01 23:25:28 +05:30
Dev Khant 80945df4ca Match output format with APIs (#1595) 2024-08-01 10:47:39 -07:00
Dev Khant 45ae1f0313 Add ChromaDB support (#1612) 2024-08-01 09:46:35 -07:00
Dev Khant e585d3c1cc Skip few tests in Mem0 (#1625) 2024-08-01 08:31:57 -07:00
Pranav Puranik abd4ec64eb Fixes pytests openai: args change and pathlib reference for pricing file (#1602) 2024-07-31 07:56:23 -07:00
Dev Khant 47afe52296 Add update method in client (#1615) 2024-07-31 11:43:30 +05:30
Dev Khant f2ddc573f6 Redundant code fix (#1611) 2024-07-30 22:42:32 +05:30
Dev Khant 6f42a95aab Fix docs (#1609) 2024-07-30 08:35:51 -07:00
Taranjeet Singh ac6b53ed0a feat: Update docs (#1608) 2024-07-30 10:57:30 +05:30
Taranjeet Singh c39436ae74 feat: update docs (#1607) 2024-07-30 10:28:43 +05:30
Taranjeet Singh 607c689cb0 feat: Improve readme and author details (#1606) 2024-07-30 10:05:47 +05:30
Taranjeet Singh 914feb65a0 Add: Licence (#1605) 2024-07-30 07:43:29 +05:30
Dev Khant ab3c9f889d Add output examples and multion travel agent notebook (#1594) 2024-07-26 23:35:21 +05:30
Taranjeet Singh bb2efb8b8b fix: substack link (#1589) 2024-07-26 12:53:11 +05:30
Taranjeet Singh 9148cce8fc Feat: Add mem0 newsletter link (#1588) 2024-07-26 00:19:42 -07:00
Prateek Chhikara cbb2b2991d Improved readme (#1587) 2024-07-26 00:01:04 -07:00
Dev Khant fd1d5e0e2b Update readme (#1549) 2024-07-23 10:28:25 -07:00
Deshraj Yadav 04b0297ae4 Update docs and README (#1546) 2024-07-23 01:02:55 -07:00
Deshraj Yadav ef706ad976 Update README.md (#1540) 2024-07-22 21:50:35 -07:00
Taranjeet Singh d9f09c1819 Fix: Slack and Discord links (#1537) 2024-07-23 08:56:45 +05:30
Dev Khant 0773b37197 update multion docs (#1518) 2024-07-20 14:17:38 -07:00
Dev Khant c8a5c6f0e9 Update mem0 version in embedchain (#1512) 2024-07-20 10:11:01 -07:00
Deshraj Yadav c7b9498693 [Mem0] Update platform client, improve deduction logic and update client docs (#1510) 2024-07-20 02:44:33 -07:00
Dev Khant c27ab0585c version bump (#1509) 2024-07-19 22:52:49 -07:00
Dev Khant e913c96926 Update docs for LLMs and Overview (#1504) 2024-07-19 22:33:16 -07:00
Dev Khant 0c9c5fe9c2 Poetry and LLM fixes (#1508) 2024-07-19 22:19:15 -07:00
Dev Khant 51fd7db205 Mem0 fix in embedchain (#1506) 2024-07-19 15:03:09 -07:00
Dev Khant e9136c1aa0 Fix CI tests for Mem0 (#1498) 2024-07-18 13:28:19 -07:00
Dev Khant a546a9f56a Update Mem0 LLM docs (#1497) 2024-07-18 13:06:40 -07:00
Dev Khant 40c9abe484 Support model config in LLMs (#1495) 2024-07-18 09:21:40 -07:00
Dev Khant c411dc294e Add Litellm support (#1493) 2024-07-17 23:49:48 -07:00
Deshraj Yadav fb5a3bfd95 Fix CI/CD (#1492) 2024-07-17 23:40:12 -07:00
Dev Khant 7441f1462d Change dependency to mem0ai (#1476) 2024-07-17 22:12:25 -07:00
Deshraj Yadav c9240e7ca6 User/dyadav/add platform docs (#1491) 2024-07-17 17:39:57 -07:00
Dev Khant 1e7618dfa4 Add AWS Bedrock support (#1482) 2024-07-17 14:38:10 -07:00
Deshraj Yadav 4e5d34103f [Docs] Add multion integration (#1489) 2024-07-17 10:53:21 -07:00
Dev Khant da435bc025 add delete and reset in docs (#1488) 2024-07-17 09:47:16 -07:00
Deshraj Yadav 2a43aa6902 [Docs] Add example for building Personal AI Assistant using Mem0 (#1486) 2024-07-16 15:20:03 -07:00
Dev Khant b620f8fae3 Add TogetherAI support (#1485) 2024-07-16 13:19:18 -07:00
Deshraj Yadav 03f787d5cb Update mem0 version to 0.0.4 (#1484) 2024-07-16 11:12:42 -07:00
Dev Khant 19637804b3 Add Groq Support (#1481) 2024-07-16 11:03:28 -07:00
Dev Khant 80f145fceb Add model pricing file (#1483) 2024-07-16 10:55:33 -07:00
Deshraj Yadav 34477d4936 Update README (#1478) 2024-07-15 00:16:56 -07:00
Deshraj Yadav 4ec51f2dd6 [Mem0] Fix issues and update docs (#1477) 2024-07-14 22:21:07 -07:00
Taranjeet Singh f842a92e25 Rename embedchain to mem0 and open sourcing code for long term memory (#1474)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2024-07-12 20:21:33 +05:30
Vatsal Rathod 83e8c97295 Refactoring vectordb naming convention in embedchain.config (#1469) 2024-07-08 16:01:17 -07:00
Dev Khant 1a5d0d236a Remove unwanted libraries and lighten package (#1391) 2024-07-08 16:00:16 -07:00
Dev Khant ebbf90f4aa Version bump -> 0.1.116 (#1464) 2024-07-06 21:23:10 -07:00
Stefan Bokarev 4f119692f1 [Docs]: Add Integration for 🧊 Helicone (LLM-Observability for Developers) (#1458) 2024-07-06 12:27:57 -07:00
Dev Khant bbe56107fb Integrate Mem0 (#1462)
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2024-07-06 12:27:01 -07:00
Parshva Daftari bd654e7aac Fixed Docs for the Token Usage (#1461)
Co-authored-by: parshvadaftari <parshva@192.168.1.2>
2024-07-05 08:54:04 -07:00
Dev Khant 33500a7ce2 Version bump (#1460) 2024-07-04 14:42:59 -07:00
Dev Khant 4880557d51 Show details for query tokens (#1392) 2024-07-04 11:40:56 -07:00
Dev Khant ea09b5f7f0 Version bump (#1457) 2024-07-02 23:07:52 -07:00
Pranav Puranik 5258fd91ea http_client and http_async_client bugfix (#1454) 2024-07-02 16:13:33 -07:00
João Moura b305d674de Updating dependencies (#1453) 2024-07-02 16:12:52 -07:00
Pranav Puranik 7c24601d0f Adding model_kwargs for huggingface embedders. (#1450) 2024-06-29 12:37:31 -07:00
Dev Khant 50c0285cb2 Fix batch_size for vectordb (#1449) 2024-06-28 11:18:22 -07:00
Dev Khant 0a78198bb5 Add batch_size in config for VectorDB (#1448) 2024-06-27 14:45:58 -07:00
Vatsal Rathod edaeb78ccf Refactor openai embedder (#1444) 2024-06-26 10:58:12 -07:00
Dev Khant f80be2d2ea Version bump -> 0.1.113 (#1447) 2024-06-24 11:00:55 -07:00
Halan Marques 8700165b42 Fixed Azure OpenAI Deprecations and Adjusted the Tests (#1437) 2024-06-24 10:55:38 -07:00
Prashant Dixit 18fb92f1f8 Updated LanceDB Doc (#1445) 2024-06-24 10:55:20 -07:00
Nikhil Sharma 14fc6bbadd change: replaced deprecated gpt-4-perview with gpt-4o (#1443) 2024-06-24 10:27:10 -07:00
Dev Khant 5070a1d83e Change HF embedding library (#1440) 2024-06-22 01:38:29 -07:00
Dev Khant 8a9088ea9d Version bump (#1438) 2024-06-21 09:11:24 -07:00
Prashant Dixit 48b24f6f12 Lancedb Integration (#1411) 2024-06-21 08:59:22 -07:00
Dev Khant f6ddd5ffc5 Add HF endpoint in embedder (#1436) 2024-06-21 08:57:21 -07:00
Dev Khant b43a116b3c Add vector dimension to Ollama embedder (#1435) 2024-06-21 08:56:46 -07:00
Dev Khant 50512a5f03 Doc fix for embedders (#1433) 2024-06-19 10:08:31 -07:00
Dev Khant e3e107b31d Raise import error if Ollama and Google not found (#1432) 2024-06-18 21:46:48 -07:00
Dev Khant 21a04541ea poetry fix (#1430) 2024-06-18 10:45:37 -07:00
Dev Khant cdd5d8ac76 Version bump (#1426) 2024-06-18 09:13:52 -07:00
Dev Khant 11094f504e Fix Ollama test (#1428) 2024-06-18 09:10:43 -07:00
mogith-pn 5acaae5f56 Clarifai : Added Clarifai as LLM and embedding model provider. (#1311)
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2024-06-17 08:48:18 -07:00
Pranav Puranik 4547d870af azure openai features and bugs solve - openai_version, docs (#1425) 2024-06-17 08:47:27 -07:00
Aditya Veer Parmar dc0d8e0932 Allow ollama llm to take custom callback for handling streaming (#1376) 2024-06-17 08:44:52 -07:00
patcher9 c558eae9ce [Docs]: Fix the Title and Description for OpenLIT Integration (#1424) 2024-06-14 00:09:31 -07:00
patcher9 abb9af66a6 [Docs]: Add Integration for OpenLIT (OpenTelemetry-native LLM Application O11y) (#1377) 2024-06-13 23:06:04 -07:00
Ananto Joyoadikusumo 4800e0344c Added language detection for non-english youtube videos (#1362) 2024-06-13 23:02:37 -07:00
Dev Khant 439b425c61 Version bump (#1423) 2024-06-13 22:28:35 -07:00
Dev Khant 2855f1635b Add support for loading api_key from config or env variable (#1421) 2024-06-13 11:19:54 -07:00
Dev Khant 08b67b4a78 Support for Audio Files (#1416) 2024-06-12 10:25:58 -07:00
Dev Khant 1bddd46ed2 Verion bump, chromadb_version change and doc update (#1407) 2024-06-12 08:46:00 -07:00
Pranav Puranik 6ecdadfd97 Add model_kwargs to OpenAI call (#1402) 2024-06-11 11:20:04 -07:00
Dimitra Gerontaki 4119040005 Add documentation for text_file data type (#1410) 2024-06-10 21:34:28 -07:00
Taranjeet Singh 873eef6ef8 Remove: EC deployment docs, and js links (#1409) 2024-06-11 02:34:15 +05:30
Taranjeet Singh 445fed4d3f Remove embedchain js (#1408) 2024-06-11 01:54:56 +05:30
Dev Khant 52fd3e0dd4 Update contributing doc (#1404) 2024-06-10 10:14:52 -07:00
Saurabh Misra 8fd0e1f3b0 ⚡️ Speed up read_env_file() in embedchain/utils/cli.py (#1260) 2024-06-09 09:11:15 -07:00
golemus 11fc4a8451 Update llms card to properly use local ollama (#1395) 2024-06-09 09:09:49 -07:00
shuo e22293294e Delete embedchain/embedder/.ollama.py.swp (#1398) 2024-06-09 09:02:38 -07:00
Dev Khant 73e53aaff1 Download Ollama model if not present (#1397) 2024-06-08 23:43:03 -07:00
Deshraj Yadav 6fa946557f Update package version to 0.1.108 (#1396) 2024-06-08 10:34:15 -07:00
Youbin Choi fb0852f585 [Bug Fix] Fix issue of loading other languages in csv file (#1225) 2024-06-08 10:09:29 -07:00
Dev Khant 4070fc1bf0 Fix ollama embeddings for remote machine (#1394) 2024-06-08 10:08:15 -07:00
Dev Khant 00c1fa1ec7 Fix OpenAI Assistant (#1393) 2024-06-08 10:07:52 -07:00
Dev Khant 04e77ef34e version bump (#1389) 2024-06-07 10:30:22 -07:00
Dev Khant 827d63d115 Fix skipped tests (#1385) 2024-06-07 10:26:54 -07:00
Anu e0d0f6e94c Change list[str] -> str for vectordbs (#1388) 2024-06-07 09:15:40 -07:00
Dev Khant fd07513004 Fix online feat and add docs (#1387) 2024-06-06 23:33:16 -07:00
Dev Khant b0e436d9c4 Poetry fixes (#1382) 2024-06-06 10:41:46 -07:00
Dev Khant a4bfd9cfc6 Version bump (#1386) 2024-06-06 10:40:38 -07:00
Dev Khant 8ca01918e5 Ollama embeddings tested and Docs ready (#1384) 2024-06-06 10:29:01 -07:00
Deshraj Yadav a5b2381458 Update version to 0.1.105 (#1383) 2024-06-05 10:53:34 -07:00
Anu 26c771503b Doc string fix for embedchain.py (#1381) 2024-06-05 10:44:09 -07:00
Saurabh Misra 622ed4a7c9 Speed up _auto_encoder() by 15% in embedchain/helpers/json_serializable.py (#1265) 2024-06-05 10:40:46 -07:00
Saurabh Misra 940f0128d5 Speed up docs site loader (#1266) 2024-06-05 10:39:30 -07:00
Saurabh Misra 1354747ca8 ⚡️ Speed up get_word_count() by 6% in embedchain/chunkers/base_chunker.py (#1268) 2024-06-05 10:36:00 -07:00
Deshraj Yadav 9544c69c55 [Improvements] Upgrade langchain-openai package and other improvements (#1372) 2024-05-21 23:42:50 -07:00
LeonieFreisinger 9ba445e623 Fix cohere embedder (#1353) 2024-05-21 22:55:10 -07:00
Abdur Rahman Nawaz ebc5e25f98 Add support for http clients in config (#1355) 2024-05-06 10:32:46 -07:00
Esparon1 78301ee63d Add feature to extract timestamps from youtube videos (#1345) 2024-05-06 10:31:04 -07:00
Niv Hertz 797dea1dca Support supplying custom headers to OpenAI requests (#1356) 2024-05-06 10:26:12 -07:00
Deshraj Yadav a0ff764f0a [Misc] Update package version for chroma and pypdf (#1352) 2024-05-01 22:24:49 -07:00
Colin O'Brien a795798156 Add Ollama as a supported embedding provider (#1344) 2024-05-01 22:08:47 -07:00
Jesús Ferretti 1a66f961f4 Docs: fix typo (#1350) 2024-05-01 22:06:22 -07:00
Deshraj Yadav 6fb2048af0 [Bug fix] Remove duplicate constants (#1342) 2024-04-19 09:50:37 -07:00
Deshraj Yadav ba9f186fc5 [Improvement] Make embedchain home dir configurable (#1341) 2024-04-18 11:20:01 -07:00
Dev Khant 6c32d287b5 Support for Excel files (#1319) 2024-04-15 22:03:43 -07:00
Deshraj Yadav 536f85b78a [Improvements] Improve logging and fix insertion in data_sources table (#1337) 2024-04-11 15:00:04 -07:00
neilbhutada f8619870ad Update llms.mdx (#1336) 2024-04-10 16:03:03 -07:00
Deshraj Yadav d00a2085d5 [Bug Fix] Make claude-3-opus model work (#1331) 2024-03-28 00:56:14 -07:00
Deshraj Yadav 85ec61335a Update package version to 0.1.98 (#1327) 2024-03-20 19:29:51 -07:00
Flyfoxs 9b48a12c27 [Bug fix] Avoid saving the duplicated docs (#1326) 2024-03-20 09:58:11 -07:00
Deshraj Yadav c181ccbe42 Update requirements.txt (#1325) 2024-03-19 21:27:21 -07:00
Deshraj Yadav 8520033d44 [Bug fix] Fix issues related to logging configuration (#1318) 2024-03-14 00:45:37 -07:00
Deshraj Yadav ebdce87fde [Version] Update version to 0.1.96 (#1317) 2024-03-14 00:02:51 -07:00
Abhishek Sharma f2122ed696 [Fix] Added missing provider for 'vllm' (#1316) 2024-03-14 00:01:30 -07:00
Deshraj Yadav 3616eaadb4 [Refactor] Improve logging package wide (#1315) 2024-03-13 17:13:30 -07:00
berwin joule ef69c91b60 [Bug fix]: fix Cannot add documents to chromadb with inconsistent sizes. (#1314) 2024-03-13 11:01:46 -07:00
Dev Khant 117824b32c Add folder and branch to GitHub (#1308) 2024-03-12 12:15:37 -07:00
Dev Khant f77f5b996e Support for Cohere Embeddings (#1310) 2024-03-12 12:14:45 -07:00
Deshraj Yadav a4d32aec24 [Docs] Update docs and readme (#1309) 2024-03-10 00:23:21 -08:00
Uzair Naeem 9111495fae Enhance code readability and documentation clarity (#1307) 2024-03-07 08:12:31 -08:00
Hardik Jindal ee1e3f0957 [docs] update langsmith.mdx (#1306) 2024-03-07 08:10:11 -08:00
Deshraj Yadav 4dc5c7348f [Bug fix]: Fix issue of OPENAI_API_BASE env variable being mandatory (#1305) 2024-03-05 14:07:44 -08:00
Deshraj Yadav 4428768eaa [Bug Fix]: Fix test cases and update version to 0.1.93 (#1303) 2024-03-04 18:35:01 -08:00
Joe 11f4ce8fb6 #1155: Add support for OpenAI-compatible endpoint in LLM and Embed (#1197) 2024-03-04 18:17:20 -08:00
Felipe Amaral 6078738d34 Feature: Custom Ollama endpoint base_url (#1301) 2024-03-04 18:09:45 -08:00
Deshraj Yadav faacfeb891 [Improvement] Set a default app id if not provided in the app configuration (#1300) 2024-03-02 15:10:34 -08:00
Deshraj Yadav 8d7e8b6fb9 [Docs] Update pinecone integration docs (#1296) 2024-03-01 16:49:43 -08:00
Deshraj Yadav 7e1d2ffdd7 [Bug fix] Fix search API for pinecone vector db 2024-03-01 16:44:42 -08:00
Deshraj Yadav 91044ec591 [Bug fix] Fix issue related to get_data_sources() method (#1295) 2024-03-01 11:54:36 -08:00
Deshraj Yadav c77a75dfb5 [Feature] Add support for NVIDIA AI LLMs and embedding models (#1293) 2024-02-29 23:56:25 -08:00
Deshraj Yadav 6518c0c06b [Docs] Update docs for resetting vector database (#1289) 2024-02-28 12:19:47 -08:00
Deshraj Yadav 09cdaff9a2 [Improvement] Fix deprecation warnings (#1288) 2024-02-27 15:10:41 -08:00
Deshraj Yadav 56bf33ab7f [Feature] Add support for running huggingface models locally (#1287) 2024-02-27 15:05:17 -08:00
Deshraj Yadav 752f638cfc [Feature/Improvements] Delete data sources from metadata db when using app.delete() (#1286) 2024-02-26 13:18:42 -08:00
Deshraj Yadav 92dd7edb57 [Feature] Add support for Groq LLMs (#1284) 2024-02-25 11:58:03 -08:00
Deshraj Yadav b4bb4cf053 [Bug fix] Fix issue for using any metadata db apart from sqlite (#1282) 2024-02-22 09:52:40 -08:00
Deshraj Yadav f0400e928a [Bug fix] Fix issue related to initalizing the local database engine (#1281) 2024-02-22 02:40:06 -08:00
Deshraj Yadav aa5ad625af Add support for supplying custom db params (#1276) 2024-02-21 16:15:57 -08:00
Deshraj Yadav f8f69eab03 [Feature] Add support for python 3.13 and other migration related fixes (#1279) 2024-02-21 13:04:03 -08:00
João Moura 2b2263acaa Updating python version to including <=3.13 (#1278) 2024-02-21 11:48:13 -08:00
Deshraj Yadav 5e2e7fb639 [Feature] Add support to use any sql database as the metadata storage for embedchain apps (#1273) 2024-02-19 13:04:18 -08:00
Deshraj Yadav 6c12bc9044 [Improvements] Improve the default prompt and data loader util functions (#1272) 2024-02-18 14:06:32 -08:00
Saurabh Misra 9a11683003 ⚡️ Speed up is_readable by 101% in embedchain/utils/misc.py (#1258)
Co-authored-by: codeflash-ai[bot] <148906541+codeflash-ai[bot]@users.noreply.github.com>
2024-02-15 23:18:53 -08:00
Deshraj Yadav 38b4e06963 [Feature] Add support for hybrid search for pinecone vector database (#1259) 2024-02-15 13:20:14 -08:00
Deshraj Yadav 0766a44ccf [Bug fix] Fix vertex ai integration issue (#1257) 2024-02-14 11:19:32 -08:00
Deshraj Yadav 036bf3a161 Update version to 0.1.78 (#1256) 2024-02-12 17:23:14 -08:00
UnMonsieur 41bd258b93 [Feature] OpenAI Function Calling (#1224) 2024-02-11 17:58:11 -08:00
Deshraj Yadav 38e212c721 [Bug fix] Fix test (#1255) 2024-02-11 17:57:14 -08:00
Deshraj Yadav 2f285ea00a [Bug fix] Fix history sequence in prompt (#1254) 2024-02-11 16:07:36 -08:00
Dhravya Shah d38120c839 [Docs] Added documentation to deploy to Railway.app (#1250) 2024-02-11 15:58:42 -08:00
Michael d94aee812b [Improvements] Fixes to null data results and OpenAI embedding limits (#1238) 2024-02-11 15:45:02 -08:00
Rishiraj2594 68d650ec40 [Docs] Typo fixed youtube-video.mdx (#1253) 2024-02-09 16:34:08 -08:00
Rishiraj2594 769d926f5a [Docs] Typo fixed in youtube-channel.mdx (#1252) 2024-02-09 16:33:48 -08:00
Oskar 9478bab04e Fix links to the Discourse docs in the Discourse Loader (#1251) 2024-02-09 08:21:52 -08:00
Deshraj Yadav 7ad4af250f [Feature] Add support for optionally fetch all chat history for app (#1249) 2024-02-07 14:52:39 -08:00
Deshraj Yadav 9fa368b114 [Refactor] Remove usage of 'Pipeline' in favor of 'App' (#1246) 2024-02-06 19:00:33 -08:00
Deshraj Yadav 4afef04f26 [Feature] Add support for metadata filtering on search API (#1245) 2024-02-06 15:42:51 -08:00
Thomas T 8fe2c3effc [Bug Fix] Add support for AWS_REGION override (#1237) 2024-02-06 11:25:58 -08:00
Deshraj Yadav fa78c972be [Bug Fix] Fix issue related to using embedding model from huggingface (#1242) 2024-02-06 10:54:58 -08:00
Deshraj Yadav 0e66261644 Update docs (#1240) 2024-02-05 18:56:05 -08:00
Juanan Pereira 819650a254 Update URL Validation Regex to Support IP Addresses and Port Numbers (#1233) 2024-02-02 09:06:56 +05:30
Taranjeet Singh 34c41c87dc Docs: Update full stack docs (#1230) 2024-01-30 09:51:32 +05:30
Deshraj Yadav 2985b667b0 [Bug fix] Fix issue with gmail loader (#1228) 2024-01-29 18:36:02 +05:30
Taranjeet Singh 31bb0e7f0f Bump version to 0.1.71 (#1223) 2024-01-27 13:34:34 +05:30
Taranjeet Singh 8f28264aec feat: add UA header for pdf and sitemap (#1222) 2024-01-27 13:29:09 +05:30
Taranjeet Singh ec4fb11aa5 bump version to 0.1.70 (#1221) 2024-01-27 09:31:32 +05:30
Deven Patel b210723de1 [Improvement] add default user-agent header in webpage loader (#1219) 2024-01-26 11:04:25 +05:30
Deven Patel 433f99dd78 [Bugfix] fix typo in opensearch db (#1218) 2024-01-26 10:08:47 +05:30
Deven Patel e75c05112e [Improvement] update pinecone client v3 (#1200)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-26 09:08:37 +05:30
Taranjeet Singh d2a5b50ff8 add support for openai embedding models - text-em-3 (#1216) 2024-01-26 00:46:17 +05:30
Deven Patel 120690afd4 [Docs] Update mistral model in quickstart example (#1215) 2024-01-25 22:29:24 +05:30
Deven Patel 344dbeee42 [Bugfix] openai assistant (#1213)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-25 15:22:18 +05:30
Taranjeet Singh 3fe3b0320a Bump version to 0.1.69 (#1212) 2024-01-25 13:42:12 +05:30
Deven Patel 75896b647f [Docs] add docs for getting the list of added data sources (#1209)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-25 13:33:09 +05:30
Peter Jausovec 446d0975aa enable using custom Pinecone index name (#1172)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-25 13:30:10 +05:30
Deven Patel b7d365119c [Feature] add app.delete() method (#1187)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-23 14:27:30 +05:30
Deven Patel 2d9fbd4e49 [Bugfix] fix qdrant and weaviate db integration (#1181)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-23 14:24:29 +05:30
Deven Patel 22e14b5e65 [Bugfix] update zilliz db (#1186)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-23 14:23:57 +05:30
Deven Patel 1a654beea4 [Bugfix] fix pinecone db (#1185)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-23 14:23:30 +05:30
Deven Patel f50f8a444a [Bugfix] fix opensearch db (#1184)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-23 14:22:58 +05:30
Deven Patel 3cc3a0058d [bugfix] fix elasticsearch db (#1183)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-23 14:22:22 +05:30
Taranjeet Singh ae473b5e3c Bump version to 0.1.68 (#1206) 2024-01-23 14:19:11 +05:30
Deven Patel efb7e31565 [Docs] fix slack join link (#1205) 2024-01-22 20:54:56 -08:00
Deven Patel 069d265338 [Feature] Add support for AWS Bedrock LLM (#1189)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-21 14:09:08 +05:30
Taranjeet Singh 751a3a4bd1 Bump version to 0.1.67 (#1198) 2024-01-20 12:40:43 +05:30
Deven Patel cb0499407e [Feature] Add support for Mistral API (#1194)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-20 12:31:50 +05:30
Deven Patel 9afc6878c8 [Update] add test for passing vector dimension in embedder config (#1196)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-19 23:38:09 +05:30
Deven Patel 0b5b12575a [Bugfix] fix google ai embedding function (#1195)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-19 21:35:44 +05:30
aryankhanna475 d79d30bf0c Update Askabraham showcase (#1190) 2024-01-19 13:24:06 +05:30
Deven Patel 59600e2a5b [Improvement] add vector_dimension configuration in embedder config (#1192)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-19 10:31:41 +05:30
Deven Patel e572b5a3dc [Bugfix] fix import youtube allowed netlocks by defining them locally (#1191)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-19 09:35:46 +05:30
Juanan Pereira 5b46daaee4 Fix #1176 (a bug in the chromadb provider definition example) (#1177) 2024-01-18 02:28:55 +05:30
Deven Patel 2784bae772 [Tests] add tests for evaluation metrics (#1174)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-15 16:05:58 +05:30
Deshraj Yadav 325e11f0de Update docs (#1170) 2024-01-14 12:09:40 +05:30
Deven Patel 7444f59e3c [Bugfix] fix ec dev command for hf spaces (#1168)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-14 08:36:23 +05:30
Deshraj Yadav affe319460 [Refactor] Change evaluation script path (#1165) 2024-01-12 21:29:59 +05:30
Deshraj Yadav 862ff6cca6 [Bug fix] Fix embedding issue for opensearch and some other vector databases (#1163) 2024-01-12 14:15:39 +05:30
Deven Patel c020e65a50 [Improvement] update LLM memory get function (#1162)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-12 12:33:07 +05:30
Deven Patel f582c1fe25 [Bugfix] fix chat history management when app.reset (#1161)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-12 11:10:25 +05:30
Deshraj Yadav 785929c502 [Docs] Update docs for evaluation (#1160) 2024-01-11 22:53:16 +05:30
Deshraj Yadav 68ec6615b1 Update version to 0.1.61 (#1159) 2024-01-11 20:28:20 +05:30
Deven Patel e2cca61cd3 [Feature] Add support for RAG evaluation (#1154)
Co-authored-by: Deven Patel <deven298@yahoo.com>
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2024-01-11 20:02:47 +05:30
Deshraj Yadav 69e83adae0 [Docs] Update docs for dropbox loader (#1158) 2024-01-11 14:28:59 +05:30
Deshraj Yadav 9e24aee40d [Bug fix] Fix issue of loading other languages in config file (#1153) 2024-01-10 13:04:57 +05:30
Christian Clauss 3cff5e9898 Ruff: Add ASYNC checks (#1139) 2024-01-10 09:39:42 +05:30
Deshraj Yadav f3553040bc [Bug fix] Fix chromadb issue on embedchain version 0.1.58 (#1151) 2024-01-09 23:45:42 +05:30
Christian Clauss 2b13984e11 README.md: Expand the acronym to help readers understand (#1148) 2024-01-09 23:08:16 +05:30
Sandra Serrano 0de9491c61 #1128 | Remove deprecated type hints from typing module (#1131) 2024-01-09 23:05:24 +05:30
Deshraj Yadav c9df7a2020 Documentation edits made through Mintlify web editor 2024-01-09 17:47:36 +05:30
Deshraj Yadav a7222e8c50 [Docs] Fix docs heading (#1150) 2024-01-09 17:44:20 +05:30
Deshraj Yadav 0373fa231c [Feature] Add support for vllm as llm source (#1149) 2024-01-09 17:38:53 +05:30
Deshraj Yadav 5f653e69ae Update version to 0.1.57 (#1146) 2024-01-09 02:12:31 +05:30
Sandra Serrano 2496ed133e [Bug fix] Fix typos, static methods and other sanity improvements in the package (#1129) 2024-01-09 00:17:46 +05:30
Madison Ebersole 62c0c52e31 Add support for Hugging Face Inference Endpoint as LLM (#1143) 2024-01-08 23:50:04 +05:30
Deven Patel e36198dcc2 [Docs] add slack ai in docs under examples tab (#1142)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-08 23:03:06 +05:30
Deshraj Yadav 5fa6221f91 Update documentation based on user feedback (#1141) 2024-01-08 11:07:24 +05:30
YusukeJustinNakajima f7696d1dc1 [Fix] Enhanced security for OpenAPI and JSON Loader Integration (#1122) 2024-01-08 11:07:03 +05:30
Christian Clauss 1878f8d4fc README.md: Fix typo (#1140) 2024-01-08 09:49:47 +05:30
Deshraj Yadav 6c69ddef9b Update package version to 0.1.56 (#1137) 2024-01-07 23:50:49 +05:30
Deshraj Yadav 0c45020d81 [Feature] Add support for docker in fullstack app (#1134) 2024-01-07 23:49:20 +05:30
Deven Patel 4dfce44c1a [BugFixes] slack loader (#1135)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-07 23:13:25 +05:30
1952 changed files with 240409 additions and 30740 deletions
+18
View File
@@ -0,0 +1,18 @@
{
"name": "mem0-plugins",
"owner": {
"name": "Mem0",
"email": "support@mem0.ai"
},
"metadata": {
"description": "Official Mem0 plugins for Claude"
},
"plugins": [
{
"name": "mem0",
"source": "./mem0-plugin",
"description": "Mem0 memory layer for AI applications. Add persistent memory, personalization, and semantic search to Claude workflows.",
"version": "0.1.0"
}
]
}
+18
View File
@@ -0,0 +1,18 @@
{
"name": "mem0-plugins",
"owner": {
"name": "Mem0",
"email": "support@mem0.ai"
},
"metadata": {
"description": "Official Mem0 plugins for Cursor"
},
"plugins": [
{
"name": "mem0",
"source": "./mem0-plugin",
"description": "Mem0 memory layer for AI applications. Add persistent memory, personalization, and semantic search.",
"version": "0.1.0"
}
]
}
-1
View File
@@ -1 +0,0 @@
OPENAI_API_KEY="your-openai-api-key"
+46 -32
View File
@@ -1,41 +1,55 @@
name: 🐛 Bug Report
description: Create a report to help us reproduce and fix the bug
name: Bug Report
description: Report a bug in mem0
labels: ["bug"]
body:
- type: markdown
attributes:
value: >
#### Before submitting a bug, please make sure the issue hasn't been already addressed by searching through [the existing and past issues](https://github.com/embedchain/embedchain/issues?q=is%3Aissue+sort%3Acreated-desc+).
- type: textarea
attributes:
label: 🐛 Describe the bug
description: |
Please provide a clear and concise description of what the bug is.
- type: dropdown
id: component
attributes:
label: Component
description: Which part of mem0 is affected?
options:
- Core / Python SDK
- TypeScript SDK
- Vector Store (Qdrant, PGVector, Redis, Chroma, etc.)
- Graph Memory (Neo4j, Memgraph, etc.)
- Ollama / Local Models
- OpenClaw
- REST API
- Other
validations:
required: true
If relevant, add a minimal example so that we can reproduce the error by running the code. It is very important for the snippet to be as succinct (minimal) as possible, so please take time to trim down any irrelevant code to help us debug efficiently. We are going to copy-paste your code and we expect to get the same result as you did: avoid any external data, and include the relevant imports, etc. For example:
- type: textarea
id: description
attributes:
label: Description
value: |
### Summary
```python
# All necessary imports at the beginning
import embedchain as ec
# Your code goes here
A clear summary of the bug.
### Steps to Reproduce
```
```python
from mem0 import Memory
Please also paste or describe the results you observe instead of the expected results. If you observe an error, please paste the error message including the **full** traceback of the exception. It may be relevant to wrap error messages in ```` ```triple quotes blocks``` ````.
placeholder: |
A clear and concise description of what the bug is.
m = Memory()
# Your code here...
```
```python
Sample code to reproduce the problem
```
### Expected Behavior
```
The error message you got, with the full traceback.
````
validations:
required: true
- type: markdown
attributes:
value: >
Thanks for contributing 🎉!
What you expected to happen.
### Actual Behavior
What actually happened. Paste the full error traceback if applicable.
### Environment
- mem0 version:
- Python/Node version:
- OS:
validations:
required: true
+5 -5
View File
@@ -1,8 +1,8 @@
blank_issues_enabled: true
contact_links:
- name: 1-on-1 Session
url: https://cal.com/taranjeetio/ec
about: Speak directly with Taranjeet, the founder, to discuss issues, share feedback, or explore improvements for Embedchain
- name: Discord
- name: Discord Community
url: https://discord.gg/6PzXDgEjG5
about: General community discussions
about: Ask questions and discuss with the community
- name: Documentation
url: https://docs.mem0.ai
about: Read the official mem0 documentation
+21 -9
View File
@@ -1,11 +1,23 @@
name: Documentation
description: Report an issue related to the Embedchain docs.
title: "DOC: <Please write a comprehensive title after the 'DOC: ' prefix>"
name: Documentation Issue
description: Report an issue or suggest an improvement to the mem0 docs
labels: ["documentation"]
body:
- type: textarea
attributes:
label: "Issue with current documentation:"
description: >
Please make sure to leave a reference to the document/code you're
referring to.
- type: textarea
id: description
attributes:
label: Description
value: |
### Page
Link to the docs page: https://docs.mem0.ai/...
### What's Wrong or Missing
Describe what's incorrect, unclear, or missing.
### Suggested Fix
How should the docs be improved?
validations:
required: true
+39 -21
View File
@@ -1,23 +1,41 @@
name: 🚀 Feature request
description: Submit a proposal/request for a new Embedchain feature
name: Feature Request
description: Suggest a new feature or improvement for mem0
labels: ["enhancement"]
body:
- type: textarea
id: feature-request
attributes:
label: 🚀 The feature
description: >
A clear and concise description of the feature proposal
validations:
required: true
- type: textarea
attributes:
label: Motivation, pitch
description: >
Please outline the motivation for the proposal. Is your feature request related to a specific problem? e.g., *"I'm working on X and would like Y to be possible"*. If this is related to another GitHub issue, please link here too.
validations:
required: true
- type: markdown
attributes:
value: >
Thanks for contributing 🎉!
- type: dropdown
id: component
attributes:
label: Component
description: Which part of mem0 does this relate to?
options:
- Core / Python SDK
- TypeScript SDK
- Vector Store (Qdrant, PGVector, Redis, Chroma, etc.)
- Graph Memory (Neo4j, Memgraph, etc.)
- Ollama / Local Models
- OpenClaw
- REST API
- Benchmarks / Evals
- Other
validations:
required: true
- type: textarea
id: description
attributes:
label: Description
value: |
### Use Case
What problem are you trying to solve?
### Proposed Solution
How should this work? Include API examples or pseudocode if helpful.
### Alternatives Considered
Any workarounds you've tried or other approaches considered.
validations:
required: true
+25 -28
View File
@@ -1,41 +1,38 @@
## Linked Issue
Closes #<!-- issue number -->
## Description
Please include a summary of the change and which issue is fixed. Please also include relevant motivation and context. List any dependencies that are required for this change.
<!-- What does this PR do? Why is it needed? -->
Fixes # (issue)
## Type of Change
## Type of change
Please delete options that are not relevant.
- [ ] Bug fix (non-breaking change which fixes an issue)
- [ ] New feature (non-breaking change which adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to not work as expected)
- [ ] Refactor (does not change functionality, e.g. code style improvements, linting)
- [ ] Bug fix (non-breaking change that fixes an issue)
- [ ] New feature (non-breaking change that adds functionality)
- [ ] Breaking change (fix or feature that would cause existing functionality to change)
- [ ] Refactor (no functional changes)
- [ ] Documentation update
## How Has This Been Tested?
## Breaking Changes
Please describe the tests that you ran to verify your changes. Provide instructions so we can reproduce. Please also list any relevant details for your test configuration
<!-- If this is a breaking change, describe what breaks and the migration path. Delete this section if not applicable. -->
Please delete options that are not relevant.
N/A
- [ ] Unit Test
- [ ] Test Script (please provide)
## Test Coverage
## Checklist:
- [ ] I added/updated unit tests
- [ ] I added/updated integration tests
- [ ] I tested manually (describe below)
- [ ] No tests needed (explain why)
- [ ] My code follows the style guidelines of this project
- [ ] I have performed a self-review of my own code
- [ ] I have commented my code, particularly in hard-to-understand areas
- [ ] I have made corresponding changes to the documentation
- [ ] My changes generate no new warnings
- [ ] I have added tests that prove my fix is effective or that my feature works
- [ ] New and existing unit tests pass locally with my changes
- [ ] Any dependent changes have been merged and published in downstream modules
- [ ] I have checked my code and corrected any misspellings
<!-- Describe how you tested this, or link to CI results. -->
## Maintainer Checklist
## Checklist
- [ ] closes #xxxx (Replace xxxx with the GitHub issue number)
- [ ] Made sure Checks passed
- [ ] My code follows the project's style guidelines
- [ ] I have performed a self-review of my code
- [ ] I have added tests that prove my fix/feature works
- [ ] New and existing tests pass locally
- [ ] I have updated documentation if needed
+18
View File
@@ -0,0 +1,18 @@
# Maps dropdown selections to GitHub labels
# Used by the advanced-issue-labeler GitHub Action
component:
- label: "sdk-python"
matcher: "Core / Python SDK"
- label: "sdk-typescript"
matcher: "TypeScript SDK"
- label: "vector-store"
matcher: "Vector Store"
- label: "graph-memory"
matcher: "Graph Memory"
- label: "ollama"
matcher: "Ollama"
- label: "openclaw"
matcher: "OpenClaw"
- label: "rest-api"
matcher: "REST API"
+18 -13
View File
@@ -2,14 +2,14 @@ name: Publish Python 🐍 distributions 📦 to PyPI and TestPyPI
on:
release:
types: [published] # This will trigger the workflow when you create a new release
types: [published]
jobs:
build-n-publish:
name: Build and publish Python 🐍 distributions 📦 to PyPI and TestPyPI
if: startsWith(github.event.release.tag_name, 'v')
runs-on: ubuntu-latest
permissions:
# IMPORTANT: this permission is mandatory for trusted publishing
id-token: write
steps:
- uses: actions/checkout@v2
@@ -19,22 +19,27 @@ jobs:
with:
python-version: '3.11'
- name: Install Poetry
- name: Install Hatch
run: |
curl -sSL https://install.python-poetry.org | python3 -
echo "$HOME/.local/bin" >> $GITHUB_PATH
pip install hatch
- name: Install dependencies
run: poetry install
run: |
hatch env create
- name: Build a binary wheel and a source tarball
run: poetry build
run: |
hatch build --clean
- name: Publish distribution 📦 to Test PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
repository_url: https://test.pypi.org/legacy/
# TODO: Needs to setup mem0 repo on Test PyPI
# - name: Publish distribution 📦 to Test PyPI
# uses: pypa/gh-action-pypi-publish@release/v1
# with:
# repository_url: https://test.pypi.org/legacy/
# packages_dir: dist/
- name: Publish distribution 📦 to PyPI
if: startsWith(github.ref, 'refs/tags')
uses: pypa/gh-action-pypi-publish@release/v1
if: startsWith(github.ref, 'refs/tags/v')
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages_dir: dist/
+83 -20
View File
@@ -4,47 +4,110 @@ on:
push:
branches: [main]
paths:
- 'embedchain/**'
- 'mem0/**'
- 'tests/**'
- 'examples/**'
- 'embedchain/**'
- '.github/workflows/**'
- 'pyproject.toml'
pull_request:
paths:
- 'embedchain/**'
- 'mem0/**'
- 'tests/**'
- 'examples/**'
- 'embedchain/**'
jobs:
build:
check_changes:
runs-on: ubuntu-latest
outputs:
mem0_changed: ${{ steps.filter.outputs.mem0 }}
embedchain_changed: ${{ steps.filter.outputs.embedchain }}
steps:
- uses: actions/checkout@v3
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
mem0:
- 'mem0/**'
- 'tests/**'
- '.github/workflows/**'
- 'pyproject.toml'
embedchain:
- 'embedchain/**'
build_mem0:
needs: check_changes
if: needs.check_changes.outputs.mem0_changed == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11"]
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install poetry
uses: snok/install-poetry@v1
with:
version: 1.4.2
virtualenvs-create: true
virtualenvs-in-project: true
- name: Clean up disk space
run: |
df -h
sudo rm -rf /usr/share/dotnet /usr/local/lib/android /opt/ghc /opt/hostedtoolcache/CodeQL
sudo docker image prune --all --force
sudo docker builder prune -a
df -h
- name: Install Hatch
run: pip install hatch
- name: Load cached venv
id: cached-poetry-dependencies
uses: actions/cache@v2
id: cached-hatch-dependencies
uses: actions/cache@v3
with:
path: .venv
key: venv-${{ runner.os }}-${{ hashFiles('**/poetry.lock') }}
key: venv-mem0-${{ runner.os }}-${{ hashFiles('**/pyproject.toml') }}
- name: Install GEOS Libraries
run: sudo apt-get update && sudo apt-get install -y libgeos-dev
- name: Install dependencies
run: poetry install --all-extras
if: steps.cached-poetry-dependencies.outputs.cache-hit != 'true'
- name: Lint with ruff
run: |
pip install --upgrade pip
pip install -e ".[test,graph,vector_stores,llms,extras]"
pip install ruff
if: steps.cached-hatch-dependencies.outputs.cache-hit != 'true'
- name: Run Linting
run: make lint
- name: Run tests and generate coverage report
run: make coverage
run: make test
build_embedchain:
needs: check_changes
if: needs.check_changes.outputs.embedchain_changed == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.9", "3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v3
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v4
with:
python-version: ${{ matrix.python-version }}
- name: Install Hatch
run: pip install hatch
- name: Load cached venv
id: cached-hatch-dependencies
uses: actions/cache@v3
with:
path: .venv
key: venv-embedchain-${{ runner.os }}-${{ hashFiles('**/pyproject.toml') }}
- name: Install dependencies
run: cd embedchain && make install_all
if: steps.cached-hatch-dependencies.outputs.cache-hit != 'true'
- name: Run Formatting
run: |
mkdir -p embedchain/.ruff_cache && chmod -R 777 embedchain/.ruff_cache
cd embedchain && hatch run format
- name: Lint with ruff
run: cd embedchain && make lint
- name: Run tests and generate coverage report
run: cd embedchain && make coverage
- name: Upload coverage reports to Codecov
uses: codecov/codecov-action@v3
with:
+43
View File
@@ -0,0 +1,43 @@
name: Publish @mem0/cli 📦 to npm
on:
release:
types: [published]
jobs:
build-n-publish:
name: Build and publish @mem0/cli 📦 to npm
if: startsWith(github.event.release.tag_name, 'cli-node-v')
runs-on: ubuntu-latest
permissions:
id-token: write
defaults:
run:
working-directory: cli/node
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
cache-dependency-path: cli/node/pnpm-lock.yaml
- name: Upgrade npm for OIDC trusted publishing
run: npm install -g npm@latest
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm run build
- name: Publish to npm
run: npm publish --provenance --access public
+100
View File
@@ -0,0 +1,100 @@
name: CLI Node CI
on:
workflow_dispatch:
push:
branches: [main]
paths:
- 'cli/node/**'
- '.github/workflows/cli-node-ci.yml'
pull_request:
paths:
- 'cli/node/**'
- '.github/workflows/cli-node-ci.yml'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: cli/node/pnpm-lock.yaml
- name: Install dependencies
working-directory: cli/node
run: pnpm install --frozen-lockfile
- name: Lint
working-directory: cli/node
run: pnpm run lint
- name: Type check
working-directory: cli/node
run: pnpm run typecheck
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
cache-dependency-path: cli/node/pnpm-lock.yaml
- name: Install dependencies
working-directory: cli/node
run: pnpm install --frozen-lockfile
- name: Run tests
working-directory: cli/node
run: pnpm run test
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: cli/node/pnpm-lock.yaml
- name: Install dependencies
working-directory: cli/node
run: pnpm install --frozen-lockfile
- name: Build
working-directory: cli/node
run: pnpm run build
- name: Verify dist output
run: |
test -f cli/node/dist/index.js || (echo "Build output missing: dist/index.js" && exit 1)
+34
View File
@@ -0,0 +1,34 @@
name: Publish mem0-cli 🐍 distributions 📦 to PyPI
on:
release:
types: [published]
jobs:
build-n-publish:
name: Build and publish mem0-cli 📦 to PyPI
if: startsWith(github.event.release.tag_name, 'cli-v')
runs-on: ubuntu-latest
permissions:
id-token: write
defaults:
run:
working-directory: cli/python
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.11'
- name: Install Hatch
run: pip install hatch
- name: Build a binary wheel and a source tarball
run: hatch build --clean
- name: Publish distribution 📦 to PyPI
uses: pypa/gh-action-pypi-publish@release/v1
with:
packages-dir: cli/python/dist/
+79
View File
@@ -0,0 +1,79 @@
name: CLI Python CI
on:
workflow_dispatch:
push:
branches: [main]
paths:
- 'cli/python/**'
- '.github/workflows/cli-python-ci.yml'
pull_request:
paths:
- 'cli/python/**'
- '.github/workflows/cli-python-ci.yml'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install dev dependencies
working-directory: cli/python
run: pip install -e ".[dev]"
- name: Lint with ruff
working-directory: cli/python
run: ruff check .
- name: Check formatting
working-directory: cli/python
run: ruff format --check .
test:
runs-on: ubuntu-latest
strategy:
matrix:
python-version: ["3.10", "3.11", "3.12"]
steps:
- uses: actions/checkout@v4
- name: Set up Python ${{ matrix.python-version }}
uses: actions/setup-python@v5
with:
python-version: ${{ matrix.python-version }}
- name: Install dev dependencies
working-directory: cli/python
run: pip install -e ".[dev]"
- name: Run tests
working-directory: cli/python
run: pytest
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Set up Python
uses: actions/setup-python@v5
with:
python-version: '3.12'
- name: Install Hatch
run: pip install hatch
- name: Build
working-directory: cli/python
run: hatch build --clean
- name: Verify dist output
run: |
ls cli/python/dist/*.whl || (echo "Wheel file missing" && exit 1)
ls cli/python/dist/*.tar.gz || (echo "Source dist missing" && exit 1)
+39
View File
@@ -0,0 +1,39 @@
name: Auto-label issues
on:
issues:
types: [opened]
permissions:
contents: read
issues: write
jobs:
label:
runs-on: ubuntu-latest
steps:
- uses: stefanbuck/github-issue-parser@v3
id: issue-parser
with:
template-path: .github/ISSUE_TEMPLATE/bug_report.yml
- uses: redhat-plumbers-in-action/advanced-issue-labeler@v3
with:
issue-form: ${{ steps.issue-parser.outputs.jsonString }}
section: component
token: ${{ secrets.GITHUB_TOKEN }}
config-path: .github/advanced-issue-labeler.yml
- uses: stefanbuck/github-issue-parser@v3
id: feature-parser
if: contains(github.event.issue.labels.*.name, 'enhancement')
with:
template-path: .github/ISSUE_TEMPLATE/feature_request.yml
- uses: redhat-plumbers-in-action/advanced-issue-labeler@v3
if: contains(github.event.issue.labels.*.name, 'enhancement')
with:
issue-form: ${{ steps.feature-parser.outputs.jsonString }}
section: component
token: ${{ secrets.GITHUB_TOKEN }}
config-path: .github/advanced-issue-labeler.yml
+100
View File
@@ -0,0 +1,100 @@
name: openclaw checks
on:
workflow_dispatch:
push:
branches: [main]
paths:
- 'openclaw/**'
- '.github/workflows/openclaw-checks.yml'
pull_request:
paths:
- 'openclaw/**'
- '.github/workflows/openclaw-checks.yml'
jobs:
lint:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: openclaw/pnpm-lock.yaml
- name: Install dependencies
run: cd openclaw && pnpm install --frozen-lockfile
- name: Type check
run: cd openclaw && pnpm exec tsc --noEmit
test:
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js ${{ matrix.node-version }}
uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
cache-dependency-path: openclaw/pnpm-lock.yaml
- name: Install dependencies
run: cd openclaw && pnpm install --frozen-lockfile
- name: Run tests with coverage
run: cd openclaw && pnpm exec vitest run --coverage
- name: Upload coverage to Codecov
if: matrix.node-version == 20
uses: codecov/codecov-action@v4
with:
flags: openclaw
directory: openclaw/coverage
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
build:
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- name: Setup Node.js
uses: actions/setup-node@v4
with:
node-version: 20
cache: 'pnpm'
cache-dependency-path: openclaw/pnpm-lock.yaml
- name: Install dependencies
run: cd openclaw && pnpm install --frozen-lockfile
- name: Build
run: cd openclaw && pnpm build
- name: Verify dist output exists
run: |
test -f openclaw/dist/index.js || (echo "Build output missing: dist/index.js" && exit 1)
test -f openclaw/dist/index.d.ts || (echo "Build output missing: dist/index.d.ts" && exit 1)
+48
View File
@@ -0,0 +1,48 @@
name: Close stale issues
on:
schedule:
- cron: '0 0 * * *'
workflow_dispatch:
permissions:
issues: write
pull-requests: write
jobs:
stale:
runs-on: ubuntu-latest
steps:
- uses: actions/stale@v9
with:
# Issue settings
days-before-issue-stale: 90
days-before-issue-close: 14
stale-issue-label: 'stale'
stale-issue-message: >
This issue has been automatically marked as stale because it has not
had any activity in 90 days. It will be closed in 14 days if no
further activity occurs. If this is still relevant, please leave a
comment or remove the `stale` label.
close-issue-message: >
This issue has been closed due to inactivity. If this is still
relevant, feel free to reopen it or create a new issue.
# PR settings — mark stale but never auto-close
days-before-pr-stale: 90
days-before-pr-close: -1
stale-pr-label: 'stale'
stale-pr-message: >
This pull request has been automatically marked as stale because it
has not had any activity in 90 days. Please update your branch and
address any review comments, or it may be closed in the future.
# Exempt these labels from stale processing
exempt-issue-labels: 'P0-critical,P1-high,good first issue,security'
exempt-pr-labels: 'P0-critical,P1-high'
# Remove stale label when there is new activity
remove-stale-when-updated: true
# Process up to 100 issues per run to stay within API limits
operations-per-run: 100
+43
View File
@@ -0,0 +1,43 @@
name: Publish mem0ai 📦 to npm
on:
release:
types: [published]
jobs:
build-n-publish:
name: Build and publish mem0ai 📦 to npm
if: startsWith(github.event.release.tag_name, 'ts-v')
runs-on: ubuntu-latest
permissions:
id-token: write
defaults:
run:
working-directory: mem0-ts
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
cache-dependency-path: mem0-ts/pnpm-lock.yaml
- name: Upgrade npm for OIDC trusted publishing
run: npm install -g npm@latest
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm run build
- name: Publish to npm
run: npm publish --provenance --access public
+110
View File
@@ -0,0 +1,110 @@
name: TypeScript SDK CI
on:
push:
branches: [main]
paths:
- 'mem0-ts/**'
- '.github/workflows/ts-sdk-ci.yml'
pull_request:
paths:
- 'mem0-ts/**'
jobs:
check_changes:
runs-on: ubuntu-latest
outputs:
ts_sdk_changed: ${{ steps.filter.outputs.ts_sdk }}
steps:
- uses: actions/checkout@v4
- uses: dorny/paths-filter@v2
id: filter
with:
filters: |
ts_sdk:
- 'mem0-ts/**'
build_ts_sdk:
needs: check_changes
if: needs.check_changes.outputs.ts_sdk_changed == 'true'
runs-on: ubuntu-latest
strategy:
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
cache-dependency-path: mem0-ts/pnpm-lock.yaml
- name: Install dependencies
working-directory: mem0-ts
run: pnpm install --frozen-lockfile
- name: Lint
working-directory: mem0-ts
run: npx prettier --check .
- name: Build
working-directory: mem0-ts
run: pnpm run build
- name: Run unit tests
working-directory: mem0-ts
run: pnpm run test:unit
- name: Verify package exports
working-directory: mem0-ts
run: |
node -e "const m = require('./dist/index.js'); console.log('Client exports:', Object.keys(m).length)"
node -e "const m = require('./dist/oss/index.js'); console.log('OSS exports:', Object.keys(m).length)"
- name: Upload coverage
if: matrix.node-version == 20
uses: actions/upload-artifact@v4
with:
name: coverage-report
path: mem0-ts/coverage/
integration_ts_sdk:
needs: build_ts_sdk
runs-on: ubuntu-latest
strategy:
max-parallel: 1
matrix:
node-version: [20, 22]
steps:
- uses: actions/checkout@v4
- uses: pnpm/action-setup@v4
with:
version: 10
- uses: actions/setup-node@v4
with:
node-version: ${{ matrix.node-version }}
cache: 'pnpm'
cache-dependency-path: mem0-ts/pnpm-lock.yaml
- name: Install dependencies
working-directory: mem0-ts
run: pnpm install --frozen-lockfile
- name: Build
working-directory: mem0-ts
run: pnpm run build
- name: Run integration tests (with cleanup)
working-directory: mem0-ts
env:
MEM0_API_KEY: ${{ secrets.MEM0_API_KEY }}
run: pnpm run test:integration
+43
View File
@@ -0,0 +1,43 @@
name: Publish @mem0/vercel-ai-provider 📦 to npm
on:
release:
types: [published]
jobs:
build-n-publish:
name: Build and publish @mem0/vercel-ai-provider 📦 to npm
if: startsWith(github.event.release.tag_name, 'vercel-ai-v')
runs-on: ubuntu-latest
permissions:
id-token: write
defaults:
run:
working-directory: vercel-ai-sdk
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 10
- name: Set up Node.js
uses: actions/setup-node@v4
with:
node-version: '22'
registry-url: 'https://registry.npmjs.org'
cache: 'pnpm'
cache-dependency-path: vercel-ai-sdk/pnpm-lock.yaml
- name: Upgrade npm for OIDC trusted publishing
run: npm install -g npm@latest
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm run build
- name: Publish to npm
run: npm publish --provenance --access public
+9 -1
View File
@@ -2,6 +2,7 @@
__pycache__/
*.py[cod]
*$py.class
**/node_modules/
# C extensions
*.so
@@ -103,7 +104,7 @@ ipython_config.py
# pdm
# Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
#pdm.lock
# pdm stores project-wide configurations in .pdm.toml, but it is recommended to not include it
# pdm stores project-wide configurations in .pdm.toml, but it is recommended not to include it
# in version control.
# https://pdm.fming.dev/#use-with-ide
.pdm.toml
@@ -165,6 +166,7 @@ cython_debug/
# Database
db
test-db
!embedchain/embedchain/core/db/
.vscode
.idea/
@@ -178,3 +180,9 @@ notebooks/*.yaml
# cache db
*.db
# local directories for testing
eval/
qdrant_storage/
.crossnote
testing.ipynb
+12 -16
View File
@@ -1,20 +1,16 @@
repos:
- repo: https://github.com/psf/black
rev: 23.3.0
hooks:
- id: black
- repo: https://github.com/charliermarsh/ruff-pre-commit
rev: 'v0.0.220'
hooks:
- id: ruff
name: ruff
# Respect `exclude` and `extend-exclude` settings.
args: ["--force-exclude"]
- repo: local
hooks:
- id: pytest-check
name: pytest-check
entry: poetry run pytest
- id: ruff
name: Ruff
entry: ruff check
language: system
pass_filenames: false
always_run: true
types: [python]
args: [--fix]
- id: isort
name: isort
entry: isort
language: system
types: [python]
args: ["--profile", "black"]
+53 -37
View File
@@ -1,4 +1,4 @@
# Contributing to embedchain
# Contributing to mem0
Let us make contribution easy, collaborative and fun.
@@ -10,27 +10,26 @@ To make a contribution, follow these steps:
2. Do the changes on your fork with dedicated feature branch `feature/f1`
3. If you modified the code (new feature or bug-fix), please add tests for it
4. Include proper documentation / docstring and examples to run the feature
5. Check the linting
6. Ensure that all tests pass
7. Submit a pull request
5. Ensure that all tests pass
6. Submit a pull request
For more details about pull requests, please read [GitHub's guides](https://docs.github.com/en/pull-requests/collaborating-with-pull-requests/proposing-changes-to-your-work-with-pull-requests/creating-a-pull-request).
### 📦 Package manager
### 📦 Development Environment
We use `poetry` as our package manager. You can install poetry by following the instructions [here](https://python-poetry.org/docs/#installation).
Please DO NOT use pip or conda to install the dependencies. Instead, use poetry:
We use `hatch` for managing development environments. To set up:
```bash
poetry install --all-extras
or
poetry install --with dev
# Activate environment for specific Python version:
hatch shell dev_py_3_9 # Python 3.9
hatch shell dev_py_3_10 # Python 3.10
hatch shell dev_py_3_11 # Python 3.11
hatch shell dev_py_3_12 # Python 3.12
#activate
poetry shell
# The environment will automatically install all dev dependencies
# Run tests within the activated shell:
make test
```
### 📌 Pre-commit
@@ -41,34 +40,51 @@ To ensure our standards, make sure to install pre-commit before starting to cont
pre-commit install
```
### 🧹 Linting
We use `ruff` to lint our code. You can run the linter by running the following command:
```bash
make lint
```
Make sure that the linter does not report any errors or warnings before submitting a pull request.
### Code Formatting with `black`
We use `black` to reformat the code by running the following command:
```bash
make format
```
### 🧪 Testing
We use `pytest` to test our code. You can run the tests by running the following command:
We use `pytest` to test our code across multiple Python versions. You can run tests using:
```bash
poetry run pytest
# Run tests with default Python version
make test
# Test specific Python versions:
make test-py-3.9 # Python 3.9 environment
make test-py-3.10 # Python 3.10 environment
make test-py-3.11 # Python 3.11 environment
make test-py-3.12 # Python 3.12 environment
# When using hatch shells, run tests with:
make test # After activating a shell with hatch shell test_XX
```
Make sure that all tests pass before submitting a pull request.
Make sure that all tests pass across all supported Python versions before submitting a pull request.
## 🚀 Release Process
We look forward to your pull requests and can't wait to see your contributions!
At the moment, the release process is manual. We try to make frequent releases. Usually, we release a new version when we have a new feature or bugfix. A developer with admin rights to the repository will create a new release on GitHub, and then publish the new version to PyPI.
### 🚀 Releasing
All packages are published automatically via GitHub Actions when a GitHub Release is created with the correct tag prefix.
#### Tag Prefixes
| Package | Registry | Tag Prefix | Example |
|---------|----------|------------|---------|
| `mem0ai` (Python SDK) | PyPI | `v*` | `v0.1.31` |
| `mem0-cli` (Python CLI) | PyPI | `cli-v*` | `cli-v0.2.1` |
| `mem0ai` (TypeScript SDK) | npm | `ts-v*` | `ts-v2.4.6` |
| `@mem0/cli` (Node CLI) | npm | `cli-node-v*` | `cli-node-v0.1.2` |
| `@mem0/vercel-ai-provider` | npm | `vercel-ai-v*` | `vercel-ai-v2.0.6` |
#### How to Release
1. Bump the version in `pyproject.toml` (Python) or `package.json` (Node)
2. Create a [GitHub Release](https://github.com/mem0ai/mem0/releases/new) with the matching tag prefix
3. The correct workflow will trigger automatically — verify in the [Actions tab](https://github.com/mem0ai/mem0/actions)
#### Publishing Details
- **PyPI packages** use OIDC trusted publishing via `pypa/gh-action-pypi-publish`
- **npm packages** use OIDC trusted publishing via npm CLI (>= 11.5.1) — no tokens or secrets required
- All workflows require `permissions: id-token: write` for OIDC authentication
- First publish of a new npm package must be done manually; OIDC works for subsequent versions
+1323
View File
File diff suppressed because it is too large Load Diff
+221
View File
@@ -0,0 +1,221 @@
# Migration Guide: Upgrading to mem0 1.0.0
## TL;DR
**What changed?** We simplified the API by removing confusing version parameters. Now everything returns a consistent format: `{"results": [...]}`.
**What you need to do:**
1. Upgrade: `pip install mem0ai==1.0.0`
2. Remove `version` and `output_format` parameters from your code
3. Update response handling to use `result["results"]` instead of treating responses as lists
**Time needed:** ~5-10 minutes for most projects
---
## Quick Migration Guide
### 1. Install the Update
```bash
pip install mem0ai==1.0.0
```
### 2. Update Your Code
**If you're using the Memory API:**
```python
# Before
memory = Memory(config=MemoryConfig(version="v1.1"))
result = memory.add("I like pizza")
# After
memory = Memory() # That's it - version is automatic now
result = memory.add("I like pizza")
```
**If you're using the Client API:**
```python
# Before
client.add(messages, output_format="v1.1")
client.search(query, version="v2", output_format="v1.1")
# After
client.add(messages) # Just remove those extra parameters
client.search(query)
```
### 3. Update How You Handle Responses
All responses now use the same format: a dictionary with `"results"` key.
```python
# Before - you might have done this
result = memory.add("I like pizza")
for item in result: # Treating it as a list
print(item)
# After - do this instead
result = memory.add("I like pizza")
for item in result["results"]: # Access the results key
print(item)
# Graph relations (if you use them)
if "relations" in result:
for relation in result["relations"]:
print(relation)
```
---
## Enhanced Message Handling
The platform client (MemoryClient) now supports the same flexible message formats as the OSS version:
```python
from mem0 import MemoryClient
client = MemoryClient(api_key="your-key")
# All three formats now work:
# 1. Single string (automatically converted to user message)
client.add("I like pizza", user_id="alice")
# 2. Single message dictionary
client.add({"role": "user", "content": "I like pizza"}, user_id="alice")
# 3. List of messages (conversation)
client.add([
{"role": "user", "content": "I like pizza"},
{"role": "assistant", "content": "I'll remember that!"}
], user_id="alice")
```
### Async Mode Configuration
The `async_mode` parameter now defaults to `True` but can be configured:
```python
# Default behavior (async_mode=True)
client.add(messages, user_id="alice")
# Explicitly set async mode
client.add(messages, user_id="alice", async_mode=True)
# Disable async mode if needed
client.add(messages, user_id="alice", async_mode=False)
```
**Note:** `async_mode=True` provides better performance for most use cases. Only set it to `False` if you have specific synchronous processing requirements.
---
## That's It!
For most users, that's all you need to know. The changes are:
- ✅ No more `version` or `output_format` parameters
- ✅ Consistent `{"results": [...]}` response format
- ✅ Cleaner, simpler API
---
## Common Issues
**Getting `KeyError: 'results'`?**
Your code is still treating the response as a list. Update it:
```python
# Change this:
for memory in response:
# To this:
for memory in response["results"]:
```
**Getting `TypeError: unexpected keyword argument`?**
You're still passing old parameters. Remove them:
```python
# Change this:
client.add(messages, output_format="v1.1")
# To this:
client.add(messages)
```
**Seeing deprecation warnings?**
Remove any explicit `version="v1.0"` from your config:
```python
# Change this:
memory = Memory(config=MemoryConfig(version="v1.0"))
# To this:
memory = Memory()
```
---
## What's New in 1.0.0
- **Better vector stores:** Fixed OpenSearch and improved reliability across all stores
- **Cleaner API:** One way to do things, no more confusing options
- **Enhanced GCP support:** Better Vertex AI configuration options
- **Flexible message input:** Platform client now accepts strings, dicts, and lists (aligned with OSS)
- **Configurable async_mode:** Now defaults to `True` but users can override if needed
---
## Need Help?
- Check [GitHub Issues](https://github.com/mem0ai/mem0/issues)
- Read the [documentation](https://docs.mem0.ai/)
- Open a new issue if you're stuck
---
## Advanced: Configuration Changes
**If you configured vector stores with version:**
```python
# Before
config = MemoryConfig(
version="v1.1",
vector_store=VectorStoreConfig(...)
)
# After
config = MemoryConfig(
vector_store=VectorStoreConfig(...)
)
```
---
## Testing Your Migration
Quick sanity check:
```python
from mem0 import Memory
memory = Memory()
# Add should return a dict with "results"
result = memory.add("I like pizza", user_id="test")
assert "results" in result
# Search should return a dict with "results"
search = memory.search("food", user_id="test")
assert "results" in search
# Get all should return a dict with "results"
all_memories = memory.get_all(user_id="test")
assert "results" in all_memories
print("✅ Migration successful!")
```
+43 -33
View File
@@ -1,45 +1,55 @@
# Variables
PYTHON := python3
PIP := $(PYTHON) -m pip
PROJECT_NAME := embedchain
.PHONY: format sort lint
# Targets
.PHONY: install format lint clean test ci_lint ci_test coverage
# Variables
ISORT_OPTIONS = --profile black
PROJECT_NAME := mem0ai
# Default target
all: format sort lint
install:
poetry install
hatch env create
install_all:
poetry install --all-extras
install_es:
poetry install --extras elasticsearch
install_opensearch:
poetry install --extras opensearch
install_milvus:
poetry install --extras milvus
shell:
poetry shell
py_shell:
poetry run python
pip install ruff==0.6.9 groq together boto3 litellm ollama chromadb weaviate weaviate-client sentence_transformers vertexai \
google-generativeai elasticsearch opensearch-py vecs "pinecone<7.0.0" pinecone-text faiss-cpu langchain-community \
upstash-vector azure-search-documents langchain-memgraph langchain-neo4j langchain-aws rank-bm25 pymochow pymongo psycopg kuzu databricks-sdk valkey
# Format code with ruff
format:
$(PYTHON) -m black .
$(PYTHON) -m isort .
hatch run format
# Sort imports with isort
sort:
hatch run isort mem0/
# Lint code with ruff
lint:
hatch run lint
docs:
cd docs && mintlify dev
build:
hatch build
publish:
hatch publish
clean:
rm -rf dist build *.egg-info
rm -rf dist
lint:
poetry run ruff .
# for example: make test file=tests/test_factory.py
test:
poetry run pytest $(file)
hatch run test
coverage:
poetry run pytest --cov=$(PROJECT_NAME) --cov-report=xml
test-py-3.9:
hatch run dev_py_3_9:test
test-py-3.10:
hatch run dev_py_3_10:test
test-py-3.11:
hatch run dev_py_3_11:test
test-py-3.12:
hatch run dev_py_3_12:test
+136 -85
View File
@@ -1,132 +1,183 @@
<p align="center">
<img src="docs/logo/dark.svg" width="400px" alt="Embedchain Logo">
<a href="https://github.com/mem0ai/mem0">
<img src="docs/images/banner-sm.png" width="800px" alt="Mem0 - The Memory Layer for Personalized AI">
</a>
</p>
<p align="center" style="display: flex; justify-content: center; gap: 20px; align-items: center;">
<a href="https://trendshift.io/repositories/11194" target="blank">
<img src="https://trendshift.io/api/badge/repositories/11194" alt="mem0ai%2Fmem0 | Trendshift" width="250" height="55"/>
</a>
</p>
<p align="center">
<a href="https://runacap.com/ross-index/q3-2023/" target="_blank" rel="noopener"><img style="width: 260px; height: 56px" src="https://runacap.com/wp-content/uploads/2023/10/ROSS_badge_black_Q3_2023.svg" alt="ROSS Index - Fastest Growing Open-Source Startups in Q3 2023 | Runa Capital" width="260" height="56"/></a>
<a href="https://mem0.ai">Learn more</a>
·
<a href="https://mem0.dev/DiG">Join Discord</a>
·
<a href="https://mem0.dev/demo">Demo</a>
</p>
<p align="center">
<a href="https://pypi.org/project/embedchain/">
<img src="https://img.shields.io/pypi/v/embedchain" alt="PyPI">
<a href="https://mem0.dev/DiG">
<img src="https://img.shields.io/badge/Discord-%235865F2.svg?&logo=discord&logoColor=white" alt="Mem0 Discord">
</a>
<a href="https://pepy.tech/project/embedchain">
<img src="https://static.pepy.tech/badge/embedchain" alt="Downloads">
<a href="https://pepy.tech/project/mem0ai">
<img src="https://img.shields.io/pypi/dm/mem0ai" alt="Mem0 PyPI - Downloads">
</a>
<a href="https://embedchain.ai/slack">
<img src="https://img.shields.io/badge/slack-embedchain-brightgreen.svg?logo=slack" alt="Slack">
<a href="https://github.com/mem0ai/mem0">
<img src="https://img.shields.io/github/commit-activity/m/mem0ai/mem0?style=flat-square" alt="GitHub commit activity">
</a>
<a href="https://embedchain.ai/discord">
<img src="https://dcbadge.vercel.app/api/server/6PzXDgEjG5?style=flat" alt="Discord">
<a href="https://pypi.org/project/mem0ai" target="blank">
<img src="https://img.shields.io/pypi/v/mem0ai?color=%2334D058&label=pypi%20package" alt="Package version">
</a>
<a href="https://twitter.com/embedchain">
<img src="https://img.shields.io/twitter/follow/embedchain" alt="Twitter">
<a href="https://www.npmjs.com/package/mem0ai" target="blank">
<img src="https://img.shields.io/npm/v/mem0ai" alt="Npm package">
</a>
<a href="https://colab.research.google.com/drive/138lMWhENGeEu7Q1-6lNbNTHGLZXBBz_B?usp=sharing">
<img src="https://colab.research.google.com/assets/colab-badge.svg" alt="Open in Colab">
</a>
<a href="https://codecov.io/gh/embedchain/embedchain">
<img src="https://codecov.io/gh/embedchain/embedchain/graph/badge.svg?token=EMRRHZXW1Q" alt="codecov">
<a href="https://www.ycombinator.com/companies/mem0">
<img src="https://img.shields.io/badge/Y%20Combinator-S24-orange?style=flat-square" alt="Y Combinator S24">
</a>
</p>
<hr />
<p align="center">
<a href="https://mem0.ai/research"><strong>📄 Building Production-Ready AI Agents with Scalable Long-Term Memory →</strong></a>
</p>
<p align="center">
<strong>⚡ +26% Accuracy vs. OpenAI Memory • 🚀 91% Faster • 💰 90% Fewer Tokens</strong>
</p>
> **🎉 mem0ai v1.0.0 is now available!** This major release includes API modernization, improved vector store support, and enhanced GCP integration. [See migration guide →](MIGRATION_GUIDE_v1.0.md)
> ### Checkout our latest [Sadhguru AI app](https://sadhguru-ai.streamlit.app/) built using Embedchain.
## 🔥 Research Highlights
- **+26% Accuracy** over OpenAI Memory on the LOCOMO benchmark
- **91% Faster Responses** than full-context, ensuring low-latency at scale
- **90% Lower Token Usage** than full-context, cutting costs without compromise
- [Read the full paper](https://mem0.ai/research)
## What is Embedchain?
# Introduction
Embedchain is an Open Source RAG Framework that makes it easy to create and deploy AI apps. At its core, Embedchain follows the design principle of being *"Conventional but Configurable"* to serve both software engineers and machine learning engineers.
[Mem0](https://mem0.ai) ("mem-zero") enhances AI assistants and agents with an intelligent memory layer, enabling personalized AI interactions. It remembers user preferences, adapts to individual needs, and continuously learns over time—ideal for customer support chatbots, AI assistants, and autonomous systems.
Embedchain streamlines the creation of RAG applications, offering a seamless process for managing various types of unstructured data. It efficiently segments data into manageable chunks, generates relevant embeddings, and stores them in a vector database for optimized retrieval. With a suite of diverse APIs, it enables users to extract contextual information, find precise answers, or engage in interactive chat conversations, all tailored to their own data.
### Key Features & Use Cases
## 🔧 Quick install
**Core Capabilities:**
- **Multi-Level Memory**: Seamlessly retains User, Session, and Agent state with adaptive personalization
- **Developer-Friendly**: Intuitive API, cross-platform SDKs, and a fully managed service option
### Python API
**Applications:**
- **AI Assistants**: Consistent, context-rich conversations
- **Customer Support**: Recall past tickets and user history for tailored help
- **Healthcare**: Track patient preferences and history for personalized care
- **Productivity & Gaming**: Adaptive workflows and environments based on user behavior
## 🚀 Quickstart Guide <a name="quickstart"></a>
Choose between our hosted platform or self-hosted package:
### Hosted Platform
Get up and running in minutes with automatic updates, analytics, and enterprise security.
1. Sign up on [Mem0 Platform](https://app.mem0.ai)
2. Embed the memory layer via SDK or API keys
### Self-Hosted (Open Source)
Install the sdk via pip:
```bash
pip install embedchain
pip install mem0ai
```
## ✨ Live demo
Install sdk via npm:
```bash
npm install mem0ai
```
Checkout the [Chat with PDF](https://embedchain.ai/demo/chat-pdf) live demo we created using Embedchain. You can find the source code [here](https://github.com/embedchain/embedchain/tree/main/examples/chat-pdf).
### CLI
## 🔍 Usage
Manage memories from your terminal:
<!-- Demo GIF or Image -->
<p align="center">
<img src="docs/images/cover.gif" width="900px" alt="Embedchain Demo">
</p>
```bash
npm install -g @mem0/cli # or: pip install mem0-cli
For example, you can create an Elon Musk bot using the following code:
mem0 init
mem0 add "Prefers dark mode and vim keybindings" --user-id alice
mem0 search "What does Alice prefer?" --user-id alice
```
See the [CLI documentation](https://docs.mem0.ai/platform/cli) for the full command reference.
### Basic Usage
Mem0 requires an LLM to function, with `gpt-4.1-nano-2025-04-14 from OpenAI as the default. However, it supports a variety of LLMs; for details, refer to our [Supported LLMs documentation](https://docs.mem0.ai/components/llms/overview).
First step is to instantiate the memory:
```python
import os
from embedchain import Pipeline as App
from openai import OpenAI
from mem0 import Memory
# Create a bot instance
os.environ["OPENAI_API_KEY"] = "YOUR API KEY"
elon_bot = App()
openai_client = OpenAI()
memory = Memory()
# Embed online resources
elon_bot.add("https://en.wikipedia.org/wiki/Elon_Musk")
elon_bot.add("https://www.forbes.com/profile/elon-musk")
def chat_with_memories(message: str, user_id: str = "default_user") -> str:
# Retrieve relevant memories
relevant_memories = memory.search(query=message, user_id=user_id, limit=3)
memories_str = "\n".join(f"- {entry['memory']}" for entry in relevant_memories["results"])
# Query the bot
elon_bot.query("How many companies does Elon Musk run and name those?")
# Answer: Elon Musk currently runs several companies. As of my knowledge, he is the CEO and lead designer of SpaceX, the CEO and product architect of Tesla, Inc., the CEO and founder of Neuralink, and the CEO and founder of The Boring Company. However, please note that this information may change over time, so it's always good to verify the latest updates.
# Generate Assistant response
system_prompt = f"You are a helpful AI. Answer the question based on query and memories.\nUser Memories:\n{memories_str}"
messages = [{"role": "system", "content": system_prompt}, {"role": "user", "content": message}]
response = openai_client.chat.completions.create(model="gpt-4.1-nano-2025-04-14", messages=messages)
assistant_response = response.choices[0].message.content
# Create new memories from the conversation
messages.append({"role": "assistant", "content": assistant_response})
memory.add(messages, user_id=user_id)
return assistant_response
def main():
print("Chat with AI (type 'exit' to quit)")
while True:
user_input = input("You: ").strip()
if user_input.lower() == 'exit':
print("Goodbye!")
break
print(f"AI: {chat_with_memories(user_input)}")
if __name__ == "__main__":
main()
```
You can also try it in your browser with Google Colab:
For detailed integration steps, see the [Quickstart](https://docs.mem0.ai/quickstart) and [API Reference](https://docs.mem0.ai/api-reference).
[![Open in Colab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/drive/17ON1LPonnXAtLaZEebnOktstB_1cJJmh?usp=sharing)
## 🔗 Integrations & Demos
## 📖 Documentation
Comprehensive guides and API documentation are available to help you get the most out of Embedchain:
- **ChatGPT with Memory**: Personalized chat powered by Mem0 ([Live Demo](https://mem0.dev/demo))
- **Browser Extension**: Store memories across ChatGPT, Perplexity, and Claude ([Chrome Extension](https://chromewebstore.google.com/detail/onihkkbipkfeijkadecaafbgagkhglop?utm_source=item-share-cb))
- **Langgraph Support**: Build a customer bot with Langgraph + Mem0 ([Guide](https://docs.mem0.ai/integrations/langgraph))
- **CrewAI Integration**: Tailor CrewAI outputs with Mem0 ([Example](https://docs.mem0.ai/integrations/crewai))
- [Introduction](https://docs.embedchain.ai/get-started/introduction#what-is-embedchain)
- [Getting Started](https://docs.embedchain.ai/get-started/quickstart)
- [Examples](https://docs.embedchain.ai/examples)
- [Supported data types](https://docs.embedchain.ai/components/data-sources/overview)
## 📚 Documentation & Support
## 🔗 Join the Community
* Connect with fellow developers by joining our [Slack Community](https://embedchain.ai/slack) or [Discord Community](https://embedchain.ai/discord).
* Dive into [GitHub Discussions](https://github.com/embedchain/embedchain/discussions), ask questions, or share your experiences.
## 🤝 Schedule a 1-on-1 Session
Book a [1-on-1 Session](https://cal.com/taranjeetio/ec) with the founders, to discuss any issues, provide feedback, or explore how we can improve Embedchain for you.
## 🌐 Contributing
Contributions are welcome! Please check out the issues on the repository, and feel free to open a pull request.
For more information, please see the [contributing guidelines](CONTRIBUTING.md).
For more reference, please go through [Development Guide](https://docs.embedchain.ai/contribution/dev) and [Documentation Guide](https://docs.embedchain.ai/contribution/docs).
<a href="https://github.com/embedchain/embedchain/graphs/contributors">
<img src="https://contrib.rocks/image?repo=embedchain/embedchain" />
</a>
## Anonymous Telemetry
We collect anonymous usage metrics to enhance our package's quality and user experience. This includes data like feature usage frequency and system info, but never personal details. The data helps us prioritize improvements and ensure compatibility. If you wish to opt-out, set the environment variable `EC_TELEMETRY=false`. We prioritize data security and don't share this data externally.
- Full docs: https://docs.mem0.ai
- Community: [Discord](https://mem0.dev/DiG) · [X (formerly Twitter)](https://x.com/mem0ai)
- Contact: founders@mem0.ai
## Citation
If you utilize this repository, please consider citing it with:
We now have a paper you can cite:
```
@misc{embedchain,
author = {Taranjeet Singh, Deshraj Yadav},
title = {Embedchain: The Open Source RAG Framework},
year = {2023},
publisher = {GitHub},
journal = {GitHub repository},
howpublished = {\url{https://github.com/embedchain/embedchain}},
```bibtex
@article{mem0,
title={Mem0: Building Production-Ready AI Agents with Scalable Long-Term Memory},
author={Chhikara, Prateek and Khant, Dev and Aryan, Saket and Singh, Taranjeet and Yadav, Deshraj},
journal={arXiv preprint arXiv:2504.19413},
year={2025}
}
```
## ⚖️ License
Apache 2.0 — see the [LICENSE](https://github.com/mem0ai/mem0/blob/main/LICENSE) file for details.
File diff suppressed because it is too large Load Diff
+136
View File
@@ -0,0 +1,136 @@
# mem0 CLI
The official command-line interface for [mem0](https://mem0.ai) — the memory layer for AI agents. Works with the Mem0 Platform API. Available in Python and Node.js.
> **For AI agents:** pass `--agent` (or `--json`) on any command for structured JSON output purpose-built for tool loops — sanitized fields, no colors or spinners, errors as JSON. See [Agent mode](#agent-mode) below.
## Installation
```bash
npm install -g @mem0/cli
```
```bash
pip install mem0-cli
```
Both packages install a `mem0` binary with identical behavior.
## Quick start
```bash
# Interactive setup wizard
mem0 init
# Or login via email (get a new API key)
mem0 init --email alice@company.com
# Or authenticate with an existing API key
mem0 init --api-key m0-xxx
# Add a memory
mem0 add "I prefer dark mode and use vim keybindings" --user-id alice
# Search memories
mem0 search "What are Alice's preferences?" --user-id alice
# List all memories for a user
mem0 list --user-id alice
# Update a memory
mem0 update <memory-id> "I switched to light mode"
# Delete a memory
mem0 delete <memory-id>
```
## Commands
| Command | Description |
|---------|-------------|
| `mem0 init` | Setup wizard — login via email or configure API key manually |
| `mem0 add` | Add a memory from text, JSON messages, a file, or stdin |
| `mem0 search` | Search memories using natural language |
| `mem0 list` | List memories with optional filters and pagination |
| `mem0 get` | Retrieve a specific memory by ID |
| `mem0 update` | Update the text or metadata of a memory |
| `mem0 delete` | Delete a memory, all memories for a scope, or an entity |
| `mem0 import` | Bulk import memories from a JSON file |
| `mem0 config` | View or modify CLI configuration |
| `mem0 entity` | List or delete entities (users, agents, apps, runs) |
| `mem0 event` | Inspect background processing events (bulk deletes, large add jobs) |
| `mem0 status` | Verify API connection and display current project |
| `mem0 version` | Print the CLI version |
Run `mem0 <command> --help` for detailed usage on any command.
## Agent mode
Pass `--agent` (or its alias `--json`) as a **global flag** on any command to get output designed for AI agent tool loops:
```bash
mem0 --agent search "user preferences" --user-id alice
mem0 --agent add "User prefers dark mode" --user-id alice
mem0 --agent list --user-id alice
```
Every command returns the same envelope shape:
```json
{
"status": "success",
"command": "search",
"duration_ms": 134,
"scope": { "user_id": "alice" },
"count": 2,
"data": [
{ "id": "abc-123", "memory": "User prefers dark mode", "score": 0.97, "created_at": "2026-01-15", "categories": ["preferences"] }
]
}
```
What agent mode does differently from `--output json`:
- **Sanitized `data`**: only the fields an agent needs (id, memory, score, etc.) — no internal API noise
- **No human output**: spinners, colors, and banners are suppressed entirely
- **Errors as JSON**: errors go to stdout as `{"status": "error", "command": "...", "error": "..."}` with a non-zero exit code
Use `mem0 help --json` to get the full command tree as JSON — useful for agents that need to self-discover available commands.
## Output formats
Control how results are displayed with `--output`:
| Format | Description |
|--------|-------------|
| `text` | Human-readable with colors and formatting (default) |
| `json` | Structured JSON for piping to `jq` (raw API response) |
| `table` | Tabular format (default for `list`) |
| `quiet` | Minimal — just IDs or status codes |
| `agent` | Structured JSON envelope with sanitized fields (set by `--agent`/`--json`) |
## Environment variables
| Variable | Description |
|----------|-------------|
| `MEM0_API_KEY` | API key (overrides config file) |
| `MEM0_BASE_URL` | API base URL |
| `MEM0_USER_ID` | Default user ID |
| `MEM0_AGENT_ID` | Default agent ID |
| `MEM0_APP_ID` | Default app ID |
| `MEM0_RUN_ID` | Default run ID |
| `MEM0_ENABLE_GRAPH` | Enable graph memory (`true` / `false`) |
## Implementations
| Language | Directory | Package | Docs |
|----------|-----------|---------|------|
| TypeScript | [`node/`](./node/) | `@mem0/cli` | [README](./node/README.md) |
| Python | [`python/`](./python/) | `mem0-cli` | [README](./python/README.md) |
## Documentation
Full documentation is available at [docs.mem0.ai/platform/cli](https://docs.mem0.ai/platform/cli).
## License
Apache-2.0
+549
View File
@@ -0,0 +1,549 @@
{
"specVersion": 1,
"cli": {
"name": "mem0",
"version": "0.1.0",
"description": "The Memory Layer for AI Agents",
"helpText": "\u25c6 mem0 CLI \u2014 The Memory Layer for AI Agents"
},
"branding": {
"logoMini": "\u25c6 mem0",
"tagline": "The Memory Layer for AI Agents",
"colors": {
"brand": "#F1C96C",
"accent": "#F5D78E",
"success": "#22c55e",
"error": "#ef4444",
"warning": "#f59e0b",
"dim": "#6b7280"
},
"icons": {
"success": "\u2713",
"error": "\u2717",
"warning": "\u26a0",
"info": "\u25c6",
"pending": "\u29d7",
"add": "+",
"update": "~",
"delete": "-",
"noop": "\u00b7",
"connected": "\u25cf",
"disconnected": "\u25cf"
},
"logo": "███\u2557 ███\u2557███████\u2557███\u2557 ███\u2557 ██████\u2557 ██████\u2557██\u2557 ██\u2557\n████\u2557 ████\u2551██\u2554\u2550\u2550\u2550\u2550\u255d████\u2557 ████\u2551██\u2554\u2550████\u2557 ██\u2554\u2550\u2550\u2550\u2550\u255d██\u2551 ██\u2551\n██\u2554████\u2554██\u2551█████\u2557 ██\u2554████\u2554██\u2551██\u2551██\u2554██\u2551 ██\u2551 ██\u2551 ██\u2551\n██\u2551\u255a██\u2554\u255d██\u2551██\u2554\u2550\u2550\u255d ██\u2551\u255a██\u2554\u255d██\u2551████\u2554\u255d██\u2551 ██\u2551 ██\u2551 ██\u2551\n██\u2551 \u255a\u2550\u255d ██\u2551███████\u2557██\u2551 \u255a\u2550\u255d ██\u2551\u255a██████\u2554\u255d \u255a██████\u2557███████\u2557██\u2551\n\u255a\u2550\u255d \u255a\u2550\u255d\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u255d\u255a\u2550\u255d \u255a\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u255d \u255a\u2550\u2550\u2550\u2550\u2550\u255d\u255a\u2550\u2550\u2550\u2550\u2550\u2550\u255d\u255a\u2550\u255d"
},
"config": {
"configDir": "~/.mem0",
"configFile": "config.json",
"version": 1,
"defaultBaseUrl": "https://api.mem0.ai",
"sections": {
"platform": {
"fields": {
"api_key": {
"type": "string",
"default": "",
"envVar": "MEM0_API_KEY",
"redact": true
},
"base_url": {
"type": "string",
"default": "https://api.mem0.ai",
"envVar": "MEM0_BASE_URL"
}
}
},
"defaults": {
"fields": {
"user_id": {
"type": "string",
"default": "",
"envVar": "MEM0_USER_ID"
},
"agent_id": {
"type": "string",
"default": "",
"envVar": "MEM0_AGENT_ID"
},
"app_id": {
"type": "string",
"default": "",
"envVar": "MEM0_APP_ID"
},
"run_id": {
"type": "string",
"default": "",
"envVar": "MEM0_RUN_ID"
},
"enable_graph": {
"type": "boolean",
"default": false,
"envVar": "MEM0_ENABLE_GRAPH"
}
}
}
}
},
"api": {
"endpoints": {
"add": { "method": "POST", "path": "/v1/memories/" },
"search": { "method": "POST", "path": "/v2/memories/search/" },
"get": { "method": "GET", "path": "/v1/memories/{memory_id}/" },
"list": { "method": "POST", "path": "/v2/memories/" },
"update": { "method": "PUT", "path": "/v1/memories/{memory_id}/" },
"delete": { "method": "DELETE", "path": "/v1/memories/{memory_id}/" },
"deleteAll": { "method": "DELETE", "path": "/v1/memories/" },
"entities": { "method": "GET", "path": "/v1/entities/" },
"deleteEntities": { "method": "DELETE", "path": "/v1/entities/" }
},
"authHeader": "Authorization",
"authScheme": "Token",
"timeout": 30,
"entityTypeMap": {
"users": "user",
"agents": "agent",
"apps": "app",
"runs": "run"
}
},
"errors": {
"AuthError": {
"httpStatus": 401,
"message": "Authentication failed. Your API key may be invalid or expired."
},
"NotFoundError": {
"httpStatus": 404,
"messageTemplate": "Resource not found: {path}"
},
"APIError": {
"httpStatus": 400,
"messageTemplate": "Bad request to {path}: {detail}"
},
"noApiKey": {
"message": "No API key configured.",
"hint": "Run 'mem0 init' or set MEM0_API_KEY environment variable."
}
},
"optionGroups": {
"scope": {
"label": "Scope",
"options": ["user_id", "agent_id", "app_id", "run_id"]
},
"search": {
"label": "Search",
"options": ["top_k", "threshold", "rerank", "keyword", "filter_json", "fields", "graph", "no_graph"]
},
"pagination": {
"label": "Pagination",
"options": ["page", "page_size"]
},
"filters": {
"label": "Filters",
"options": ["category", "after", "before", "graph", "no_graph"]
},
"output": {
"label": "Output",
"options": ["output"]
},
"connection": {
"label": "Connection",
"options": ["api_key", "base_url"]
}
},
"globalOptions": [
{
"name": "api_key",
"flags": ["--api-key"],
"type": "string",
"required": false,
"envVar": "MEM0_API_KEY",
"help": "Override API key.",
"panel": "Connection"
},
{
"name": "base_url",
"flags": ["--base-url"],
"type": "string",
"required": false,
"help": "Override API base URL.",
"panel": "Connection"
},
{
"name": "version",
"flags": ["--version"],
"type": "boolean",
"required": false,
"help": "Show version and exit."
}
],
"commands": [
{
"name": "add",
"description": "Add a memory from text, messages, file, or stdin.",
"usage": "mem0 add <text> [OPTIONS]",
"needsBackend": true,
"needsConfig": true,
"resolveIds": true,
"resolveGraph": true,
"confirmDangerous": false,
"outputFormats": ["text", "json", "quiet"],
"defaultOutput": "text",
"arguments": [
{
"name": "text",
"type": "string",
"required": false,
"help": "Text content to add as a memory."
}
],
"options": [
{ "name": "user_id", "flags": ["--user-id", "-u"], "type": "string", "help": "Scope to user.", "panel": "Scope" },
{ "name": "agent_id", "flags": ["--agent-id"], "type": "string", "help": "Scope to agent.", "panel": "Scope" },
{ "name": "app_id", "flags": ["--app-id"], "type": "string", "help": "Scope to app.", "panel": "Scope" },
{ "name": "run_id", "flags": ["--run-id"], "type": "string", "help": "Scope to run.", "panel": "Scope" },
{ "name": "messages", "flags": ["--messages"], "type": "string", "help": "Conversation messages as JSON." },
{ "name": "file", "flags": ["--file", "-f"], "type": "path", "help": "Read messages from JSON file." },
{ "name": "metadata", "flags": ["--metadata", "-m"], "type": "string", "help": "Custom metadata as JSON." },
{ "name": "immutable", "flags": ["--immutable"], "type": "boolean", "default": false, "help": "Prevent future updates." },
{ "name": "no_infer", "flags": ["--no-infer"], "type": "boolean", "default": false, "help": "Skip inference, store raw." },
{ "name": "expires", "flags": ["--expires"], "type": "string", "help": "Expiration date (YYYY-MM-DD)." },
{ "name": "categories", "flags": ["--categories"], "type": "string", "help": "Categories (JSON array or comma-separated)." },
{ "name": "graph", "flags": ["--graph"], "type": "boolean", "default": false, "help": "Enable graph memory extraction.", "panel": "Scope" },
{ "name": "no_graph", "flags": ["--no-graph"], "type": "boolean", "default": false, "help": "Disable graph memory extraction.", "panel": "Scope" },
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "text", "help": "Output format: text, json, quiet.", "panel": "Output" }
],
"apiEndpoint": "add"
},
{
"name": "search",
"description": "Search memories by semantic query.",
"usage": "mem0 search <query> [OPTIONS]",
"needsBackend": true,
"needsConfig": true,
"resolveIds": true,
"resolveGraph": true,
"confirmDangerous": false,
"outputFormats": ["text", "json", "table"],
"defaultOutput": "text",
"arguments": [
{
"name": "query",
"type": "string",
"required": true,
"help": "Search query."
}
],
"options": [
{ "name": "user_id", "flags": ["--user-id", "-u"], "type": "string", "help": "Filter by user.", "panel": "Scope" },
{ "name": "agent_id", "flags": ["--agent-id"], "type": "string", "help": "Filter by agent.", "panel": "Scope" },
{ "name": "app_id", "flags": ["--app-id"], "type": "string", "help": "Filter by app.", "panel": "Scope" },
{ "name": "run_id", "flags": ["--run-id"], "type": "string", "help": "Filter by run.", "panel": "Scope" },
{ "name": "top_k", "flags": ["--top-k", "-k", "--limit"], "type": "integer", "default": 10, "help": "Number of results.", "panel": "Search" },
{ "name": "threshold", "flags": ["--threshold"], "type": "float", "default": 0.3, "help": "Minimum similarity score.", "panel": "Search" },
{ "name": "rerank", "flags": ["--rerank"], "type": "boolean", "default": false, "help": "Enable reranking (Platform only).", "panel": "Search" },
{ "name": "keyword", "flags": ["--keyword"], "type": "boolean", "default": false, "help": "Use keyword search.", "panel": "Search" },
{ "name": "filter_json", "flags": ["--filter"], "type": "string", "help": "Advanced filter expression (JSON).", "panel": "Search" },
{ "name": "fields", "flags": ["--fields"], "type": "string", "help": "Specific fields to return (comma-separated).", "panel": "Search" },
{ "name": "graph", "flags": ["--graph"], "type": "boolean", "default": false, "help": "Enable graph in search.", "panel": "Search" },
{ "name": "no_graph", "flags": ["--no-graph"], "type": "boolean", "default": false, "help": "Disable graph in search.", "panel": "Search" },
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "text", "help": "Output: text, json, table.", "panel": "Output" }
],
"apiEndpoint": "search"
},
{
"name": "get",
"description": "Get a specific memory by ID.",
"usage": "mem0 get <memory_id> [OPTIONS]",
"needsBackend": true,
"needsConfig": false,
"resolveIds": false,
"resolveGraph": false,
"confirmDangerous": false,
"outputFormats": ["text", "json"],
"defaultOutput": "text",
"arguments": [
{
"name": "memory_id",
"type": "string",
"required": true,
"help": "Memory ID to retrieve."
}
],
"options": [
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "text", "help": "Output: text, json.", "panel": "Output" }
],
"apiEndpoint": "get"
},
{
"name": "list",
"description": "List memories with optional filters.",
"usage": "mem0 list [OPTIONS]",
"needsBackend": true,
"needsConfig": true,
"resolveIds": true,
"resolveGraph": true,
"confirmDangerous": false,
"outputFormats": ["text", "json", "table"],
"defaultOutput": "table",
"arguments": [],
"options": [
{ "name": "user_id", "flags": ["--user-id", "-u"], "type": "string", "help": "Filter by user.", "panel": "Scope" },
{ "name": "agent_id", "flags": ["--agent-id"], "type": "string", "help": "Filter by agent.", "panel": "Scope" },
{ "name": "app_id", "flags": ["--app-id"], "type": "string", "help": "Filter by app.", "panel": "Scope" },
{ "name": "run_id", "flags": ["--run-id"], "type": "string", "help": "Filter by run.", "panel": "Scope" },
{ "name": "page", "flags": ["--page"], "type": "integer", "default": 1, "help": "Page number.", "panel": "Pagination" },
{ "name": "page_size", "flags": ["--page-size"], "type": "integer", "default": 100, "help": "Results per page.", "panel": "Pagination" },
{ "name": "category", "flags": ["--category"], "type": "string", "help": "Filter by category.", "panel": "Filters" },
{ "name": "after", "flags": ["--after"], "type": "string", "help": "Created after (YYYY-MM-DD).", "panel": "Filters" },
{ "name": "before", "flags": ["--before"], "type": "string", "help": "Created before (YYYY-MM-DD).", "panel": "Filters" },
{ "name": "graph", "flags": ["--graph"], "type": "boolean", "default": false, "help": "Enable graph in listing.", "panel": "Filters" },
{ "name": "no_graph", "flags": ["--no-graph"], "type": "boolean", "default": false, "help": "Disable graph in listing.", "panel": "Filters" },
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "table", "help": "Output: text, json, table.", "panel": "Output" }
],
"apiEndpoint": "list"
},
{
"name": "update",
"description": "Update a memory's text or metadata.",
"usage": "mem0 update <memory_id> [text] [OPTIONS]",
"needsBackend": true,
"needsConfig": false,
"resolveIds": false,
"resolveGraph": false,
"confirmDangerous": false,
"outputFormats": ["text", "json", "quiet"],
"defaultOutput": "text",
"arguments": [
{
"name": "memory_id",
"type": "string",
"required": true,
"help": "Memory ID to update."
},
{
"name": "text",
"type": "string",
"required": false,
"help": "New memory text."
}
],
"options": [
{ "name": "metadata", "flags": ["--metadata", "-m"], "type": "string", "help": "Update metadata (JSON)." },
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "text", "help": "Output: text, json, quiet.", "panel": "Output" }
],
"apiEndpoint": "update"
},
{
"name": "delete",
"description": "Delete a memory, all memories matching a scope, or an entity.",
"usage": "mem0 delete [memory_id] [OPTIONS]",
"needsBackend": true,
"needsConfig": true,
"resolveIds": true,
"resolveGraph": false,
"confirmDangerous": true,
"outputFormats": ["text", "json", "quiet"],
"defaultOutput": "text",
"arguments": [
{
"name": "memory_id",
"type": "string",
"required": false,
"help": "Memory ID to delete (omit when using --all or --entity)."
}
],
"options": [
{ "name": "all", "flags": ["--all"], "type": "boolean", "default": false, "help": "Delete all memories matching scope filters." },
{ "name": "entity", "flags": ["--entity"], "type": "boolean", "default": false, "help": "Delete the entity itself and all its memories (cascade)." },
{ "name": "project", "flags": ["--project"], "type": "boolean", "default": false, "help": "With --all: delete ALL memories project-wide." },
{ "name": "dry_run", "flags": ["--dry-run"], "type": "boolean", "default": false, "help": "Show what would be deleted without deleting." },
{ "name": "force", "flags": ["--force"], "type": "boolean", "default": false, "help": "Skip confirmation." },
{ "name": "user_id", "flags": ["--user-id", "-u"], "type": "string", "help": "Scope to user.", "panel": "Scope" },
{ "name": "agent_id", "flags": ["--agent-id"], "type": "string", "help": "Scope to agent.", "panel": "Scope" },
{ "name": "app_id", "flags": ["--app-id"], "type": "string", "help": "Scope to app.", "panel": "Scope" },
{ "name": "run_id", "flags": ["--run-id"], "type": "string", "help": "Scope to run.", "panel": "Scope" },
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "text", "help": "Output: text, json, quiet.", "panel": "Output" }
],
"apiEndpoint": "delete",
"notes": "Mutually exclusive modes: (1) mem0 delete <id> -- single memory, (2) mem0 delete --all [scope] -- bulk delete, (3) mem0 delete --entity [scope] -- entity cascade delete. Cannot combine <memoryId> with --all or --entity, and cannot combine --all with --entity."
},
{
"name": "import",
"description": "Import memories from a JSON file.",
"usage": "mem0 import <file_path> [OPTIONS]",
"needsBackend": true,
"needsConfig": true,
"resolveIds": true,
"resolveGraph": false,
"confirmDangerous": false,
"outputFormats": ["text"],
"defaultOutput": "text",
"arguments": [
{
"name": "file_path",
"type": "string",
"required": true,
"help": "JSON file to import."
}
],
"options": [
{ "name": "user_id", "flags": ["--user-id", "-u"], "type": "string", "help": "Override user ID.", "panel": "Scope" },
{ "name": "agent_id", "flags": ["--agent-id"], "type": "string", "help": "Override agent ID.", "panel": "Scope" }
],
"apiEndpoint": "add"
},
{
"name": "config",
"description": "Manage mem0 configuration.",
"isGroup": true,
"subcommands": [
{
"name": "show",
"description": "Display current configuration (secrets redacted).",
"usage": "mem0 config show",
"needsBackend": false,
"needsConfig": false,
"arguments": [],
"options": []
},
{
"name": "get",
"description": "Get a configuration value.",
"usage": "mem0 config get <key>",
"needsBackend": false,
"needsConfig": false,
"arguments": [
{
"name": "key",
"type": "string",
"required": true,
"help": "Config key (e.g. platform.api_key)."
}
],
"options": []
},
{
"name": "set",
"description": "Set a configuration value.",
"usage": "mem0 config set <key> <value>",
"needsBackend": false,
"needsConfig": false,
"arguments": [
{
"name": "key",
"type": "string",
"required": true,
"help": "Config key (e.g. platform.api_key)."
},
{
"name": "value",
"type": "string",
"required": true,
"help": "Value to set."
}
],
"options": []
}
]
},
{
"name": "entity",
"description": "Manage entities.",
"isGroup": true,
"subcommands": [
{
"name": "list",
"description": "List all entities of a given type.",
"usage": "mem0 entity list <entity_type>",
"needsBackend": true,
"needsConfig": false,
"resolveIds": false,
"resolveGraph": false,
"confirmDangerous": false,
"outputFormats": ["table", "json"],
"defaultOutput": "table",
"arguments": [
{
"name": "entity_type",
"type": "string",
"required": true,
"help": "Entity type: users, agents, apps, runs.",
"choices": ["users", "agents", "apps", "runs"]
}
],
"options": [
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "table", "help": "Output: table, json.", "panel": "Output" }
],
"apiEndpoint": "entities"
},
{
"name": "delete",
"description": "Delete an entity and ALL its memories (cascade).",
"usage": "mem0 entity delete [OPTIONS]",
"needsBackend": true,
"needsConfig": false,
"resolveIds": false,
"resolveGraph": false,
"confirmDangerous": true,
"outputFormats": ["text", "json", "quiet"],
"defaultOutput": "text",
"arguments": [],
"options": [
{ "name": "user_id", "flags": ["--user-id", "-u"], "type": "string", "help": "User ID.", "panel": "Scope" },
{ "name": "agent_id", "flags": ["--agent-id"], "type": "string", "help": "Agent ID.", "panel": "Scope" },
{ "name": "app_id", "flags": ["--app-id"], "type": "string", "help": "App ID.", "panel": "Scope" },
{ "name": "run_id", "flags": ["--run-id"], "type": "string", "help": "Run ID.", "panel": "Scope" },
{ "name": "dry_run", "flags": ["--dry-run"], "type": "boolean", "default": false, "help": "Show what would be deleted without deleting." },
{ "name": "force", "flags": ["--force"], "type": "boolean", "default": false, "help": "Skip confirmation." },
{ "name": "output", "flags": ["--output", "-o"], "type": "string", "default": "text", "help": "Output: text, json, quiet.", "panel": "Output" }
],
"apiEndpoint": "deleteEntities"
}
]
},
{
"name": "init",
"description": "Setup wizard for mem0 CLI. Supports email login (--email) or manual API key (--api-key).",
"usage": "mem0 init [OPTIONS]",
"needsBackend": false,
"needsConfig": false,
"resolveIds": false,
"resolveGraph": false,
"confirmDangerous": false,
"arguments": [],
"options": [
{ "name": "api-key", "flags": ["--api-key"], "type": "string", "default": null, "help": "API key (skip prompt)." },
{ "name": "user-id", "flags": ["-u", "--user-id"], "type": "string", "default": null, "help": "Default user ID (skip prompt)." },
{ "name": "email", "flags": ["--email"], "type": "string", "default": null, "help": "Login via email verification code." },
{ "name": "code", "flags": ["--code"], "type": "string", "default": null, "help": "Verification code (use with --email for non-interactive login)." },
{ "name": "force", "flags": ["--force"], "type": "boolean", "default": false, "help": "Overwrite existing config without confirmation." }
]
},
{
"name": "status",
"description": "Check connectivity and authentication.",
"usage": "mem0 status [OPTIONS]",
"needsBackend": true,
"needsConfig": true,
"resolveIds": false,
"resolveGraph": false,
"confirmDangerous": false,
"arguments": [],
"options": []
},
{
"name": "help",
"description": "Show help. Use --json for machine-readable output (for LLM agents).",
"usage": "mem0 help [OPTIONS]",
"needsBackend": false,
"needsConfig": false,
"resolveIds": false,
"resolveGraph": false,
"confirmDangerous": false,
"arguments": [],
"options": [
{ "name": "json", "flags": ["--json"], "type": "boolean", "default": false, "help": "Output machine-readable JSON for LLM agents." }
]
}
]
}
+75
View File
@@ -0,0 +1,75 @@
# mem0 CLI (Node.js)
The official command-line interface for [mem0](https://mem0.ai) — the memory layer for AI agents. TypeScript implementation.
## Prerequisites
- Node.js **18+**
- pnpm (`npm install -g pnpm`)
## Installation
```bash
npm install -g @mem0/cli
```
Or from source:
```bash
cd node
pnpm install
pnpm build
pnpm link --global
# Now use it like a normal CLI
mem0 --help
```
## Running during development
```bash
cd node
pnpm install
# Development mode (runs TypeScript directly, no build needed)
pnpm dev --help
pnpm dev add "test memory" --user-id alice
pnpm dev search "test" --user-id alice
# Or build first, then run the compiled JS
pnpm build
node dist/index.js --help
node dist/index.js add "test memory" --user-id alice
```
## Quick Start
```bash
# Set up your configuration
mem0 init
# Add a memory
mem0 add "I prefer dark mode and use vim keybindings" --user-id alice
# Search memories
mem0 search "What are Alice's preferences?" --user-id alice
# List all memories
mem0 list --user-id alice
```
## Environment Variables
| Variable | Description |
|----------|-------------|
| `MEM0_API_KEY` | API key (overrides config file) |
| `MEM0_BASE_URL` | API base URL |
| `MEM0_USER_ID` | Default user ID |
| `MEM0_AGENT_ID` | Default agent ID |
| `MEM0_APP_ID` | Default app ID |
| `MEM0_RUN_ID` | Default run ID |
| `MEM0_ENABLE_GRAPH` | Enable graph memory (true/false) |
## License
Apache-2.0
+91
View File
@@ -0,0 +1,91 @@
# Development
## Prerequisites
- Node.js **18+**
- pnpm (`npm install -g pnpm`)
## Setup
From the `node/` directory:
```bash
pnpm install
```
## Running the CLI
There are two ways to run the CLI during development:
### Option 1: Development mode (no build needed)
Uses `tsx` to run TypeScript directly. Pass CLI arguments after `pnpm dev`:
```bash
pnpm dev --help
pnpm dev version
pnpm dev add "test memory" --user-id alice
pnpm dev search "test" --user-id alice
pnpm dev config show
```
> **Note:** Do NOT use `pnpm dev -- --help`. With pnpm, arguments pass through directly — adding `--` inserts a literal `--` that breaks the CLI parser.
### Option 2: Build and run compiled JS
```bash
# Build first
pnpm build
# Run the compiled CLI
node dist/index.js --help
node dist/index.js version
node dist/index.js add "test memory" --user-id alice
```
### Option 3: Link globally (makes `mem0` available system-wide)
```bash
pnpm build
pnpm link --global
# Now use it like a normal CLI
mem0 --help
mem0 version
```
> **Warning:** If you also have the Python CLI installed, both register the `mem0` command. The last one linked/installed wins. Unlink with `pnpm unlink --global`.
## Build
```bash
pnpm build
```
The compiled output is in `dist/`.
## Run tests
```bash
# Run all tests
pnpm test
# Watch mode
pnpm test:watch
```
## Lint
```bash
# Check
pnpm lint
# Auto-fix
pnpm lint:fix
```
## Type checking
```bash
pnpm typecheck
```
+47
View File
@@ -0,0 +1,47 @@
{
"name": "@mem0/cli",
"version": "0.1.2",
"description": "The official CLI for mem0 — the memory layer for AI agents",
"type": "module",
"bin": {
"mem0": "./dist/index.js"
},
"scripts": {
"build": "tsup",
"dev": "tsx src/index.ts",
"test": "vitest run",
"test:watch": "vitest",
"lint": "biome check src/",
"lint:fix": "biome check --write src/",
"typecheck": "tsc --noEmit"
},
"engines": {
"node": ">=18.0.0"
},
"license": "Apache-2.0",
"author": "mem0.ai <founders@mem0.ai>",
"repository": {
"type": "git",
"url": "https://github.com/mem0ai/mem0",
"directory": "cli/node"
},
"keywords": ["mem0", "memory", "ai", "agents", "cli"],
"publishConfig": {
"access": "public"
},
"dependencies": {
"commander": "^12.0.0",
"chalk": "^5.3.0",
"cli-table3": "^0.6.4",
"ora": "^8.0.0",
"boxen": "^7.1.0"
},
"devDependencies": {
"typescript": "^5.4.0",
"tsup": "^8.0.0",
"tsx": "^4.7.0",
"vitest": "^1.5.0",
"@biomejs/biome": "^1.7.0",
"@types/node": "^20.0.0"
}
}
+2066
View File
File diff suppressed because it is too large Load Diff
+128
View File
@@ -0,0 +1,128 @@
/**
* Abstract backend interface and factory.
*/
import type { Mem0Config } from "../config.js";
import { PlatformBackend } from "./platform.js";
export interface AddOptions {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
metadata?: Record<string, unknown>;
immutable?: boolean;
infer?: boolean;
expires?: string;
categories?: string[];
enableGraph?: boolean;
}
export interface SearchOptions {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
topK?: number;
threshold?: number;
rerank?: boolean;
keyword?: boolean;
filters?: Record<string, unknown>;
fields?: string[];
enableGraph?: boolean;
}
export interface ListOptions {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
page?: number;
pageSize?: number;
category?: string;
after?: string;
before?: string;
enableGraph?: boolean;
}
export interface DeleteOptions {
all?: boolean;
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
}
export interface EntityIds {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
}
export interface Backend {
add(
content?: string,
messages?: Record<string, unknown>[],
opts?: AddOptions,
): Promise<Record<string, unknown>>;
search(
query: string,
opts?: SearchOptions,
): Promise<Record<string, unknown>[]>;
get(memoryId: string): Promise<Record<string, unknown>>;
listMemories(opts?: ListOptions): Promise<Record<string, unknown>[]>;
update(
memoryId: string,
content?: string,
metadata?: Record<string, unknown>,
): Promise<Record<string, unknown>>;
delete(
memoryId?: string,
opts?: DeleteOptions,
): Promise<Record<string, unknown>>;
deleteEntities(opts: EntityIds): Promise<Record<string, unknown>>;
status(opts?: { userId?: string; agentId?: string }): Promise<
Record<string, unknown>
>;
entities(entityType: string): Promise<Record<string, unknown>[]>;
listEvents(): Promise<Record<string, unknown>[]>;
getEvent(eventId: string): Promise<Record<string, unknown>>;
}
export class AuthError extends Error {
constructor(
message = "Authentication failed. Your API key may be invalid or expired.",
) {
super(message);
this.name = "AuthError";
}
}
export class NotFoundError extends Error {
constructor(path: string) {
super(`Resource not found: ${path}`);
this.name = "NotFoundError";
}
}
export class APIError extends Error {
constructor(path: string, detail: string) {
super(`Bad request to ${path}: ${detail}`);
this.name = "APIError";
}
}
export function getBackend(config: Mem0Config): Backend {
return new PlatformBackend(config.platform);
}
+14
View File
@@ -0,0 +1,14 @@
/**
* Backend factory re-export.
*/
export { getBackend } from "./base.js";
export type {
Backend,
AddOptions,
SearchOptions,
ListOptions,
DeleteOptions,
EntityIds,
} from "./base.js";
export { AuthError, NotFoundError, APIError } from "./base.js";
+346
View File
@@ -0,0 +1,346 @@
/**
* Platform (SaaS) backend — communicates with api.mem0.ai.
*/
import type { PlatformConfig } from "../config.js";
import {
APIError,
type AddOptions,
AuthError,
type Backend,
type DeleteOptions,
type EntityIds,
type ListOptions,
NotFoundError,
type SearchOptions,
} from "./base.js";
export class PlatformBackend implements Backend {
private baseUrl: string;
private headers: Record<string, string>;
constructor(config: PlatformConfig) {
this.baseUrl = config.baseUrl.replace(/\/+$/, "");
this.headers = {
Authorization: `Token ${config.apiKey}`,
"Content-Type": "application/json",
};
}
private async _request(
method: string,
path: string,
opts?: { json?: unknown; params?: Record<string, string> },
): Promise<unknown> {
let url = `${this.baseUrl}${path}`;
if (opts?.params) {
const qs = new URLSearchParams(opts.params).toString();
url += `?${qs}`;
}
const fetchOpts: RequestInit = {
method,
headers: this.headers,
signal: AbortSignal.timeout(30_000),
};
if (opts?.json) {
fetchOpts.body = JSON.stringify(opts.json);
}
const resp = await fetch(url, fetchOpts);
if (resp.status === 401) {
throw new AuthError();
}
if (resp.status === 404) {
throw new NotFoundError(path);
}
if (resp.status === 400) {
let detail: string;
try {
const body = (await resp.json()) as Record<string, unknown>;
detail =
((body.detail ?? body.message ?? JSON.stringify(body)) as string) ??
resp.statusText;
} catch {
detail = resp.statusText;
}
throw new APIError(path, detail);
}
if (!resp.ok) {
let detail: string = resp.statusText;
try {
const body = (await resp.json()) as Record<string, unknown>;
detail = (body.detail ?? body.message ?? resp.statusText) as string;
} catch {
/* ignore */
}
throw new Error(`HTTP ${resp.status}: ${detail}`);
}
if (resp.status === 204) {
return {};
}
return resp.json();
}
async add(
content?: string,
messages?: Record<string, unknown>[],
opts: AddOptions = {},
): Promise<Record<string, unknown>> {
const payload: Record<string, unknown> = {};
if (messages) {
payload.messages = messages;
} else if (content) {
payload.messages = [{ role: "user", content }];
}
if (opts.userId) payload.user_id = opts.userId;
if (opts.agentId) payload.agent_id = opts.agentId;
if (opts.appId) payload.app_id = opts.appId;
if (opts.runId) payload.run_id = opts.runId;
if (opts.metadata) payload.metadata = opts.metadata;
if (opts.immutable) payload.immutable = true;
if (opts.infer === false) payload.infer = false;
if (opts.expires) payload.expiration_date = opts.expires;
if (opts.categories) payload.categories = opts.categories;
if (opts.enableGraph) payload.enable_graph = true;
return (await this._request("POST", "/v1/memories/", {
json: payload,
})) as Record<string, unknown>;
}
private _buildFilters(opts: {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
extraFilters?: Record<string, unknown>;
}): Record<string, unknown> | undefined {
// If caller passed a pre-built filter structure, use it directly
if (
opts.extraFilters &&
("AND" in opts.extraFilters || "OR" in opts.extraFilters)
) {
return opts.extraFilters;
}
const andConditions: Record<string, unknown>[] = [];
if (opts.userId) andConditions.push({ user_id: opts.userId });
if (opts.agentId) andConditions.push({ agent_id: opts.agentId });
if (opts.appId) andConditions.push({ app_id: opts.appId });
if (opts.runId) andConditions.push({ run_id: opts.runId });
if (opts.extraFilters) {
for (const [k, v] of Object.entries(opts.extraFilters)) {
andConditions.push({ [k]: v });
}
}
if (andConditions.length === 1) return andConditions[0];
if (andConditions.length > 1) return { AND: andConditions };
return undefined;
}
async search(
query: string,
opts: SearchOptions = {},
): Promise<Record<string, unknown>[]> {
const payload: Record<string, unknown> = {
query,
top_k: opts.topK ?? 10,
threshold: opts.threshold ?? 0.3,
};
const apiFilters = this._buildFilters({
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
extraFilters: opts.filters,
});
if (apiFilters) payload.filters = apiFilters;
if (opts.rerank) payload.rerank = true;
if (opts.keyword) payload.keyword_search = true;
if (opts.fields) payload.fields = opts.fields;
if (opts.enableGraph) payload.enable_graph = true;
const result = (await this._request("POST", "/v2/memories/search/", {
json: payload,
})) as unknown;
if (Array.isArray(result)) return result;
const obj = result as Record<string, unknown>;
return (obj.results ?? obj.memories ?? []) as Record<string, unknown>[];
}
async get(memoryId: string): Promise<Record<string, unknown>> {
return (await this._request("GET", `/v1/memories/${memoryId}/`)) as Record<
string,
unknown
>;
}
async listMemories(
opts: ListOptions = {},
): Promise<Record<string, unknown>[]> {
const payload: Record<string, unknown> = {};
const params: Record<string, string> = {
page: String(opts.page ?? 1),
page_size: String(opts.pageSize ?? 100),
};
const extra: Record<string, unknown> = {};
if (opts.category) {
extra.categories = { contains: opts.category };
}
if (opts.after) {
extra.created_at = {
...(extra.created_at as Record<string, unknown> | undefined),
gte: opts.after,
};
}
if (opts.before) {
extra.created_at = {
...(extra.created_at as Record<string, unknown> | undefined),
lte: opts.before,
};
}
const apiFilters = this._buildFilters({
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
extraFilters: Object.keys(extra).length > 0 ? extra : undefined,
});
if (apiFilters) payload.filters = apiFilters;
if (opts.enableGraph) payload.enable_graph = true;
const result = (await this._request("POST", "/v2/memories/", {
json: payload,
params,
})) as unknown;
if (Array.isArray(result)) return result;
const obj = result as Record<string, unknown>;
return (obj.results ?? obj.memories ?? []) as Record<string, unknown>[];
}
async update(
memoryId: string,
content?: string,
metadata?: Record<string, unknown>,
): Promise<Record<string, unknown>> {
const payload: Record<string, unknown> = {};
if (content) payload.text = content;
if (metadata) payload.metadata = metadata;
return (await this._request("PUT", `/v1/memories/${memoryId}/`, {
json: payload,
})) as Record<string, unknown>;
}
async delete(
memoryId?: string,
opts: DeleteOptions = {},
): Promise<Record<string, unknown>> {
if (opts.all) {
const params: Record<string, string> = {};
if (opts.userId) params.user_id = opts.userId;
if (opts.agentId) params.agent_id = opts.agentId;
if (opts.appId) params.app_id = opts.appId;
if (opts.runId) params.run_id = opts.runId;
return (await this._request("DELETE", "/v1/memories/", {
params,
})) as Record<string, unknown>;
}
if (memoryId) {
return (await this._request(
"DELETE",
`/v1/memories/${memoryId}/`,
)) as Record<string, unknown>;
}
throw new Error("Either memoryId or --all is required");
}
async deleteEntities(opts: EntityIds): Promise<Record<string, unknown>> {
// v2 endpoint: DELETE /v2/entities/{entity_type}/{entity_id}/
const typeMap: [string, string | undefined][] = [
["user", opts.userId],
["agent", opts.agentId],
["app", opts.appId],
["run", opts.runId],
];
const entities = typeMap.filter(([, v]) => v) as [string, string][];
if (entities.length === 0) {
throw new Error("At least one entity ID is required for deleteEntities.");
}
// Delete each provided entity via the v2 path-based endpoint
let result: Record<string, unknown> = {};
for (const [entityType, entityId] of entities) {
result = (await this._request(
"DELETE",
`/v2/entities/${entityType}/${entityId}/`,
)) as Record<string, unknown>;
}
return result;
}
async status(
opts: { userId?: string; agentId?: string } = {},
): Promise<Record<string, unknown>> {
try {
await this._request("GET", "/v1/ping/");
return { connected: true, backend: "platform", base_url: this.baseUrl };
} catch (e) {
return {
connected: false,
backend: "platform",
error: e instanceof Error ? e.message : String(e),
};
}
}
async entities(entityType: string): Promise<Record<string, unknown>[]> {
const result = (await this._request("GET", "/v1/entities/")) as unknown;
let items: Record<string, unknown>[];
if (Array.isArray(result)) {
items = result;
} else {
items = ((result as Record<string, unknown>).results ?? []) as Record<
string,
unknown
>[];
}
const typeMap: Record<string, string> = {
users: "user",
agents: "agent",
apps: "app",
runs: "run",
};
const targetType = typeMap[entityType];
if (targetType) {
items = items.filter(
(e) => (e.type as string | undefined)?.toLowerCase() === targetType,
);
}
return items;
}
async listEvents(): Promise<Record<string, unknown>[]> {
const result = (await this._request("GET", "/v1/events/")) as unknown;
if (Array.isArray(result)) return result;
return ((result as Record<string, unknown>).results ?? []) as Record<
string,
unknown
>[];
}
async getEvent(eventId: string): Promise<Record<string, unknown>> {
return (await this._request("GET", `/v1/event/${eventId}/`)) as Record<
string,
unknown
>;
}
}
+172
View File
@@ -0,0 +1,172 @@
/**
* Branding and ASCII art for mem0 CLI.
*/
import chalk from "chalk";
import ora, { type Ora } from "ora";
import { getCurrentCommand, isAgentMode } from "./state.js";
import { CLI_VERSION } from "./version.js";
export const LOGO = `
███╗ ███╗███████╗███╗ ███╗ ██████╗ ██████╗██╗ ██╗
████╗ ████║██╔════╝████╗ ████║██╔═████╗ ██╔════╝██║ ██║
██╔████╔██║█████╗ ██╔████╔██║██║██╔██║ ██║ ██║ ██║
██║╚██╔╝██║██╔══╝ ██║╚██╔╝██║████╔╝██║ ██║ ██║ ██║
██║ ╚═╝ ██║███████╗██║ ╚═╝ ██║╚██████╔╝ ╚██████╗███████╗██║
╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚══════╝╚═╝
`;
export const LOGO_MINI = "◆ mem0";
export const TAGLINE = "The Memory Layer for AI Agents";
export const BRAND_COLOR = "#F1C96C";
export const ACCENT_COLOR = "#F5D78E";
export const SUCCESS_COLOR = "#22c55e";
export const ERROR_COLOR = "#ef4444";
export const WARNING_COLOR = "#f59e0b";
export const DIM_COLOR = "#6b7280";
const brand = chalk.hex(BRAND_COLOR);
const accent = chalk.hex(ACCENT_COLOR);
const success = chalk.hex(SUCCESS_COLOR);
const error = chalk.hex(ERROR_COLOR);
const warning = chalk.hex(WARNING_COLOR);
const dim = chalk.hex(DIM_COLOR);
/**
* Choose a symbol based on TTY/NO_COLOR. Fancy for interactive terminals,
* plain-text for piped/non-TTY or NO_COLOR environments.
*/
export function sym(fancy: string, plain: string): string {
if (!process.stdout.isTTY || process.env.NO_COLOR) return plain;
return fancy;
}
export function printBanner(): void {
if (isAgentMode()) return;
const pad = 3; // horizontal padding each side (matches Rich's padding=(0, 2))
const logoLines = LOGO.trimEnd().split("\n");
const tagline = ` ${TAGLINE}`;
const subtitle = `Node.js SDK · v${CLI_VERSION}`;
const contentLines = ["", ...logoLines, "", tagline, ""];
// Compute inner width from longest content line + padding both sides
const maxContent = Math.max(...contentLines.map((l) => l.length));
const innerWidth = maxContent + pad * 2;
const totalWidth = innerWidth + 2; // + 2 for │ borders
const topBorder = brand(`╭${"─".repeat(totalWidth - 2)}╮`);
const subtitleFill = totalWidth - 2 - subtitle.length - 3; // 3 = "─ " before subtitle + "─" after
const bottomBorder = brand(
`╰${"─".repeat(subtitleFill)} ${dim(subtitle)} ${"─"}╯`,
);
const body = contentLines.map((line) => {
const rightPad = innerWidth - pad - line.length;
return `${brand("│")}${" ".repeat(pad)}${brand.bold(line)}${" ".repeat(Math.max(rightPad, 0))}${brand("│")}`;
});
// Re-color tagline line with accent instead of brand.bold
const taglineIdx = body.length - 2; // second-to-last (before trailing empty line)
const taglineRightPad = innerWidth - pad - tagline.length;
body[taglineIdx] =
`${brand("│")}${" ".repeat(pad)}${accent(tagline)}${" ".repeat(Math.max(taglineRightPad, 0))}${brand("│")}`;
console.log(topBorder);
for (const line of body) console.log(line);
console.log(bottomBorder);
}
export function printSuccess(message: string): void {
if (isAgentMode()) return;
console.log(`${success(sym("✓", "[ok]"))} ${message}`);
}
export function printError(message: string, hint?: string): void {
if (isAgentMode()) {
const envelope = {
status: "error",
command: getCurrentCommand(),
error: message,
data: null,
};
console.log(JSON.stringify(envelope));
return;
}
console.error(`${error(`${sym("✗", "[error]")} Error:`)} ${message}`);
const resolvedHint =
hint ??
(message.includes("Authentication failed")
? `Run ${brand("mem0 init")} to reconfigure your API key · https://app.mem0.ai/dashboard/api-keys`
: undefined);
if (resolvedHint) {
console.error(` ${dim(resolvedHint)}`);
}
}
export function printWarning(message: string): void {
console.error(`${warning(sym("⚠", "[warn]"))} ${message}`);
}
export function printInfo(message: string): void {
if (isAgentMode()) return;
console.error(`${brand(sym("◆", "*"))} ${message}`);
}
export function printScope(ids: Record<string, string | undefined>): void {
if (isAgentMode()) return;
const parts: string[] = [];
for (const [key, val] of Object.entries(ids)) {
if (val) {
parts.push(`${key}=${val}`);
}
}
if (parts.length > 0) {
console.error(` ${dim(`Scope: ${parts.join(", ")}`)}`);
}
}
export interface TimedStatusContext {
successMsg: string;
errorMsg: string;
}
/**
* Run an async function with a spinner, timing the operation.
* Equivalent to Python's timed_status context manager.
*/
export async function timedStatus<T>(
message: string,
fn: (ctx: TimedStatusContext) => Promise<T>,
): Promise<T> {
if (isAgentMode()) {
const ctx: TimedStatusContext = { successMsg: "", errorMsg: "" };
return fn(ctx);
}
const ctx: TimedStatusContext = { successMsg: "", errorMsg: "" };
const spinner = ora({
text: dim(message),
color: "yellow",
stream: process.stderr,
}).start();
const start = performance.now();
try {
const result = await fn(ctx);
const elapsed = ((performance.now() - start) / 1000).toFixed(2);
spinner.stop();
if (ctx.successMsg) {
console.error(`${success("✓")} ${ctx.successMsg} (${elapsed}s)`);
}
return result;
} catch (err) {
const elapsed = ((performance.now() - start) / 1000).toFixed(2);
spinner.stop();
if (ctx.errorMsg) {
printError(`${ctx.errorMsg} (${elapsed}s)`);
}
throw err;
}
}
/** Format helpers using brand colors for external use. */
export const colors = { brand, accent, success, error, warning, dim };
+111
View File
@@ -0,0 +1,111 @@
/**
* Config management commands: show, set, get.
*/
import Table from "cli-table3";
import { colors, printError, printSuccess } from "../branding.js";
import {
getNestedValue,
loadConfig,
redactKey,
saveConfig,
setNestedValue,
} from "../config.js";
import { formatAgentEnvelope, formatJsonEnvelope } from "../output.js";
import { isAgentMode, setCurrentCommand } from "../state.js";
const { brand, accent, dim } = colors;
export function cmdConfigShow(opts: { output?: string } = {}): void {
setCurrentCommand("config show");
const config = loadConfig();
if (opts.output === "agent" || opts.output === "json") {
formatAgentEnvelope({
command: "config show",
data: {
defaults: {
user_id: config.defaults.userId || null,
agent_id: config.defaults.agentId || null,
app_id: config.defaults.appId || null,
run_id: config.defaults.runId || null,
enable_graph: config.defaults.enableGraph,
},
platform: {
api_key: redactKey(config.platform.apiKey),
base_url: config.platform.baseUrl,
},
},
});
return;
}
console.log();
console.log(` ${brand("◆ mem0 Configuration")}\n`);
const table = new Table({
head: [accent("Key"), accent("Value")],
style: { head: [], border: [] },
});
// Defaults
table.push(["defaults.user_id", config.defaults.userId || dim("(not set)")]);
table.push([
"defaults.agent_id",
config.defaults.agentId || 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.enable_graph", String(config.defaults.enableGraph)]);
table.push(["", ""]);
// Platform
table.push(["platform.api_key", redactKey(config.platform.apiKey)]);
table.push(["platform.base_url", config.platform.baseUrl]);
console.log(table.toString());
console.log();
}
export function cmdConfigGet(key: string): void {
setCurrentCommand("config get");
const config = loadConfig();
const value = getNestedValue(config, key);
if (value === undefined) {
printError(`Unknown config key: ${key}`);
} else {
// Redact secrets
const displayValue =
key.includes("api_key") || key.split(".").pop() === "key"
? redactKey(String(value))
: String(value);
if (isAgentMode()) {
formatAgentEnvelope({
command: "config get",
data: { key, value: displayValue },
});
} else {
console.log(displayValue);
}
}
}
export function cmdConfigSet(key: string, value: string): void {
setCurrentCommand("config set");
const config = loadConfig();
if (setNestedValue(config, key, value)) {
saveConfig(config);
const display = key.includes("key") ? redactKey(value) : value;
if (isAgentMode()) {
formatAgentEnvelope({
command: "config set",
data: { key, value: display },
});
} else {
printSuccess(`${key} = ${display}`);
}
} else {
printError(`Unknown config key: ${key}`);
}
}
+168
View File
@@ -0,0 +1,168 @@
/**
* Entity management commands.
*/
import readline from "node:readline";
import Table from "cli-table3";
import type { Backend } from "../backend/base.js";
import {
colors,
printError,
printInfo,
printSuccess,
timedStatus,
} from "../branding.js";
import { formatAgentEnvelope, formatJson } from "../output.js";
import { setCurrentCommand } from "../state.js";
const { brand, accent, dim } = colors;
const VALID_TYPES = new Set(["users", "agents", "apps", "runs"]);
export async function cmdEntitiesList(
backend: Backend,
entityType: string,
opts: { output: string },
): Promise<void> {
setCurrentCommand("entity list");
if (!VALID_TYPES.has(entityType)) {
printError(
`Invalid entity type: ${entityType}. Use: ${[...VALID_TYPES].join(", ")}`,
);
process.exit(1);
}
const start = performance.now();
let results: Record<string, unknown>[];
try {
results = await timedStatus(`Fetching ${entityType}...`, async () => {
return backend.entities(entityType);
});
} catch (e) {
printError(
e instanceof Error ? e.message : String(e),
"This feature may require the mem0 Platform.",
);
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent" || opts.output === "json") {
formatAgentEnvelope({
command: "entity list",
data: results,
count: results.length,
durationMs: Math.round(elapsed * 1000),
});
return;
}
if (!results.length) {
printInfo(`No ${entityType} found.`);
return;
}
const table = new Table({
head: [accent("Name / ID"), accent("Created")],
style: { head: [], border: [] },
});
for (const entity of results) {
const name = String(entity.name ?? entity.id ?? "—");
const created = String(entity.created_at ?? "—").slice(0, 10);
table.push([name, created]);
}
console.log();
console.log(table.toString());
console.log(
` ${dim(`${results.length} ${entityType} (${elapsed.toFixed(2)}s)`)}`,
);
console.log();
}
export async function cmdEntitiesDelete(
backend: Backend,
opts: {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
dryRun?: boolean;
force: boolean;
output: string;
},
): Promise<void> {
setCurrentCommand("entity delete");
const { isAgentMode } = await import("../state.js");
if (isAgentMode() && !opts.force) {
printError("Destructive operation requires --force in agent mode.");
process.exit(1);
}
if (!opts.userId && !opts.agentId && !opts.appId && !opts.runId) {
printError(
"Provide at least one of --user-id, --agent-id, --app-id, --run-id.",
);
process.exit(1);
}
const scopeParts: string[] = [];
if (opts.userId) scopeParts.push(`user=${opts.userId}`);
if (opts.agentId) scopeParts.push(`agent=${opts.agentId}`);
if (opts.appId) scopeParts.push(`app=${opts.appId}`);
if (opts.runId) scopeParts.push(`run=${opts.runId}`);
const scope = scopeParts.join(", ");
if (opts.dryRun) {
printInfo(`Would delete entity ${scope} and all its memories.`);
printInfo("No changes made.");
return;
}
if (!opts.force) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const answer = await new Promise<string>((resolve) => {
rl.question(
`\n \u26a0 Delete entity ${scope} AND all its memories? This cannot be undone. [y/N] `,
resolve,
);
});
rl.close();
if (answer.toLowerCase() !== "y") {
printInfo("Cancelled.");
process.exit(0);
}
}
const start = performance.now();
let result: Record<string, unknown>;
try {
result = await timedStatus("Deleting entity...", async () => {
return backend.deleteEntities({
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
});
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent") {
formatAgentEnvelope({
command: "entity delete",
data: { deleted: true },
durationMs: Math.round(elapsed * 1000),
});
} else if (opts.output === "json") {
formatJson(result);
} else if (opts.output !== "quiet") {
printSuccess(`Entity deleted with all memories (${elapsed.toFixed(2)}s)`);
}
}
+169
View File
@@ -0,0 +1,169 @@
/**
* Event commands: list and status.
*/
import boxen from "boxen";
import Table from "cli-table3";
import type { Backend } from "../backend/base.js";
import { colors, printError, printInfo, timedStatus } from "../branding.js";
import { formatAgentEnvelope, formatJson } from "../output.js";
import { setCurrentCommand } from "../state.js";
const { brand, accent, success, error: errorColor, warning, dim } = colors;
function statusStyled(status: string): string {
switch (status.toUpperCase()) {
case "SUCCEEDED":
return success("SUCCEEDED");
case "PENDING":
return accent("PENDING");
case "FAILED":
return errorColor("FAILED");
case "PROCESSING":
return warning("PROCESSING");
default:
return status;
}
}
export async function cmdEventList(
backend: Backend,
opts: { output: string },
): Promise<void> {
setCurrentCommand("event list");
const start = performance.now();
let results: Record<string, unknown>[];
try {
results = await timedStatus("Fetching events...", async () => {
return backend.listEvents();
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent" || opts.output === "json") {
formatAgentEnvelope({
command: "event list",
data: results,
count: results.length,
durationMs: Math.round(elapsed * 1000),
});
return;
}
if (results.length === 0) {
console.log();
printInfo("No events found.");
console.log();
return;
}
const table = new Table({
head: [
accent("Event ID"),
accent("Type"),
accent("Status"),
accent("Latency"),
accent("Created"),
],
colWidths: [12, 14, 14, 10, 22],
wordWrap: true,
style: { head: [], border: [] },
});
for (const ev of results) {
const evId = String(ev.id ?? "").slice(0, 8);
const evType = String(ev.event_type ?? "—");
const status = String(ev.status ?? "—");
const latency = ev.latency as number | undefined;
const latencyStr = latency !== undefined ? `${Math.round(latency)}ms` : "—";
const created = String(ev.created_at ?? "—")
.slice(0, 19)
.replace("T", " ");
table.push([dim(evId), evType, statusStyled(status), latencyStr, created]);
}
console.log();
console.log(table.toString());
console.log(
` ${dim(`${results.length} event${results.length !== 1 ? "s" : ""}`)}`,
);
console.log();
}
export async function cmdEventStatus(
backend: Backend,
eventId: string,
opts: { output: string },
): Promise<void> {
setCurrentCommand("event status");
const start = performance.now();
let ev: Record<string, unknown>;
try {
ev = await timedStatus("Fetching event...", async () => {
return backend.getEvent(eventId);
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent" || opts.output === "json") {
formatAgentEnvelope({
command: "event status",
data: ev,
durationMs: Math.round(elapsed * 1000),
});
return;
}
const status = String(ev.status ?? "—");
const evType = String(ev.event_type ?? "—");
const latency = ev.latency as number | undefined;
const latencyStr = latency !== undefined ? `${Math.round(latency)}ms` : "—";
const created = String(ev.created_at ?? "—")
.slice(0, 19)
.replace("T", " ");
const updated = String(ev.updated_at ?? "—")
.slice(0, 19)
.replace("T", " ");
const results = ev.results as Record<string, unknown>[] | undefined;
const lines: string[] = [];
lines.push(` ${dim("Event ID:")} ${eventId}`);
lines.push(` ${dim("Type:")} ${evType}`);
lines.push(` ${dim("Status:")} ${statusStyled(status)}`);
lines.push(` ${dim("Latency:")} ${latencyStr}`);
lines.push(` ${dim("Created:")} ${created}`);
lines.push(` ${dim("Updated:")} ${updated}`);
if (results && results.length > 0) {
lines.push("");
lines.push(` ${dim(`Results (${results.length}):`)}`);
for (const r of results) {
const memId = String(r.id ?? "").slice(0, 8);
const data = r.data as Record<string, unknown> | undefined;
const memory = data?.memory ? String(data.memory) : "";
const evName = String(r.event ?? "");
const user = String(r.user_id ?? "");
let detail = `${evName} ${memory}`;
if (user) detail += ` ${dim(`(user_id=${user})`)}`;
lines.push(` ${success("·")} ${detail} ${dim(`(${memId})`)}`);
}
}
const content = lines.join("\n");
console.log();
console.log(
boxen(content, {
title: brand("Event Status"),
titleAlignment: "left",
borderColor: "magenta",
padding: 1,
}),
);
console.log();
}
+428
View File
@@ -0,0 +1,428 @@
/**
* mem0 init — interactive setup wizard.
*/
import fs from "node:fs";
import readline from "node:readline";
import { PlatformBackend } from "../backend/platform.js";
import {
colors,
printBanner,
printError,
printInfo,
printSuccess,
} from "../branding.js";
import {
CONFIG_FILE,
DEFAULT_BASE_URL,
type Mem0Config,
createDefaultConfig,
loadConfig,
redactKey,
saveConfig,
} from "../config.js";
const { brand, dim } = colors;
const EMAIL_RE = /^[^@\s]+@[^@\s]+\.[^@\s]+$/;
function validateEmail(email: string): void {
if (!EMAIL_RE.test(email)) {
printError(`Invalid email address: ${JSON.stringify(email)}`);
process.exit(1);
}
}
async function emailLogin(
email: string,
code: string | undefined,
baseUrl: string,
): Promise<Record<string, unknown>> {
const url = baseUrl.replace(/\/+$/, "");
let codeValue = code;
if (!codeValue) {
const resp = await fetch(`${url}/api/v1/auth/email_code/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email }),
signal: AbortSignal.timeout(30_000),
});
if (resp.status === 429) {
printError("Too many attempts. Try again in a few minutes.");
process.exit(1);
}
if (!resp.ok) {
let detail: string;
try {
const body = (await resp.json()) as Record<string, unknown>;
detail = (body.error ?? body.detail ?? resp.statusText) as string;
} catch {
detail = resp.statusText;
}
printError(`Failed to send code: ${detail}`);
process.exit(1);
}
printSuccess("Verification code sent! Check your email.");
if (!process.stdin.isTTY) {
printError(
"No --code provided and terminal is non-interactive.",
"Run: mem0 init --email <email> --code <code>",
);
process.exit(1);
}
console.log();
const entered = await promptLine(` ${brand("Verification Code")}`);
if (!entered) {
printError("Code is required.");
process.exit(1);
}
codeValue = entered;
}
const verifyResp = await fetch(`${url}/api/v1/auth/email_code/verify/`, {
method: "POST",
headers: { "Content-Type": "application/json" },
body: JSON.stringify({ email, code: codeValue.trim() }),
signal: AbortSignal.timeout(30_000),
});
if (verifyResp.status === 429) {
printError("Too many attempts. Try again in a few minutes.");
process.exit(1);
}
if (!verifyResp.ok) {
let detail: string;
try {
const body = (await verifyResp.json()) as Record<string, unknown>;
detail = (body.error ?? body.detail ?? verifyResp.statusText) as string;
} catch {
detail = verifyResp.statusText;
}
printError(`Verification failed: ${detail}`);
process.exit(1);
}
return verifyResp.json() as Promise<Record<string, unknown>>;
}
function promptSecret(label: string): Promise<string> {
return new Promise((resolve, reject) => {
process.stdout.write(label);
if (process.stdin.isTTY) {
process.stdin.setRawMode(true);
}
process.stdin.resume();
process.stdin.setEncoding("utf-8");
const chars: string[] = [];
const onData = (key: string) => {
for (const ch of key) {
if (ch === "\r" || ch === "\n") {
cleanup();
process.stdout.write("\n");
resolve(chars.join(""));
return;
}
if (ch === "\x03") {
cleanup();
reject(new Error("Interrupted"));
return;
}
if (ch === "\x7f" || ch === "\x08") {
// backspace
if (chars.length > 0) {
chars.pop();
process.stdout.write("\b \b");
}
} else if (ch === "\x15") {
// Ctrl+U — clear line
process.stdout.write("\b \b".repeat(chars.length));
chars.length = 0;
} else if (ch >= " ") {
chars.push(ch);
process.stdout.write("*");
}
}
};
const cleanup = () => {
process.stdin.removeListener("data", onData);
if (process.stdin.isTTY) {
process.stdin.setRawMode(false);
}
process.stdin.pause();
};
process.stdin.on("data", onData);
});
}
function promptLine(label: string, defaultValue?: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const prompt = defaultValue ? `${label} [${defaultValue}]: ` : `${label}: `;
return new Promise((resolve) => {
rl.question(prompt, (answer) => {
rl.close();
resolve(answer.trim() || defaultValue || "");
});
});
}
async function setupPlatform(config: Mem0Config): Promise<void> {
console.log();
console.log(
` ${dim("Get your API key at https://app.mem0.ai/dashboard/api-keys")}`,
);
console.log();
process.stdout.write(` ${brand("API Key")}: `);
const apiKey = await promptSecret("");
if (!apiKey) {
printError("API key is required.");
process.exit(1);
}
config.platform.apiKey = apiKey;
}
async function setupDefaults(config: Mem0Config): Promise<void> {
console.log();
printInfo("Set default entity IDs (press Enter to skip).\n");
const _systemUser = process.env.USER || process.env.USERNAME || "mem0-cli";
const userId = await promptLine(
` ${brand("Default User ID")} ${dim("(recommended)")}`,
_systemUser,
);
if (userId) config.defaults.userId = userId;
}
async function validatePlatform(config: Mem0Config): Promise<void> {
console.log();
printInfo("Validating connection...");
try {
const backend = new PlatformBackend(config.platform);
const status = await backend.status({
userId: config.defaults.userId || undefined,
agentId: config.defaults.agentId || undefined,
});
if (status.connected) {
printSuccess("Connected to mem0 Platform!");
} else {
printError(
`Could not connect: ${status.error ?? "Unknown error"}`,
"Visit https://app.mem0.ai/dashboard/api-keys to get a new key, or run mem0 init again.",
);
}
} catch (e) {
printError(`Connection test failed: ${e instanceof Error ? e.message : e}`);
}
}
export async function runInit(
opts: {
apiKey?: string;
userId?: string;
email?: string;
code?: string;
force?: boolean;
} = {},
): Promise<void> {
const config = createDefaultConfig();
const savedConfig = loadConfig();
const baseUrl =
process.env.MEM0_BASE_URL ||
savedConfig.platform.baseUrl ||
DEFAULT_BASE_URL;
// Guards
if (opts.code && !opts.email) {
printError("--code requires --email.");
process.exit(1);
}
if (opts.email && opts.apiKey) {
printError("Cannot use both --api-key and --email.");
process.exit(1);
}
// Warn if an existing config with an API key would be overwritten
if (
!opts.force &&
fs.existsSync(CONFIG_FILE) &&
savedConfig.platform.apiKey
) {
console.log(
`\n ${brand("Existing configuration found")} ${dim(`(API key: ${redactKey(savedConfig.platform.apiKey)})`)}`,
);
if (process.stdin.isTTY) {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const answer = await new Promise<string>((resolve) => {
rl.question(
" Overwrite existing config? This cannot be undone. [y/N] ",
resolve,
);
});
rl.close();
if (answer.toLowerCase() !== "y") {
printInfo("Cancelled. Use --force to skip this check.");
process.exit(0);
}
} else {
printError(
"Existing config would be overwritten.",
"Use --force to overwrite.",
);
process.exit(1);
}
}
// ── Email login flow ──────────────────────────────────────────────────────
if (opts.email) {
const email = opts.email.trim().toLowerCase();
validateEmail(email);
printBanner();
console.log();
printInfo(`Logging in as ${email}...\n`);
const result = await emailLogin(email, opts.code, baseUrl);
const apiKeyVal = result.api_key as string | undefined;
if (!apiKeyVal) {
printError(
"Auth succeeded but no API key was returned. Contact support.",
);
process.exit(1);
}
config.platform.apiKey = apiKeyVal;
config.platform.baseUrl = baseUrl;
config.defaults.userId =
opts.userId || process.env.USER || process.env.USERNAME || "mem0-cli";
saveConfig(config);
console.log();
printSuccess("Authenticated! Configuration saved to ~/.mem0/config.json");
console.log();
console.log(` ${dim("Get started:")}`);
console.log(` ${dim(' mem0 add "I prefer dark mode"')}`);
console.log(` ${dim(' mem0 search "preferences"')}`);
console.log();
return;
}
// ── API key flow ──────────────────────────────────────────────────────────
// Non-TTY: resolve defaults so partial flags work in pipelines / CI
if (!process.stdin.isTTY) {
if (!opts.apiKey) {
printError(
"Non-interactive terminal detected and --api-key is required.",
"Usage: mem0 init --api-key <key> [--user-id <id>]",
);
process.exit(1);
}
opts.userId =
opts.userId || process.env.USER || process.env.USERNAME || "mem0-cli";
}
// Non-interactive: both flags provided
if (opts.apiKey && opts.userId) {
config.platform.apiKey = opts.apiKey;
config.defaults.userId = opts.userId;
await validatePlatform(config);
saveConfig(config);
printSuccess("Configuration saved to ~/.mem0/config.json");
return;
}
printBanner();
console.log();
printInfo("Welcome! Let's set up your mem0 CLI.\n");
// Use provided API key or prompt
if (opts.apiKey) {
config.platform.apiKey = opts.apiKey;
} else {
console.log(` ${brand("How would you like to authenticate?")}`);
console.log(` ${dim("1.")} Login with email ${dim("(recommended)")}`);
console.log(` ${dim("2.")} Enter API key manually`);
console.log();
const choice = await promptLine(` ${brand("Choose")} [1/2]`, "1");
if (choice === "1") {
console.log();
const emailAddr = await promptLine(` ${brand("Email")}`);
if (!emailAddr) {
printError("Email is required.");
process.exit(1);
}
const email = emailAddr.trim().toLowerCase();
validateEmail(email);
printInfo(`Logging in as ${email}...\n`);
const result = await emailLogin(email, undefined, baseUrl);
const apiKeyVal = result.api_key as string | undefined;
if (!apiKeyVal) {
printError(
"Auth succeeded but no API key was returned. Contact support.",
);
process.exit(1);
}
config.platform.apiKey = apiKeyVal;
config.platform.baseUrl = baseUrl;
config.defaults.userId =
opts.userId || process.env.USER || process.env.USERNAME || "mem0-cli";
saveConfig(config);
console.log();
printSuccess("Authenticated! Configuration saved to ~/.mem0/config.json");
console.log();
console.log(` ${dim("Get started:")}`);
console.log(` ${dim(' mem0 add "I prefer dark mode"')}`);
console.log(` ${dim(' mem0 search "preferences"')}`);
console.log();
return;
}
// choice === "2": fall through to API key prompt
await setupPlatform(config);
}
// Use provided user ID or prompt
if (opts.userId) {
config.defaults.userId = opts.userId;
} else {
await setupDefaults(config);
}
await validatePlatform(config);
saveConfig(config);
console.log();
printSuccess("Configuration saved to ~/.mem0/config.json");
console.log();
console.log(` ${dim("Get started:")}`);
if (config.defaults.userId) {
console.log(` ${dim(' mem0 add "I prefer dark mode"')}`);
console.log(` ${dim(' mem0 search "preferences"')}`);
} else {
console.log(` ${dim(' mem0 add "I prefer dark mode" --user-id alice')}`);
console.log(` ${dim(' mem0 search "preferences" --user-id alice')}`);
}
console.log();
}
+709
View File
@@ -0,0 +1,709 @@
/**
* Memory CRUD commands: add, search, get, list, update, delete.
*/
import fs from "node:fs";
import type { Backend } from "../backend/base.js";
import {
printError,
printInfo,
printScope,
printSuccess,
timedStatus,
} from "../branding.js";
import {
formatAddResult,
formatAgentEnvelope,
formatJson,
formatJsonEnvelope,
formatMemoriesTable,
formatMemoriesText,
formatSingleMemory,
printResultSummary,
} from "../output.js";
import { isAgentMode, setCurrentCommand } from "../state.js";
/** True only when stdin is an actual pipe or file redirect — never in agent mode. */
function _stdinIsPiped(): boolean {
if (isAgentMode()) return false;
try {
const stat = fs.fstatSync(0);
return stat.isFIFO() || stat.isFile();
} catch {
return false;
}
}
export async function cmdAdd(
backend: Backend,
text: string | undefined,
opts: {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
messages?: string;
file?: string;
metadata?: string;
immutable: boolean;
noInfer: boolean;
expires?: string;
categories?: string;
enableGraph: boolean;
output: string;
},
): Promise<void> {
setCurrentCommand("add");
let msgs: Record<string, unknown>[] | undefined;
let content = text;
// Read from file
if (opts.file) {
try {
const raw = fs.readFileSync(opts.file, "utf-8");
msgs = JSON.parse(raw);
} catch (e) {
printError(`Failed to read file: ${e instanceof Error ? e.message : e}`);
process.exit(1);
}
}
// Parse messages JSON
else if (opts.messages) {
try {
msgs = JSON.parse(opts.messages);
} catch (e) {
printError(
`Invalid JSON in --messages: ${e instanceof Error ? e.message : e}`,
);
process.exit(1);
}
}
// Read from stdin only if stdin is an actual pipe or file redirect
else if (!content && _stdinIsPiped()) {
content = fs.readFileSync(0, "utf-8").trim();
}
if (content !== undefined && content.trim() === "") {
printError("Content cannot be empty.");
process.exit(1);
}
if (!content && !msgs) {
printError(
"No content provided. Pass text, --messages, --file, or pipe via stdin.",
);
process.exit(1);
}
// Validate --expires
if (opts.expires) {
if (!/^\d{4}-\d{2}-\d{2}$/.test(opts.expires)) {
printError(
"Invalid date format for --expires. Use YYYY-MM-DD (e.g. 2025-12-31).",
);
process.exit(1);
}
if (new Date(opts.expires) <= new Date()) {
printError("--expires date must be in the future.");
process.exit(1);
}
}
let meta: Record<string, unknown> | undefined;
if (opts.metadata) {
try {
meta = JSON.parse(opts.metadata);
} catch {
printError("Invalid JSON in --metadata.");
process.exit(1);
}
}
let cats: string[] | undefined;
if (opts.categories) {
try {
cats = JSON.parse(opts.categories);
} catch {
cats = opts.categories.split(",").map((c) => c.trim());
}
}
let result: Record<string, unknown>;
try {
result = await timedStatus("Adding memory...", async () => {
return backend.add(content ?? undefined, msgs, {
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
metadata: meta,
immutable: opts.immutable,
infer: !opts.noInfer,
expires: opts.expires,
categories: cats,
enableGraph: opts.enableGraph,
});
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
if (opts.output === "quiet") return;
// Deduplicate PENDING entries sharing the same event_id across all output modes
const rawResults: Record<string, unknown>[] = Array.isArray(result)
? result
: ((result.results as Record<string, unknown>[]) ?? [result]);
const seenEvents = new Set<string>();
const deduped: Record<string, unknown>[] = [];
for (const r of rawResults) {
if (r.status === "PENDING") {
const eid = (r.event_id as string) ?? "";
if (eid && seenEvents.has(eid)) continue;
if (eid) seenEvents.add(eid);
}
deduped.push(r);
}
// Write back so downstream formatters see deduplicated data
const dedupedResult: Record<string, unknown> = Array.isArray(result)
? (deduped as unknown as Record<string, unknown>)
: { ...result, results: deduped };
if (opts.output === "agent") {
const scope: Record<string, string | undefined> = {
user_id: opts.userId,
agent_id: opts.agentId,
app_id: opts.appId,
run_id: opts.runId,
};
formatAgentEnvelope({
command: "add",
data: deduped,
scope,
count: deduped.length,
});
return;
}
if (opts.output === "json") {
formatAddResult(dedupedResult, opts.output);
return;
}
console.log();
printScope({
user_id: opts.userId,
agent_id: opts.agentId,
app_id: opts.appId,
run_id: opts.runId,
});
const count = deduped.length;
const allPending = count > 0 && deduped.every((r) => r.status === "PENDING");
if (allPending) {
printSuccess(
`Memory queued — ${count} event${count !== 1 ? "s" : ""} pending`,
);
} else {
printSuccess(
`Memory processed — ${count} memor${count === 1 ? "y" : "ies"} extracted`,
);
}
formatAddResult(dedupedResult, opts.output);
}
export async function cmdSearch(
backend: Backend,
query: string | undefined,
opts: {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
topK: number;
threshold: number;
rerank: boolean;
keyword: boolean;
filterJson?: string;
fields?: string;
enableGraph: boolean;
output: string;
},
): Promise<void> {
setCurrentCommand("search");
if (!query) {
printError("No query provided. Pass a query argument or pipe via stdin.");
process.exit(1);
}
let filters: Record<string, unknown> | undefined;
if (opts.filterJson) {
try {
filters = JSON.parse(opts.filterJson);
} catch {
printError("Invalid JSON in --filter.");
process.exit(1);
}
}
const fieldList = opts.fields
? opts.fields.split(",").map((f) => f.trim())
: undefined;
if (opts.topK < 1) {
printError("--top-k must be >= 1.");
process.exit(1);
}
if (opts.threshold < 0 || opts.threshold > 1) {
printError("--threshold must be between 0.0 and 1.0.");
process.exit(1);
}
const start = performance.now();
let results: Record<string, unknown>[];
try {
results = await timedStatus("Searching memories...", async () => {
// biome-ignore lint/style/noNonNullAssertion: guarded by process.exit above
return backend.search(query!, {
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
topK: opts.topK,
threshold: opts.threshold,
rerank: opts.rerank,
keyword: opts.keyword,
filters,
fields: fieldList,
enableGraph: opts.enableGraph,
});
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "quiet") return;
if (opts.output === "agent") {
const scope: Record<string, string | undefined> = {
user_id: opts.userId,
agent_id: opts.agentId,
app_id: opts.appId,
run_id: opts.runId,
};
formatAgentEnvelope({
command: "search",
data: results,
scope,
count: results.length,
durationMs: Math.round(elapsed * 1000),
});
return;
}
if (opts.output === "json") {
formatJson(results);
} else if (opts.output === "table") {
if (results.length > 0) {
formatMemoriesTable(results, { showScore: true });
printResultSummary({
count: results.length,
durationSecs: elapsed,
scopeIds: { user_id: opts.userId, agent_id: opts.agentId },
});
} else {
console.log();
printInfo("No memories found matching your query.");
console.log();
}
} else {
if (results.length > 0) {
formatMemoriesText(results);
printResultSummary({
count: results.length,
durationSecs: elapsed,
scopeIds: { user_id: opts.userId, agent_id: opts.agentId },
});
} else {
console.log();
printInfo("No memories found matching your query.");
console.log();
}
}
}
export async function cmdGet(
backend: Backend,
memoryId: string,
opts: { output: string },
): Promise<void> {
setCurrentCommand("get");
let result: Record<string, unknown>;
try {
result = await timedStatus("Fetching memory...", async () => {
return backend.get(memoryId);
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
if (opts.output === "agent") {
formatAgentEnvelope({ command: "get", data: result });
} else {
formatSingleMemory(result, opts.output);
}
}
export async function cmdList(
backend: Backend,
opts: {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
page: number;
pageSize: number;
category?: string;
after?: string;
before?: string;
enableGraph: boolean;
output: string;
},
): Promise<void> {
setCurrentCommand("list");
if (opts.pageSize < 1) {
printError("--page-size must be >= 1.");
process.exit(1);
}
if (opts.page < 1) {
printError("--page must be >= 1.");
process.exit(1);
}
const start = performance.now();
let results: Record<string, unknown>[];
try {
results = await timedStatus("Listing memories...", async () => {
return backend.listMemories({
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
page: opts.page,
pageSize: opts.pageSize,
category: opts.category,
after: opts.after,
before: opts.before,
enableGraph: opts.enableGraph,
});
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "quiet") return;
if (opts.output === "agent" || opts.output === "json") {
const scope: Record<string, string | undefined> = {
user_id: opts.userId,
agent_id: opts.agentId,
app_id: opts.appId,
run_id: opts.runId,
};
formatAgentEnvelope({
command: "list",
data: results,
scope,
count: results.length,
durationMs: Math.round(elapsed * 1000),
});
} else if (opts.output === "table") {
if (results.length > 0) {
formatMemoriesTable(results);
printResultSummary({
count: results.length,
durationSecs: elapsed,
page: opts.page,
scopeIds: { user_id: opts.userId, agent_id: opts.agentId },
});
} else {
console.log();
printInfo("No memories found.");
console.log();
}
} else {
if (results.length > 0) {
formatMemoriesText(results, "memories");
printResultSummary({
count: results.length,
durationSecs: elapsed,
page: opts.page,
scopeIds: { user_id: opts.userId, agent_id: opts.agentId },
});
} else {
console.log();
printInfo("No memories found.");
console.log();
}
}
}
export async function cmdUpdate(
backend: Backend,
memoryId: string,
text: string | undefined,
opts: { metadata?: string; output: string },
): Promise<void> {
setCurrentCommand("update");
let meta: Record<string, unknown> | undefined;
if (opts.metadata) {
try {
meta = JSON.parse(opts.metadata);
} catch {
printError("Invalid JSON in --metadata.");
process.exit(1);
}
}
const start = performance.now();
let result: Record<string, unknown>;
try {
result = await timedStatus("Updating memory...", async () => {
return backend.update(memoryId, text, meta);
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent") {
formatAgentEnvelope({
command: "update",
data: result,
durationMs: Math.round(elapsed * 1000),
});
} else if (opts.output === "json") {
formatJson(result);
} else if (opts.output !== "quiet") {
printSuccess(
`Memory ${memoryId.slice(0, 8)} updated (${elapsed.toFixed(2)}s)`,
);
}
}
export async function cmdDelete(
backend: Backend,
memoryId: string,
opts: { output: string; dryRun?: boolean; force?: boolean },
): Promise<void> {
setCurrentCommand("delete");
if (opts.dryRun) {
let mem: Record<string, unknown>;
try {
mem = await backend.get(memoryId);
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const text = (mem.memory ?? mem.text ?? "") as string;
printInfo(`Would delete memory ${memoryId.slice(0, 8)}: ${text}`);
printInfo("No changes made.");
return;
}
const start = performance.now();
let result: Record<string, unknown>;
try {
result = await timedStatus("Deleting...", async () => {
return backend.delete(memoryId);
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent") {
formatAgentEnvelope({
command: "delete",
data: { id: memoryId, deleted: true },
durationMs: Math.round(elapsed * 1000),
});
} else if (opts.output === "json") {
formatJson(result);
} else if (opts.output !== "quiet") {
printSuccess(
`Memory ${memoryId.slice(0, 8)} deleted (${elapsed.toFixed(2)}s)`,
);
}
}
export async function cmdDeleteAll(
backend: Backend,
opts: {
force: boolean;
dryRun?: boolean;
all?: boolean;
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
output: string;
},
): Promise<void> {
setCurrentCommand("delete-all");
const { isAgentMode } = await import("../state.js");
if (isAgentMode() && !opts.force) {
printError("Destructive operation requires --force in agent mode.");
process.exit(1);
}
if (opts.all) {
// Project-wide wipe using wildcard entity IDs
// Note: --dry-run is ignored here because the API has no count-before-delete endpoint.
if (!opts.force) {
const readline = await import("node:readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const answer = await new Promise<string>((resolve) => {
rl.question(
"\n \u26a0 Delete ALL memories across the ENTIRE project? This cannot be undone. [y/N] ",
resolve,
);
});
rl.close();
if (answer.toLowerCase() !== "y") {
printInfo("Cancelled.");
process.exit(0);
}
}
const start = performance.now();
let result: Record<string, unknown>;
try {
result = await timedStatus(
"Deleting all memories project-wide...",
async () => {
return backend.delete(undefined, {
all: true,
userId: "*",
agentId: "*",
appId: "*",
runId: "*",
});
},
);
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent") {
formatAgentEnvelope({
command: "delete-all",
data: result,
durationMs: Math.round(elapsed * 1000),
});
} else if (opts.output === "json") {
formatJson(result);
} else if (opts.output !== "quiet") {
if (result.message) {
printInfo(
"Deletion started. Memories will be removed in the background.",
);
} else {
printSuccess(`All project memories deleted (${elapsed.toFixed(2)}s)`);
}
}
return;
}
if (opts.dryRun) {
let memories: Record<string, unknown>[];
try {
memories = await backend.listMemories({
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
printInfo(`Would delete ${memories.length} memories.`);
printInfo("No changes made.");
return;
}
if (!opts.force) {
const scopeParts: string[] = [];
if (opts.userId) scopeParts.push(`user=${opts.userId}`);
if (opts.agentId) scopeParts.push(`agent=${opts.agentId}`);
if (opts.appId) scopeParts.push(`app=${opts.appId}`);
if (opts.runId) scopeParts.push(`run=${opts.runId}`);
const scope =
scopeParts.length > 0 ? scopeParts.join(", ") : "ALL entities";
const readline = await import("node:readline");
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
const answer = await new Promise<string>((resolve) => {
rl.question(
`\n \u26a0 Delete ALL memories for ${scope}? This cannot be undone. [y/N] `,
resolve,
);
});
rl.close();
if (answer.toLowerCase() !== "y") {
printInfo("Cancelled.");
process.exit(0);
}
}
const start = performance.now();
let result: Record<string, unknown>;
try {
result = await timedStatus("Deleting all memories...", async () => {
return backend.delete(undefined, {
all: true,
userId: opts.userId,
agentId: opts.agentId,
appId: opts.appId,
runId: opts.runId,
});
});
} catch (e) {
printError(e instanceof Error ? e.message : String(e));
process.exit(1);
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent") {
formatAgentEnvelope({
command: "delete-all",
data: result,
durationMs: Math.round(elapsed * 1000),
});
} else if (opts.output === "json") {
formatJson(result);
} else if (opts.output !== "quiet") {
if (result.message) {
printInfo(
"Deletion started. Memories will be removed in the background.",
);
} else {
printSuccess(`All matching memories deleted (${elapsed.toFixed(2)}s)`);
}
}
}
+155
View File
@@ -0,0 +1,155 @@
/**
* Utility commands: status, version, import.
*/
import fs from "node:fs";
import boxen from "boxen";
import type { Backend } from "../backend/base.js";
import { colors, printError, printSuccess, timedStatus } from "../branding.js";
import { formatAgentEnvelope, formatJsonEnvelope } from "../output.js";
import { setCurrentCommand } from "../state.js";
import { CLI_VERSION } from "../version.js";
const { brand, dim, success, error: errorColor } = colors;
export async function cmdStatus(
backend: Backend,
opts: { userId?: string; agentId?: string; output?: string } = {},
): Promise<void> {
setCurrentCommand("status");
const start = performance.now();
let result: Record<string, unknown>;
try {
result = await timedStatus("Checking connection...", async () => {
return backend.status({ userId: opts.userId, agentId: opts.agentId });
});
} catch (e) {
result = {
connected: false,
error: e instanceof Error ? e.message : String(e),
};
}
const elapsed = (performance.now() - start) / 1000;
if (opts.output === "agent" || opts.output === "json") {
formatAgentEnvelope({
command: "status",
data: {
connected: result.connected,
backend: result.backend ?? null,
base_url: result.base_url ?? null,
},
durationMs: Math.round(elapsed * 1000),
});
return;
}
const lines: string[] = [];
if (result.connected) {
lines.push(` ${success("\u25cf")} Connected`);
} else {
lines.push(` ${errorColor("\u25cf")} Disconnected`);
}
lines.push(` ${dim("Backend:")} ${result.backend ?? "?"}`);
if (result.base_url) {
lines.push(` ${dim("API URL:")} ${result.base_url}`);
}
if (result.error) {
lines.push(` ${errorColor("Error:")} ${result.error}`);
if (String(result.error).includes("Authentication failed")) {
lines.push("");
lines.push(
` ${dim("Run")} ${brand("mem0 init")} ${dim("to reconfigure your API key")}`,
);
lines.push(
` ${dim("Get a key at")} ${brand("https://app.mem0.ai/dashboard/api-keys")}`,
);
}
}
lines.push(` ${dim("Latency:")} ${elapsed.toFixed(2)}s`);
const content = lines.join("\n");
console.log();
console.log(
boxen(content, {
title: brand("Connection Status"),
titleAlignment: "left",
borderColor: "magenta",
padding: 1,
}),
);
console.log();
}
export function cmdVersion(): void {
console.log(` ${brand("◆ Mem0")} CLI v${CLI_VERSION}`);
}
export async function cmdImport(
backend: Backend,
filePath: string,
opts: { userId?: string; agentId?: string; output?: string },
): Promise<void> {
setCurrentCommand("import");
let data: Record<string, unknown>[];
try {
const raw = fs.readFileSync(filePath, "utf-8");
const parsed = JSON.parse(raw);
data = Array.isArray(parsed) ? parsed : [parsed];
} catch (e) {
printError(`Failed to read file: ${e instanceof Error ? e.message : e}`);
process.exit(1);
}
let added = 0;
let failed = 0;
const start = performance.now();
for (let i = 0; i < data.length; i++) {
const item = data[i];
const content = (item.memory ?? item.text ?? item.content ?? "") as string;
if (!content) {
failed++;
continue;
}
try {
await backend.add(content, undefined, {
userId: opts.userId ?? (item.user_id as string | undefined),
agentId: opts.agentId ?? (item.agent_id as string | undefined),
metadata: item.metadata as Record<string, unknown> | undefined,
});
added++;
} catch {
failed++;
}
// Simple progress indicator
if ((i + 1) % 10 === 0 || i === data.length - 1) {
process.stdout.write(
`\r ${dim(`Importing memories... ${i + 1}/${data.length}`)}`,
);
}
}
const elapsed = (performance.now() - start) / 1000;
console.log(); // Clear progress line
if (opts.output === "agent" || opts.output === "json") {
formatAgentEnvelope({
command: "import",
data: {
added,
failed,
},
durationMs: Math.round(elapsed * 1000),
});
return;
}
printSuccess(`Imported ${added} memories (${elapsed.toFixed(2)}s)`);
if (failed > 0) {
printError(`${failed} memories failed to import.`);
}
}
+175
View File
@@ -0,0 +1,175 @@
/**
* Configuration management for mem0 CLI.
*
* Config precedence (highest to lowest):
* 1. CLI flags (--api-key, --base-url, etc.)
* 2. Environment variables (MEM0_API_KEY, etc.)
* 3. Config file (~/.mem0/config.json)
* 4. Defaults
*/
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
export const CONFIG_DIR = path.join(os.homedir(), ".mem0");
export const CONFIG_FILE = path.join(CONFIG_DIR, "config.json");
export const DEFAULT_BASE_URL = "https://api.mem0.ai";
export const CONFIG_VERSION = 1;
export interface PlatformConfig {
apiKey: string;
baseUrl: string;
}
export interface DefaultsConfig {
userId: string;
agentId: string;
appId: string;
runId: string;
enableGraph: boolean;
}
export interface Mem0Config {
version: number;
defaults: DefaultsConfig;
platform: PlatformConfig;
}
export function createDefaultConfig(): Mem0Config {
return {
version: CONFIG_VERSION,
defaults: {
userId: "",
agentId: "",
appId: "",
runId: "",
enableGraph: false,
},
platform: {
apiKey: "",
baseUrl: DEFAULT_BASE_URL,
},
};
}
export function ensureConfigDir(): string {
fs.mkdirSync(CONFIG_DIR, { recursive: true, mode: 0o700 });
return CONFIG_DIR;
}
export function loadConfig(): Mem0Config {
const config = createDefaultConfig();
if (fs.existsSync(CONFIG_FILE)) {
const raw = fs.readFileSync(CONFIG_FILE, "utf-8");
const data = JSON.parse(raw);
config.version = data.version ?? CONFIG_VERSION;
const plat = data.platform ?? {};
config.platform.apiKey = plat.api_key ?? "";
config.platform.baseUrl = plat.base_url ?? DEFAULT_BASE_URL;
const defaults = data.defaults ?? {};
config.defaults.userId = defaults.user_id ?? "";
config.defaults.agentId = defaults.agent_id ?? "";
config.defaults.appId = defaults.app_id ?? "";
config.defaults.runId = defaults.run_id ?? "";
config.defaults.enableGraph = defaults.enable_graph ?? false;
}
// Environment variable overrides
if (process.env.MEM0_API_KEY)
config.platform.apiKey = process.env.MEM0_API_KEY;
if (process.env.MEM0_BASE_URL)
config.platform.baseUrl = process.env.MEM0_BASE_URL;
if (process.env.MEM0_USER_ID)
config.defaults.userId = process.env.MEM0_USER_ID;
if (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_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;
}
export function saveConfig(config: Mem0Config): void {
ensureConfigDir();
const data = {
version: config.version,
defaults: {
user_id: config.defaults.userId,
agent_id: config.defaults.agentId,
app_id: config.defaults.appId,
run_id: config.defaults.runId,
enable_graph: config.defaults.enableGraph,
},
platform: {
api_key: config.platform.apiKey,
base_url: config.platform.baseUrl,
},
};
fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
fs.chmodSync(CONFIG_FILE, 0o600);
}
export function redactKey(key: string): string {
if (!key) return "(not set)";
if (key.length <= 8) return `${key.slice(0, 2)}***`;
return `${key.slice(0, 4)}...${key.slice(-4)}`;
}
/** Key map from dotted config path to the config object fields. */
const KEY_MAP: Record<string, [keyof Mem0Config, string]> = {
"platform.api_key": ["platform", "apiKey"],
"platform.base_url": ["platform", "baseUrl"],
"defaults.user_id": ["defaults", "userId"],
"defaults.agent_id": ["defaults", "agentId"],
"defaults.app_id": ["defaults", "appId"],
"defaults.run_id": ["defaults", "runId"],
"defaults.enable_graph": ["defaults", "enableGraph"],
// Short-form aliases
api_key: ["platform", "apiKey"],
base_url: ["platform", "baseUrl"],
user_id: ["defaults", "userId"],
agent_id: ["defaults", "agentId"],
app_id: ["defaults", "appId"],
run_id: ["defaults", "runId"],
enable_graph: ["defaults", "enableGraph"],
};
export function getNestedValue(config: Mem0Config, dottedKey: string): unknown {
const mapping = KEY_MAP[dottedKey];
if (!mapping) return undefined;
const [section, field] = mapping;
return (config[section] as unknown as Record<string, unknown>)[field];
}
export function setNestedValue(
config: Mem0Config,
dottedKey: string,
value: string,
): boolean {
const mapping = KEY_MAP[dottedKey];
if (!mapping) return false;
const [section, field] = mapping;
const obj = config[section] as unknown as Record<string, unknown>;
const current = obj[field];
if (typeof current === "boolean") {
obj[field] = ["true", "1", "yes"].includes(value.toLowerCase());
} else if (typeof current === "number") {
obj[field] = Number.parseInt(value, 10);
} else {
obj[field] = value;
}
return true;
}
+2
View File
@@ -0,0 +1,2 @@
/** Injected by tsup at build time from package.json version field. Undefined in dev/test. */
declare const __CLI_VERSION__: string | undefined;
+378
View File
@@ -0,0 +1,378 @@
/**
* Rich-style help formatter for Commander.js that matches the Python CLI's
* Typer + Rich output (rounded box panels, brand purple, grouped options).
*/
import chalk from "chalk";
import type { Argument, Command, Help, Option } from "commander";
// Colors imported from chalk directly to match Typer/Rich defaults
// ── Colors (matching Typer/Rich defaults) ────────────────────────────────
const cyanBold = chalk.cyan.bold; // option flags, command names
const greenBold = chalk.green.bold; // switch flags (boolean --force etc)
const yellowBold = chalk.yellow.bold; // metavar <value>
const yellow = chalk.yellow; // "Usage:" label
const bold = chalk.bold; // command name in usage
const dim = chalk.dim; // defaults, descriptions
const dimBorder = chalk.dim; // panel borders
// ── Strip ANSI ───────────────────────────────────────────────────────────
// biome-ignore lint/suspicious/noControlCharactersInRegex: ANSI escape sequence is intentional
const ANSI_RE = /\x1b\[[0-9;]*m/g;
function stripAnsi(str: string): number {
return str.replace(ANSI_RE, "").length;
}
// ── Command display order (matches Python CLI) ──────────────────────────
/** Commands grouped into panels, matching Python CLI's rich_help_panel. */
const COMMAND_GROUPS: { panel: string; commands: string[] }[] = [
{
panel: "Memory",
commands: ["add", "search", "get", "list", "update", "delete"],
},
{
panel: "Management",
commands: ["init", "status", "import", "help", "entity", "event", "config"],
},
];
/** Flat order derived from COMMAND_GROUPS. */
const COMMAND_ORDER: string[] = COMMAND_GROUPS.flatMap((g) => g.commands);
// ── Option-to-panel mapping (derived from Python's rich_help_panel) ─────
const OPTION_PANELS: Record<string, Record<string, string>> = {
add: {
"--user-id": "Scope",
"--agent-id": "Scope",
"--app-id": "Scope",
"--run-id": "Scope",
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
search: {
"--user-id": "Scope",
"--agent-id": "Scope",
"--app-id": "Scope",
"--run-id": "Scope",
"--top-k": "Search",
"--threshold": "Search",
"--rerank": "Search",
"--keyword": "Search",
"--filter": "Search",
"--fields": "Search",
"--graph": "Search",
"--no-graph": "Search",
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
get: {
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
list: {
"--user-id": "Scope",
"--agent-id": "Scope",
"--app-id": "Scope",
"--run-id": "Scope",
"--page": "Pagination",
"--page-size": "Pagination",
"--category": "Filters",
"--after": "Filters",
"--before": "Filters",
"--graph": "Filters",
"--no-graph": "Filters",
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
update: {
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
delete: {
"--user-id": "Scope",
"--agent-id": "Scope",
"--app-id": "Scope",
"--run-id": "Scope",
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
status: {
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
import: {
"--user-id": "Scope",
"--agent-id": "Scope",
"--output": "Output",
"--api-key": "Connection",
"--base-url": "Connection",
},
};
const PANEL_ORDER: string[] = [
"Scope",
"Search",
"Pagination",
"Filters",
"Output",
"Connection",
];
// ── Panel rendering ─────────────────────────────────────────────────────
/**
* Render a Rich-style ROUNDED box panel.
*
* ```
* ╭─ Title ────────────────────────╮
* │ row content padded │
* ╰────────────────────────────────╯
* ```
*/
function renderPanel(title: string, rows: string[], width: number): string {
if (rows.length === 0) return "";
// Inner width is total width minus the two border chars
const inner = width - 2;
// Top border: ╭─ Title ─...─╮
const titleStr = ` ${title} `;
const fillLen = Math.max(0, inner - 1 - titleStr.length);
const topLine =
dimBorder("╭─") +
dimBorder(titleStr) +
dimBorder("─".repeat(fillLen)) +
dimBorder("╮");
// Bottom border: ╰─...─╯
const bottomLine =
dimBorder("╰") + dimBorder("─".repeat(inner)) + dimBorder("╯");
// Content rows
const contentLines = rows.map((row) => {
const visLen = stripAnsi(row);
const pad = Math.max(0, inner - 1 - visLen);
return `${dimBorder("│")} ${row}${" ".repeat(pad)}${dimBorder("│")}`;
});
return [topLine, ...contentLines, bottomLine].join("\n");
}
// ── Format an option term (short + long) ────────────────────────────────
function formatOptionTerm(opt: Option): string {
const parts: string[] = [];
if (opt.short) parts.push(opt.short);
if (opt.long) parts.push(opt.long);
let term = parts.join(", ");
// Append value placeholder for non-boolean options
if (opt.flags) {
const match = opt.flags.match(/<[^>]+>|\[[^\]]+\]/);
if (match) {
term += ` ${match[0]}`;
}
}
return term;
}
// ── Get the long flag name for panel lookup ─────────────────────────────
function getLongFlag(opt: Option): string {
if (opt.long) return opt.long;
return opt.short || "";
}
// ── Format a default value ──────────────────────────────────────────────
function formatDefault(opt: Option): string {
if (opt.defaultValue !== undefined && opt.defaultValue !== false) {
return dim(` [default: ${opt.defaultValue}]`);
}
return "";
}
// ── The main help formatter ─────────────────────────────────────────────
export function richFormatHelp(cmd: Command, helper: Help): string {
const width = process.stdout.columns || 80;
const lines: string[] = [];
const isRoot = !cmd.parent;
// ── Usage line ──
const usage = helper.commandUsage(cmd);
lines.push("");
if (isRoot) {
// Root: "Usage: mem0 <command> [options]" — <command> yellow, [options] bold
lines.push(
` ${yellow("Usage:")} ${bold(cmd.name())} ${yellow("<command>")} ${bold("[options]")}`,
);
} else {
// Subcommands: split into command path (bold) and args (yellow)
const usageParts = usage.split(" ");
const cmdPath: string[] = [];
const argParts: string[] = [];
let pastCmd = false;
for (const part of usageParts) {
if (!pastCmd && !part.startsWith("[") && !part.startsWith("<")) {
cmdPath.push(part);
} else {
pastCmd = true;
argParts.push(part);
}
}
lines.push(
` ${yellow("Usage:")} ${bold(cmdPath.join(" "))} ${yellow(argParts.join(" "))}`,
);
}
lines.push("");
// ── Description ──
const desc = helper.commandDescription(cmd);
if (desc) {
// Split multi-line descriptions (e.g., title + tagline)
const descLines = desc.split("\n");
for (let i = 0; i < descLines.length; i++) {
const dLine = descLines[i];
// First line is the title, subsequent non-empty lines are tagline (dimmed)
if (i === 0 || dLine.trim() === "") {
lines.push(` ${dLine}`);
} else {
lines.push(` ${dim(dLine)}`);
}
}
lines.push("");
}
// ── Arguments panel (subcommands only) ──
if (!isRoot) {
const visibleArgs = helper.visibleArguments(cmd);
if (visibleArgs.length > 0) {
const maxLen = Math.max(
...visibleArgs.map((a: Argument) => a.name().length),
);
const argRows = visibleArgs.map((a: Argument) => {
const name = cyanBold(a.name().padEnd(maxLen));
const description = helper.argumentDescription(a);
return ` ${name} ${description}`;
});
const panel = renderPanel("Arguments", argRows, width);
if (panel) lines.push(panel);
}
}
// ── Collect options (grouped into panels for subcommands) ──
const visibleOpts = helper.visibleOptions(cmd);
const cmdName = cmd.name();
const panelMap =
!isRoot && OPTION_PANELS[cmdName] ? OPTION_PANELS[cmdName] : {};
const grouped: Record<string, Option[]> = { Options: [] };
for (const panelName of PANEL_ORDER) {
grouped[panelName] = [];
}
for (const opt of visibleOpts) {
const flag = getLongFlag(opt);
const panel = panelMap[flag];
if (panel && PANEL_ORDER.includes(panel)) {
grouped[panel].push(opt);
} else {
grouped.Options.push(opt);
}
}
// ── Collect commands ──
const visibleCmds = helper.visibleCommands(cmd);
if (isRoot) {
// ROOT: Options first, then command groups (matches Python/Typer ordering)
if (grouped.Options.length > 0) {
const optRows = formatOptionRows(grouped.Options);
const panel = renderPanel("Options", optRows, width);
if (panel) lines.push(panel);
}
if (visibleCmds.length > 0) {
const cmdMap = new Map(visibleCmds.map((c) => [c.name(), c]));
for (const group of COMMAND_GROUPS) {
const groupCmds = group.commands
.map((name) => cmdMap.get(name))
.filter((c): c is Command => c !== undefined);
if (groupCmds.length === 0) continue;
const maxLen = Math.max(...groupCmds.map((c) => c.name().length));
const cmdRows = groupCmds.map((c) => {
const name = cyanBold(c.name().padEnd(maxLen));
const description = helper.subcommandDescription(c);
return ` ${name} ${description}`;
});
const panel = renderPanel(group.panel, cmdRows, width);
if (panel) lines.push(panel);
}
}
} else {
// SUBCOMMANDS: Options/panels first, then sub-subcommands
const panelSequence = ["Options", ...PANEL_ORDER];
for (const panelName of panelSequence) {
const opts = grouped[panelName];
if (opts && opts.length > 0) {
const optRows = formatOptionRows(opts);
const panel = renderPanel(panelName, optRows, width);
if (panel) lines.push(panel);
}
}
// Sub-subcommands (e.g., config show/get/set, entity list/delete)
if (visibleCmds.length > 0) {
const maxLen = Math.max(...visibleCmds.map((c) => c.name().length));
const cmdRows = visibleCmds.map((c) => {
const name = cyanBold(c.name().padEnd(maxLen));
const description = helper.subcommandDescription(c);
return ` ${name} ${description}`;
});
const panel = renderPanel("Commands", cmdRows, width);
if (panel) lines.push(panel);
}
}
lines.push("");
return lines.join("\n");
}
// ── Format option rows with aligned columns ─────────────────────────────
function formatOptionRows(opts: Option[]): string[] {
const terms = opts.map((o) => formatOptionTerm(o));
const maxTermLen = Math.max(...terms.map((t) => t.length));
return opts.map((opt, i) => {
const term = cyanBold(terms[i].padEnd(maxTermLen));
const desc = opt.description || "";
const def = formatDefault(opt);
return ` ${term} ${desc}${def}`;
});
}
// ── Sort commands by COMMAND_ORDER ──────────────────────────────────────
function sortCommands(cmds: Command[]): Command[] {
return [...cmds].sort((a, b) => {
const ai = COMMAND_ORDER.indexOf(a.name());
const bi = COMMAND_ORDER.indexOf(b.name());
// Unknown commands go to end, preserving original order
const aIdx = ai === -1 ? COMMAND_ORDER.length : ai;
const bIdx = bi === -1 ? COMMAND_ORDER.length : bi;
return aIdx - bIdx;
});
}
+711
View File
@@ -0,0 +1,711 @@
#!/usr/bin/env node
/**
* Main CLI application — the entrypoint for `mem0`.
*/
import fs from "node:fs";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { Command } from "commander";
import { type Backend, getBackend } from "./backend/index.js";
import { colors, printError } from "./branding.js";
import type { Mem0Config } from "./config.js";
import { loadConfig } from "./config.js";
import { richFormatHelp } from "./help.js";
import { setAgentMode } from "./state.js";
import { CLI_VERSION } from "./version.js";
const program = new Command();
// ── Helpers ──────────────────────────────────────────────────────────────
function getBackendAndConfig(
apiKey?: string,
baseUrl?: string,
): { backend: Backend; config: Mem0Config } {
const config = loadConfig();
if (apiKey) config.platform.apiKey = apiKey;
if (baseUrl) config.platform.baseUrl = baseUrl;
if (!config.platform.apiKey) {
printError(
"No API key configured.",
"Run 'mem0 init' or set MEM0_API_KEY environment variable.",
);
process.exit(1);
}
return { backend: getBackend(config), config };
}
function getBackendOnly(apiKey?: string, baseUrl?: string): Backend {
return getBackendAndConfig(apiKey, baseUrl).backend;
}
function checkAgentMode(): boolean {
const rootOpts = program.opts();
const isAgent = !!(rootOpts.json || rootOpts.agent);
if (isAgent) setAgentMode(true);
return isAgent;
}
/**
* Resolve entity IDs: CLI flag > config default > undefined.
*
* If any explicit ID is provided, only use explicit IDs (don't mix
* in defaults for other entity types which would over-filter).
* If no explicit IDs, fall back to all configured defaults.
*/
function resolveIds(
config: Mem0Config,
opts: {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
},
): { userId?: string; agentId?: string; appId?: string; runId?: string } {
const hasExplicit = !!(
opts.userId ||
opts.agentId ||
opts.appId ||
opts.runId
);
if (hasExplicit) {
return {
userId: opts.userId || undefined,
agentId: opts.agentId || undefined,
appId: opts.appId || undefined,
runId: opts.runId || undefined,
};
}
return {
userId: config.defaults.userId || undefined,
agentId: config.defaults.agentId || undefined,
appId: config.defaults.appId || undefined,
runId: config.defaults.runId || undefined,
};
}
/**
* 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 ──────────────────────────────────────────────────────────
program
.name("mem0")
.description(
`◆ Mem0 CLI v${CLI_VERSION} · Node.js SDK\n\nThe Memory Layer for AI Agents`,
)
.option("--version", "Show version and exit.")
.on("option:version", () => {
console.log(` ${colors.brand("◆ Mem0")} CLI v${CLI_VERSION}`);
process.exit(0);
})
.option("--json", "Output as JSON for agent/programmatic use.")
.option(
"--agent",
"Output as JSON for agent/programmatic use. (alias: --json)",
)
.usage("<command> [options]")
.helpOption("--help", "Show this message and exit.")
.addHelpCommand(false)
.configureHelp({ formatHelp: richFormatHelp });
// ── Init ──────────────────────────────────────────────────────────────────
program
.command("init")
.description("Interactive setup wizard for mem0 CLI.")
.option("--api-key <key>", "API key (skip prompt).")
.option("-u, --user-id <id>", "Default user ID (skip prompt).")
.option("--email <email>", "Login via email verification code.")
.option(
"--code <code>",
"Verification code (use with --email for non-interactive login).",
)
.option("--force", "Overwrite existing config without confirmation.", false)
.addHelpText(
"after",
"\nExamples:\n $ mem0 init\n $ mem0 init --api-key m0-xxx --user-id alice\n $ mem0 init --email you@example.com\n $ mem0 init --email you@example.com --code 123456",
)
.action(async (opts) => {
const { runInit } = await import("./commands/init.js");
await runInit({
apiKey: opts.apiKey,
userId: opts.userId,
email: opts.email,
code: opts.code,
force: opts.force,
});
});
// ── Memory: add ───────────────────────────────────────────────────────────
program
.command("add [text]")
.description("Add a memory from text, messages, file, or stdin.")
.option("-u, --user-id <id>", "Scope to user.")
.option("--agent-id <id>", "Scope to agent.")
.option("--app-id <id>", "Scope to app.")
.option("--run-id <id>", "Scope to run.")
.option("--messages <json>", "Conversation messages as JSON.")
.option("-f, --file <path>", "Read messages from JSON file.")
.option("-m, --metadata <json>", "Custom metadata as JSON.")
.option("--immutable", "Prevent future updates.", false)
.option("--no-infer", "Skip inference, store raw.")
.option("--expires <date>", "Expiration date (YYYY-MM-DD).")
.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("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
'\nExamples:\n $ mem0 add "I prefer dark mode" --user-id alice\n $ echo "text" | mem0 add -u alice\n $ mem0 add --file msgs.json -u alice -o json',
)
.action(async (text, opts) => {
const { cmdAdd } = await import("./commands/memory.js");
const isAgent = checkAgentMode();
const { backend, config } = getBackendAndConfig(opts.apiKey, opts.baseUrl);
const ids = resolveIds(config, opts);
const enableGraph = resolveGraph(config, opts);
const output = isAgent ? "agent" : opts.output;
await cmdAdd(backend, text, { ...ids, ...opts, enableGraph, output });
});
// ── Memory: search ────────────────────────────────────────────────────────
program
.command("search [query]")
.description(
"Query your memory store — semantic, keyword, or hybrid retrieval.",
)
.option("-u, --user-id <id>", "Filter by user.")
.option("--agent-id <id>", "Filter by agent.")
.option("--app-id <id>", "Filter by app.")
.option("--run-id <id>", "Filter by run.")
.option(
"-k, --top-k <n>",
"Number of results.",
(v) => Number.parseInt(v),
10,
)
.option(
"--threshold <n>",
"Minimum similarity score.",
(v) => Number.parseFloat(v),
0.3,
)
.option("--rerank", "Enable reranking (Platform only).", false)
.option("--keyword", "Use keyword search.", false)
.option("--filter <json>", "Advanced filter expression (JSON).")
.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("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
'\nExamples:\n $ mem0 search "preferences" --user-id alice\n $ mem0 search "tools" -u alice -o json -k 5\n $ echo "preferences" | mem0 search -u alice',
)
.action(async (query, opts) => {
let resolvedQuery = query;
if (!resolvedQuery && !process.stdin.isTTY) {
resolvedQuery = fs.readFileSync(0, "utf-8").trim();
}
if (!resolvedQuery) {
printError("No query provided. Pass a query argument or pipe via stdin.");
process.exit(1);
}
const { cmdSearch } = await import("./commands/memory.js");
const isAgent = checkAgentMode();
const { backend, config } = getBackendAndConfig(opts.apiKey, opts.baseUrl);
const ids = resolveIds(config, opts);
const enableGraph = resolveGraph(config, opts);
const output = isAgent ? "agent" : opts.output;
await cmdSearch(backend, resolvedQuery, {
...ids,
topK: opts.topK,
threshold: opts.threshold,
rerank: opts.rerank,
keyword: opts.keyword,
filterJson: opts.filter,
fields: opts.fields,
enableGraph,
output,
});
});
// ── Memory: get ───────────────────────────────────────────────────────────
program
.command("get <memoryId>")
.description("Get a specific memory by ID.")
.option("-o, --output <format>", "Output: text, json.", "text")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 get abc-123-def-456\n $ mem0 get abc-123-def-456 -o json",
)
.action(async (memoryId, opts) => {
const { cmdGet } = await import("./commands/memory.js");
const isAgent = checkAgentMode();
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
const output = isAgent ? "agent" : opts.output;
await cmdGet(backend, memoryId, { output });
});
// ── Memory: list ──────────────────────────────────────────────────────────
program
.command("list")
.description("List memories with optional filters.")
.option("-u, --user-id <id>", "Filter by user.")
.option("--agent-id <id>", "Filter by agent.")
.option("--app-id <id>", "Filter by app.")
.option("--run-id <id>", "Filter by run.")
.option("--page <n>", "Page number.", (v) => Number.parseInt(v), 1)
.option(
"--page-size <n>",
"Results per page.",
(v) => Number.parseInt(v),
100,
)
.option("--category <name>", "Filter by category.")
.option("--after <date>", "Created after (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("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 list -u alice\n $ mem0 list --category prefs --after 2024-01-01 -o json",
)
.action(async (opts) => {
const { cmdList } = await import("./commands/memory.js");
const isAgent = checkAgentMode();
const { backend, config } = getBackendAndConfig(opts.apiKey, opts.baseUrl);
const ids = resolveIds(config, opts);
const enableGraph = resolveGraph(config, opts);
const output = isAgent ? "agent" : opts.output;
await cmdList(backend, {
...ids,
page: opts.page,
pageSize: opts.pageSize,
category: opts.category,
after: opts.after,
before: opts.before,
enableGraph,
output,
});
});
// ── Memory: update ────────────────────────────────────────────────────────
program
.command("update <memoryId> [text]")
.description("Update a memory's text or metadata.")
.option("-m, --metadata <json>", "Update metadata (JSON).")
.option("-o, --output <format>", "Output: text, json, quiet.", "text")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
`\nExamples:\n $ mem0 update abc-123 "new text"\n $ mem0 update abc-123 --metadata '{"key":"val"}'\n $ echo "new text" | mem0 update abc-123`,
)
.action(async (memoryId, text, opts) => {
let resolvedText = text;
if (!resolvedText && !opts.metadata && !process.stdin.isTTY) {
resolvedText = fs.readFileSync(0, "utf-8").trim();
}
const { cmdUpdate } = await import("./commands/memory.js");
const isAgent = checkAgentMode();
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
const output = isAgent ? "agent" : opts.output;
await cmdUpdate(backend, memoryId, resolvedText, {
metadata: opts.metadata,
output,
});
});
// ── Memory: delete (consolidated) ─────────────────────────────────────────
program
.command("delete [memoryId]")
.description("Delete a memory, all memories matching a scope, or an entity.")
.option("--all", "Delete all memories matching scope filters.", false)
.option(
"--entity",
"Delete the entity itself and all its memories (cascade).",
false,
)
.option("--project", "With --all: delete ALL memories project-wide.", false)
.option("--dry-run", "Show what would be deleted without deleting.", false)
.option("--force", "Skip confirmation.", false)
.option("-u, --user-id <id>", "Scope to user.")
.option("--agent-id <id>", "Scope to agent.")
.option("--app-id <id>", "Scope to app.")
.option("--run-id <id>", "Scope to run.")
.option("-o, --output <format>", "Output: text, json, quiet.", "text")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
[
"\nExamples:",
" $ mem0 delete abc-123-def-456 # single memory",
" $ mem0 delete --all -u alice --force # all memories for user",
" $ mem0 delete --all --project --force # project-wide wipe",
" $ mem0 delete --entity -u alice --force # entity + all its memories",
].join("\n"),
)
.action(async (memoryId, opts) => {
const isAgent = checkAgentMode();
const output = isAgent ? "agent" : opts.output;
// ── Mutual-exclusion checks ──
if (memoryId && opts.all) {
printError("Cannot combine <memoryId> with --all. Use one or the other.");
process.exit(1);
}
if (memoryId && opts.entity) {
printError(
"Cannot combine <memoryId> with --entity. Use one or the other.",
);
process.exit(1);
}
if (opts.all && opts.entity) {
printError("Cannot combine --all with --entity. Use one or the other.");
process.exit(1);
}
if (!memoryId && !opts.all && !opts.entity) {
printError(
"Specify a memory ID, --all, or --entity.\n" +
" mem0 delete <id> Delete a single memory\n" +
" mem0 delete --all [scope] Delete all memories matching scope\n" +
" mem0 delete --entity [scope] Delete an entity and all its memories",
);
process.exit(1);
}
// ── Dispatch: single memory ──
if (memoryId) {
const { cmdDelete } = await import("./commands/memory.js");
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
await cmdDelete(backend, memoryId, {
output,
dryRun: opts.dryRun,
force: opts.force,
});
return;
}
// ── Dispatch: --all ──
if (opts.all) {
const { cmdDeleteAll } = await import("./commands/memory.js");
const { backend, config } = getBackendAndConfig(
opts.apiKey,
opts.baseUrl,
);
const ids = opts.project
? {
userId: undefined,
agentId: undefined,
appId: undefined,
runId: undefined,
}
: resolveIds(config, opts);
await cmdDeleteAll(backend, {
force: opts.force,
dryRun: opts.dryRun,
all: opts.project,
...ids,
output,
});
return;
}
// ── Dispatch: --entity ──
if (opts.entity) {
const { cmdEntitiesDelete } = await import("./commands/entities.js");
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
await cmdEntitiesDelete(backend, { ...opts, output });
return;
}
});
// ── Config subcommands ────────────────────────────────────────────────────
const configCmd = program
.command("config")
.description("Manage mem0 configuration.")
.addHelpCommand(false);
configCmd
.command("show")
.description("Display current configuration (secrets redacted).")
.option("-o, --output <format>", "Output: text, json.", "text")
.addHelpText(
"after",
"\nExamples:\n $ mem0 config show\n $ mem0 config show -o json",
)
.action(async (opts) => {
const { cmdConfigShow } = await import("./commands/config.js");
const isAgent = checkAgentMode();
const output = isAgent ? "agent" : opts.output;
cmdConfigShow({ output });
});
configCmd
.command("get <key>")
.description("Get a configuration value.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 config get platform.api_key\n $ mem0 config get defaults.user_id",
)
.action(async (key) => {
const { cmdConfigGet } = await import("./commands/config.js");
checkAgentMode();
cmdConfigGet(key);
});
configCmd
.command("set <key> <value>")
.description("Set a configuration value.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 config set defaults.user_id alice\n $ mem0 config set platform.base_url https://api.mem0.ai",
)
.action(async (key, value) => {
const { cmdConfigSet } = await import("./commands/config.js");
checkAgentMode();
cmdConfigSet(key, value);
});
// ── Entity subcommand group ───────────────────────────────────────────────
const entityCmd = program
.command("entity")
.description("Manage entities.")
.addHelpCommand(false)
.configureHelp({ formatHelp: richFormatHelp });
entityCmd
.command("list <entityType>")
.description("List all entities of a given type.")
.option("-o, --output <format>", "Output: table, json.", "table")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 entity list users\n $ mem0 entity list agents -o json",
)
.action(async (entityType, opts) => {
const { cmdEntitiesList } = await import("./commands/entities.js");
const isAgent = checkAgentMode();
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
const output = isAgent ? "agent" : opts.output;
await cmdEntitiesList(backend, entityType, { output });
});
entityCmd
.command("delete")
.description("Delete an entity and ALL its memories (cascade).")
.option("--dry-run", "Show what would be deleted without deleting.", false)
.option("-u, --user-id <id>", "Scope to user.")
.option("--agent-id <id>", "Scope to agent.")
.option("--app-id <id>", "Scope to app.")
.option("--run-id <id>", "Scope to run.")
.option("--force", "Skip confirmation.", false)
.option("-o, --output <format>", "Output: text, json, quiet.", "text")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 entity delete --user-id alice --force\n $ mem0 entity delete --user-id alice --dry-run",
)
.action(async (opts) => {
const { cmdEntitiesDelete } = await import("./commands/entities.js");
const isAgent = checkAgentMode();
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
const output = isAgent ? "agent" : opts.output;
await cmdEntitiesDelete(backend, { ...opts, output });
});
// ── Event subcommands ─────────────────────────────────────────────────────
const eventCmd = program
.command("event")
.description("Inspect background processing events.")
.addHelpCommand(false)
.configureHelp({ formatHelp: richFormatHelp });
eventCmd
.command("list")
.description("List recent background processing events.")
.option("-o, --output <format>", "Output: table, json.", "table")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 event list\n $ mem0 event list -o json",
)
.action(async (opts) => {
const { cmdEventList } = await import("./commands/events.js");
const isAgent = checkAgentMode();
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
const output = isAgent ? "agent" : opts.output;
await cmdEventList(backend, { output });
});
eventCmd
.command("status <eventId>")
.description("Check the status of a specific background event.")
.option("-o, --output <format>", "Output: text, json.", "text")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 event status <event-id>\n $ mem0 event status <event-id> -o json",
)
.action(async (eventId, opts) => {
const { cmdEventStatus } = await import("./commands/events.js");
const isAgent = checkAgentMode();
const backend = getBackendOnly(opts.apiKey, opts.baseUrl);
const output = isAgent ? "agent" : opts.output;
await cmdEventStatus(backend, eventId, { output });
});
// ── Utility commands ──────────────────────────────────────────────────────
program
.command("status")
.description("Check connectivity and authentication.")
.option("-o, --output <format>", "Output: text, json.", "text")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText("after", "\nExamples:\n $ mem0 status\n $ mem0 status -o json")
.action(async (opts) => {
const { cmdStatus } = await import("./commands/utils.js");
const isAgent = checkAgentMode();
const { backend, config } = getBackendAndConfig(opts.apiKey, opts.baseUrl);
const output = isAgent ? "agent" : opts.output;
await cmdStatus(backend, {
userId: config.defaults.userId || undefined,
agentId: config.defaults.agentId || undefined,
output,
});
});
program
.command("import <filePath>")
.description("Import memories from a JSON file.")
.option("-u, --user-id <id>", "Override user ID.")
.option("--agent-id <id>", "Override agent ID.")
.option("-o, --output <format>", "Output: text, json.", "text")
.option("--api-key <key>", "Override API key.")
.option("--base-url <url>", "Override API base URL.")
.addHelpText(
"after",
"\nExamples:\n $ mem0 import data.json --user-id alice\n $ mem0 import data.json -u alice -o json",
)
.action(async (filePath, opts) => {
const { cmdImport } = await import("./commands/utils.js");
const isAgent = checkAgentMode();
const { backend, config } = getBackendAndConfig(opts.apiKey, opts.baseUrl);
const ids = resolveIds(config, opts);
const output = isAgent ? "agent" : opts.output;
await cmdImport(backend, filePath, {
userId: ids.userId,
agentId: ids.agentId,
output,
});
});
// ── Help (machine-readable) ──────────────────────────────────────────────
program
.command("help")
.description(
"Show help. Use --json for machine-readable output (for LLM agents).",
)
.option("--json", "Output machine-readable JSON for LLM agents.", false)
.addHelpText("after", "\nExamples:\n $ mem0 help\n $ mem0 help --json")
.action((opts) => {
// opts.json is set when `mem0 help --json` is used (subcommand flag).
// program.opts().json is set when the root --json global flag was used first.
if (opts.json || program.opts().json) {
// Load spec from parent directory
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const specPath = path.join(__dirname, "..", "..", "cli-spec.json");
if (fs.existsSync(specPath)) {
const spec = JSON.parse(fs.readFileSync(specPath, "utf-8"));
console.log(JSON.stringify(spec, null, 2));
} else {
console.log(
JSON.stringify(
{
name: "mem0",
version: CLI_VERSION,
description: "The Memory Layer for AI Agents",
},
null,
2,
),
);
}
} else {
const { brand: b } = colors;
console.log(
`${b("◆ Mem0 CLI")} v${CLI_VERSION} · Node.js SDK\n The Memory Layer for AI Agents\n`,
);
console.log("Usage: mem0 <command> [OPTIONS]\n");
console.log("Commands:");
console.log(
" add Add a memory from text, messages, file, or stdin",
);
console.log(
" search Query your memory store (semantic, keyword, hybrid)",
);
console.log(" get Get a specific memory by ID");
console.log(" list List memories with optional filters");
console.log(" update Update a memory's text or metadata");
console.log(
" delete Delete a memory, all memories, or an entity",
);
console.log(" import Import memories from a JSON file");
console.log(" config Manage configuration (show, get, set)");
console.log(" entity Manage entities (list, delete)");
console.log(
" event Inspect background events (list, status)",
);
console.log(" init Interactive setup wizard");
console.log(" status Check connectivity and authentication");
console.log();
console.log(" mem0 <command> --help Get help for a command");
console.log(
" mem0 help --json Machine-readable help (for LLM agents)",
);
console.log();
}
});
// ── Entrypoint ────────────────────────────────────────────────────────────
program.parse();
+381
View File
@@ -0,0 +1,381 @@
/**
* Output formatting for mem0 CLI — text, JSON, table, quiet modes.
*/
import boxen from "boxen";
import Table from "cli-table3";
import { colors, sym } from "./branding.js";
const { brand, accent, success, error: errorColor, dim } = colors;
function formatDate(dtStr?: string): string | undefined {
if (!dtStr) return undefined;
try {
const dt = new Date(dtStr.replace("Z", "+00:00"));
return dt.toISOString().slice(0, 10);
} catch {
return dtStr?.slice(0, 10);
}
}
export function formatMemoriesText(
memories: Record<string, unknown>[],
title = "memories",
): void {
const count = memories.length;
console.log(`\n${brand(`Found ${count} ${title}:`)}\n`);
for (let i = 0; i < memories.length; i++) {
const mem = memories[i];
const memoryText = (mem.memory ?? mem.text ?? "") as string;
const memId = ((mem.id as string) ?? "").slice(0, 8);
const score = mem.score as number | undefined;
const created = formatDate(mem.created_at as string | undefined);
let category: string | undefined;
const cats = mem.categories;
if (Array.isArray(cats)) {
category = cats[0] as string | undefined;
}
console.log(` ${i + 1}. ${memoryText}`);
const details: string[] = [];
if (score !== undefined) details.push(`Score: ${score.toFixed(2)}`);
if (memId) details.push(`ID: ${memId}`);
if (created) details.push(`Created: ${created}`);
if (category) details.push(`Category: ${category}`);
if (details.length > 0) {
console.log(` ${dim(details.join(" · "))}`);
}
console.log();
}
}
export function formatMemoriesTable(
memories: Record<string, unknown>[],
opts: { showScore?: boolean } = {},
): void {
const head = opts.showScore
? [
accent("ID"),
accent("Score"),
accent("Memory"),
accent("Category"),
accent("Created"),
]
: [accent("ID"), accent("Memory"), accent("Category"), accent("Created")];
const colWidths = opts.showScore ? [38, 8, 40, 16, 14] : [38, 40, 16, 14];
const table = new Table({
head,
colWidths,
wordWrap: true,
style: { head: [], border: [] },
});
for (const mem of memories) {
const memId = (mem.id as string) ?? "";
let memoryText = (mem.memory ?? mem.text ?? "") as string;
if (memoryText.length > 60) {
memoryText = `${memoryText.slice(0, 57)}...`;
}
const categories = mem.categories;
const cat =
Array.isArray(categories) && categories.length > 0
? categories.length > 1
? `${categories[0]} (+${categories.length - 1})`
: (categories[0] as string)
: "—";
const created = formatDate(mem.created_at as string | undefined) ?? "—";
if (opts.showScore) {
const score = mem.score as number | undefined;
const scoreStr = score !== undefined ? score.toFixed(2) : "—";
table.push([dim(memId), scoreStr, memoryText, cat, created]);
} else {
table.push([dim(memId), memoryText, cat, created]);
}
}
console.log();
console.log(table.toString());
console.log();
}
export function formatJson(data: unknown): void {
console.log(JSON.stringify(data, null, 2));
}
export function formatSingleMemory(
mem: Record<string, unknown>,
output = "text",
): void {
if (output === "json") {
formatJson(mem);
return;
}
const memoryText = (mem.memory ?? mem.text ?? "") as string;
const memId = (mem.id ?? "") as string;
const lines: string[] = [];
lines.push(` ${memoryText}`);
lines.push("");
if (memId) lines.push(` ${dim("ID:")} ${memId}`);
const created = formatDate(mem.created_at as string | undefined);
if (created) lines.push(` ${dim("Created:")} ${created}`);
const updated = formatDate(mem.updated_at as string | undefined);
if (updated) lines.push(` ${dim("Updated:")} ${updated}`);
const meta = mem.metadata;
if (meta) lines.push(` ${dim("Metadata:")} ${JSON.stringify(meta)}`);
const categories = mem.categories;
if (categories) {
const catStr = Array.isArray(categories)
? categories.join(", ")
: String(categories);
lines.push(` ${dim("Categories:")} ${catStr}`);
}
const content = lines.join("\n");
console.log();
console.log(
boxen(content, {
title: brand("Memory"),
titleAlignment: "left",
borderColor: "magenta",
padding: 1,
}),
);
console.log();
}
export function formatAddResult(
result: Record<string, unknown> | Record<string, unknown>[],
output = "text",
): void {
if (output === "json") {
formatJson(result);
return;
}
if (output === "quiet") return;
const results: Record<string, unknown>[] = Array.isArray(result)
? result
: ((result.results as Record<string, unknown>[]) ?? [result]);
if (!results.length) {
console.log(` ${dim("No memories extracted.")}`);
return;
}
console.log();
const seenPendingEvents = new Set<string>();
for (const r of results) {
// Detect async PENDING response
if (r.status === "PENDING") {
const eventId = (r.event_id as string) ?? "";
// Deduplicate PENDING entries with the same event_id
if (eventId && seenPendingEvents.has(eventId)) continue;
if (eventId) seenPendingEvents.add(eventId);
const icon = accent(sym("⧗", "..."));
const parts = [
` ${icon} ${dim("Queued".padEnd(10))}`,
"Processing in background",
];
console.log(parts.join(" "));
if (eventId) {
console.log(` ${dim(` event_id: ${eventId}`)}`);
console.log(
` ${dim(` → Check status: mem0 event status ${eventId}`)}`,
);
}
continue;
}
const event = (r.event ?? "ADD") as string;
const memory = (r.memory ?? r.text ?? r.content ?? r.data ?? "") as string;
const memId = ((r.id as string) ?? (r.memory_id as string) ?? "").slice(
0,
8,
);
let icon: string;
let label: string;
if (event === "ADD") {
icon = success("+");
label = "Added";
} else if (event === "UPDATE") {
icon = accent("~");
label = "Updated";
} else if (event === "DELETE") {
icon = errorColor("-");
label = "Deleted";
} else if (event === "NOOP") {
icon = dim("·");
label = "No change";
} else {
icon = dim("?");
label = event;
}
const parts = [` ${icon} ${dim(label.padEnd(10))}`];
if (memory) parts.push(memory);
if (memId) parts.push(dim(`(${memId})`));
console.log(parts.join(" "));
}
console.log();
}
export function formatJsonEnvelope(opts: {
command: string;
data: unknown;
durationMs?: number;
scope?: Record<string, string | undefined>;
count?: number;
status?: string;
error?: string;
}): void {
const envelope: Record<string, unknown> = {
status: opts.status ?? "success",
command: opts.command,
};
if (opts.durationMs !== undefined) envelope.duration_ms = opts.durationMs;
if (opts.scope !== undefined) envelope.scope = opts.scope;
if (opts.count !== undefined) envelope.count = opts.count;
if (opts.error) envelope.error = opts.error;
envelope.data = opts.data;
console.log(JSON.stringify(envelope, null, 2));
}
function pick(
obj: Record<string, unknown>,
keys: string[],
): Record<string, unknown> {
const result: Record<string, unknown> = {};
for (const key of keys) {
if (key in obj) result[key] = obj[key];
}
return result;
}
export function sanitizeAgentData(command: string, data: unknown): unknown {
if (data === null || data === undefined) return data;
switch (command) {
case "add": {
const items = Array.isArray(data) ? data : [data];
return items.map((item) => {
const r = item as Record<string, unknown>;
if (r.status === "PENDING") return pick(r, ["status", "event_id"]);
return pick(r, ["id", "memory", "event"]);
});
}
case "search":
return (data as Record<string, unknown>[]).map((r) =>
pick(r, ["id", "memory", "score", "created_at", "categories"]),
);
case "list":
return (data as Record<string, unknown>[]).map((r) =>
pick(r, ["id", "memory", "created_at", "categories"]),
);
case "get": {
const r = data as Record<string, unknown>;
return pick(r, [
"id",
"memory",
"created_at",
"updated_at",
"categories",
"metadata",
]);
}
case "update": {
const r = data as Record<string, unknown>;
return pick(r, ["id", "memory"]);
}
case "delete":
case "delete-all":
case "entity delete":
return data;
case "entity list":
return (data as Record<string, unknown>[]).map((r) => ({
name: (r.name ?? r.id) as string,
...pick(r, ["type", "count"]),
}));
case "event list":
return (data as Record<string, unknown>[]).map((r) =>
pick(r, ["id", "event_type", "status", "latency", "created_at"]),
);
case "event status": {
const ev = data as Record<string, unknown>;
const rawResults =
(ev.results as Record<string, unknown>[] | undefined) ?? [];
const sanitizedResults = rawResults.map((r) => {
const nested = r.data as Record<string, unknown> | undefined;
return {
id: r.id,
event: r.event,
user_id: r.user_id,
memory: nested?.memory ?? null,
};
});
return {
...pick(ev, [
"id",
"event_type",
"status",
"latency",
"created_at",
"updated_at",
]),
results: sanitizedResults,
};
}
default:
return data;
}
}
export function formatAgentEnvelope(opts: {
command: string;
data: unknown;
durationMs?: number;
scope?: Record<string, string | undefined>;
count?: number;
}): void {
const envelope: Record<string, unknown> = {
status: "success",
command: opts.command,
};
if (opts.durationMs !== undefined) envelope.duration_ms = opts.durationMs;
if (opts.scope) {
const filtered = Object.fromEntries(
Object.entries(opts.scope).filter(([, v]) => v),
);
if (Object.keys(filtered).length > 0) envelope.scope = filtered;
}
if (opts.count !== undefined) envelope.count = opts.count;
envelope.data = sanitizeAgentData(opts.command, opts.data);
console.log(JSON.stringify(envelope, null, 2));
}
export function printResultSummary(opts: {
count: number;
durationSecs?: number;
page?: number;
scopeIds?: Record<string, string | undefined>;
}): void {
const parts = [`${opts.count} result${opts.count !== 1 ? "s" : ""}`];
if (opts.page !== undefined) parts.push(`page ${opts.page}`);
if (opts.scopeIds) {
const scopeParts = Object.entries(opts.scopeIds)
.filter(([, v]) => v)
.map(([k, v]) => `${k}=${v}`);
if (scopeParts.length > 0) parts.push(scopeParts.join(", "));
}
if (opts.durationSecs !== undefined)
parts.push(`${opts.durationSecs.toFixed(2)}s`);
console.log(` ${dim(parts.join(" · "))}`);
console.log();
}
+23
View File
@@ -0,0 +1,23 @@
/**
* Agent mode state — set by the root program option handler,
* read by commands and branding functions.
*/
let _agentMode = false;
let _currentCommand = "";
export function isAgentMode(): boolean {
return _agentMode;
}
export function setAgentMode(val: boolean): void {
_agentMode = val;
}
export function getCurrentCommand(): string {
return _currentCommand;
}
export function setCurrentCommand(name: string): void {
_currentCommand = name;
}
+10
View File
@@ -0,0 +1,10 @@
import { createRequire } from "node:module";
// __CLI_VERSION__ is replaced at build time by tsup (see tsup.config.ts).
// When running via tsx in dev/test mode, fall back to reading package.json.
// typeof is safe to use on undeclared identifiers — it returns 'undefined' without throwing.
export const CLI_VERSION: string =
typeof __CLI_VERSION__ !== "undefined"
? (__CLI_VERSION__ as string)
: (createRequire(import.meta.url)("../package.json") as { version: string })
.version;
+98
View File
@@ -0,0 +1,98 @@
/**
* Tests for branding utilities.
*/
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import {
BRAND_COLOR,
SUCCESS_COLOR,
ERROR_COLOR,
TAGLINE,
LOGO_MINI,
printSuccess,
printError,
printWarning,
printInfo,
printScope,
} from "../src/branding.js";
let output: string;
let errOutput: string;
const originalLog = console.log;
const originalError = console.error;
beforeEach(() => {
output = "";
errOutput = "";
console.log = (...args: unknown[]) => {
output += args.map(String).join(" ") + "\n";
};
console.error = (...args: unknown[]) => {
errOutput += args.map(String).join(" ") + "\n";
};
});
afterEach(() => {
console.log = originalLog;
console.error = originalError;
});
describe("branding constants", () => {
it("has correct brand color", () => {
expect(BRAND_COLOR).toBe("#F1C96C");
});
it("has correct tagline", () => {
expect(TAGLINE).toBe("The Memory Layer for AI Agents");
});
it("has correct logo mini", () => {
expect(LOGO_MINI).toBe("◆ mem0");
});
});
describe("printSuccess", () => {
it("prints success message", () => {
printSuccess("Operation completed");
expect(output).toContain("Operation completed");
});
});
describe("printError", () => {
it("prints error message to stderr", () => {
printError("Something failed");
expect(errOutput).toContain("Something failed");
});
it("prints hint when provided to stderr", () => {
printError("Failed", "Try again");
expect(errOutput).toContain("Try again");
});
});
describe("printWarning", () => {
it("prints warning message to stderr", () => {
printWarning("Be careful");
expect(errOutput).toContain("Be careful");
});
});
describe("printInfo", () => {
it("prints info message", () => {
printInfo("Important note");
expect(errOutput).toContain("Important note");
});
});
describe("printScope", () => {
it("prints scope when IDs present", () => {
printScope({ user_id: "alice", agent_id: "bot" });
expect(errOutput).toContain("alice");
expect(errOutput).toContain("bot");
});
it("prints nothing when no IDs", () => {
printScope({});
expect(errOutput).toBe("");
});
});
+149
View File
@@ -0,0 +1,149 @@
/**
* Integration tests — invoke CLI as subprocess to test end-to-end.
*/
import { describe, it, expect } from "vitest";
import { execSync } from "node:child_process";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
function run(
args: string[],
opts: { home?: string; env?: Record<string, string> } = {},
): { stdout: string; stderr: string; exitCode: number } {
const env = { ...process.env };
// Strip MEM0_ env vars
for (const key of Object.keys(env)) {
if (key.startsWith("MEM0_")) delete env[key];
}
if (opts.home) env.HOME = opts.home;
if (opts.env) Object.assign(env, opts.env);
try {
const stdout = execSync(
`npx tsx src/index.ts ${args.join(" ")}`,
{ cwd: path.join(__dirname, ".."), env, encoding: "utf-8", timeout: 15000 },
);
return { stdout, stderr: "", exitCode: 0 };
} catch (e: any) {
return {
stdout: e.stdout ?? "",
stderr: e.stderr ?? "",
exitCode: e.status ?? 1,
};
}
}
describe("CLI Integration — help and version", () => {
it("shows help with --help", () => {
const result = run(["--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("mem0");
expect(result.stdout).toContain("add");
expect(result.stdout).toContain("search");
});
it("help --json produces valid JSON", () => {
const result = run(["help", "--json"]);
expect(result.exitCode).toBe(0);
const parsed = JSON.parse(result.stdout);
// spec may have cli.name or top-level name
const name = parsed.name ?? parsed.cli?.name;
expect(name).toBe("mem0");
});
it("shows add help", () => {
const result = run(["add", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("user-id");
expect(result.stdout).toContain("messages");
});
it("shows search help", () => {
const result = run(["search", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("top-k");
});
it("shows list help", () => {
const result = run(["list", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("page-size");
});
it("shows delete help with --all, --entity, --project", () => {
const result = run(["delete", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--all");
expect(result.stdout).toContain("--entity");
expect(result.stdout).toContain("--project");
expect(result.stdout).toContain("--force");
expect(result.stdout.toLowerCase()).toContain("memory");
});
it("delete with no args errors", () => {
const result = run(["delete"]);
expect(result.exitCode).not.toBe(0);
const combined = result.stdout + result.stderr;
expect(combined).toContain("--all");
});
it("shows entity list help", () => {
const result = run(["entity", "list", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout.toLowerCase()).toContain("entitytype");
});
it("shows entity delete help", () => {
const result = run(["entity", "delete", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--user-id");
expect(result.stdout).toContain("--force");
});
it("shows import help", () => {
const result = run(["import", "--help"]);
expect(result.exitCode).toBe(0);
});
it("add help has --graph flag", () => {
const result = run(["add", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--graph");
});
it("search help has --graph flag", () => {
const result = run(["search", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--graph");
});
it("list help has --graph flag", () => {
const result = run(["list", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--graph");
});
});
describe("CLI Integration — isolated (clean home)", () => {
function cleanHome(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-test-"));
}
it("add without API key errors", () => {
const home = cleanHome();
const result = run(["add", "test", "--user-id", "alice"], { home });
expect(result.exitCode).not.toBe(0);
const combined = result.stdout + result.stderr;
expect(combined.toLowerCase()).toMatch(/api.key|error/i);
fs.rmSync(home, { recursive: true, force: true });
});
it("config show works with clean home", () => {
const home = cleanHome();
const result = run(["config", "show"], { home });
expect(result.exitCode).toBe(0);
fs.rmSync(home, { recursive: true, force: true });
});
});
+434
View File
@@ -0,0 +1,434 @@
/**
* Tests for CLI commands using mock backend.
*/
import { describe, it, expect, vi, beforeEach } from "vitest";
import { createMockBackend } from "./setup.js";
import type { Backend } from "../src/backend/base.js";
import { setAgentMode } from "../src/state.js";
let mockBackend: Backend;
// Capture console.log and console.error output
let output: string;
let errOutput: string;
const originalLog = console.log;
const originalError = console.error;
beforeEach(() => {
mockBackend = createMockBackend();
output = "";
errOutput = "";
console.log = (...args: unknown[]) => {
output += args.map(String).join(" ") + "\n";
};
console.error = (...args: unknown[]) => {
errOutput += args.map(String).join(" ") + "\n";
};
});
// Restore after each test
import { afterEach } from "vitest";
afterEach(() => {
console.log = originalLog;
console.error = originalError;
setAgentMode(false);
});
describe("cmdAdd", () => {
it("adds text memory", async () => {
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, "I prefer dark mode", {
userId: "alice",
immutable: false,
noInfer: false,
enableGraph: false,
output: "text",
});
expect(mockBackend.add).toHaveBeenCalledOnce();
});
it("adds from messages JSON", async () => {
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, undefined, {
userId: "alice",
messages: JSON.stringify([{ role: "user", content: "I love Python" }]),
immutable: false,
noInfer: false,
enableGraph: false,
output: "text",
});
expect(mockBackend.add).toHaveBeenCalledOnce();
});
it("outputs json format", async () => {
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, "test", {
userId: "alice",
immutable: false,
noInfer: false,
enableGraph: false,
output: "json",
});
expect(output).toContain("results");
});
it("quiet mode produces no memory content", async () => {
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, "test", {
userId: "alice",
immutable: false,
noInfer: false,
enableGraph: false,
output: "quiet",
});
expect(output).not.toContain("dark mode");
});
});
describe("cmdAdd deduplicates PENDING", () => {
const DUPLICATE_PENDING = {
results: [
{ status: "PENDING", event_id: "evt-dup" },
{ status: "PENDING", event_id: "evt-dup" },
],
};
it("text shows one pending block", async () => {
(mockBackend.add as ReturnType<typeof vi.fn>).mockResolvedValue(DUPLICATE_PENDING);
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, "test", {
userId: "alice",
immutable: false,
noInfer: false,
enableGraph: false,
output: "text",
});
expect(output.match(/Queued/g)?.length).toBe(1);
});
it("json shows one pending entry", async () => {
(mockBackend.add as ReturnType<typeof vi.fn>).mockResolvedValue(DUPLICATE_PENDING);
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, "test", {
userId: "alice",
immutable: false,
noInfer: false,
enableGraph: false,
output: "json",
});
const data = JSON.parse(output);
const pending = data.results.filter((r: Record<string, unknown>) => r.status === "PENDING");
expect(pending).toHaveLength(1);
});
it("agent shows one pending entry", async () => {
(mockBackend.add as ReturnType<typeof vi.fn>).mockResolvedValue(DUPLICATE_PENDING);
setAgentMode(true);
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, "test", {
userId: "alice",
immutable: false,
noInfer: false,
enableGraph: false,
output: "agent",
});
const data = JSON.parse(output);
expect(data.count).toBe(1);
expect(data.data).toHaveLength(1);
});
});
describe("cmdSearch", () => {
it("searches and shows results in text mode", async () => {
const { cmdSearch } = await import("../src/commands/memory.js");
await cmdSearch(mockBackend, "preferences", {
userId: "alice",
topK: 10,
threshold: 0.3,
rerank: false,
keyword: false,
enableGraph: false,
output: "text",
});
expect(output).toContain("Found 2");
});
it("outputs json format", async () => {
const { cmdSearch } = await import("../src/commands/memory.js");
await cmdSearch(mockBackend, "preferences", {
userId: "alice",
topK: 10,
threshold: 0.3,
rerank: false,
keyword: false,
enableGraph: false,
output: "json",
});
expect(output).toContain("memory");
});
it("shows no results message", async () => {
(mockBackend.search as ReturnType<typeof vi.fn>).mockResolvedValue([]);
const { cmdSearch } = await import("../src/commands/memory.js");
await cmdSearch(mockBackend, "nonexistent", {
userId: "alice",
topK: 10,
threshold: 0.3,
rerank: false,
keyword: false,
enableGraph: false,
output: "text",
});
expect(errOutput).toContain("No memories found");
});
});
describe("cmdGet", () => {
it("gets memory in text mode", async () => {
const { cmdGet } = await import("../src/commands/memory.js");
await cmdGet(mockBackend, "abc-123-def-456", { output: "text" });
expect(output).toContain("dark mode");
});
it("gets memory in json mode", async () => {
const { cmdGet } = await import("../src/commands/memory.js");
await cmdGet(mockBackend, "abc-123-def-456", { output: "json" });
expect(output).toContain("memory");
});
});
describe("cmdList", () => {
it("lists in table mode", async () => {
const { cmdList } = await import("../src/commands/memory.js");
await cmdList(mockBackend, {
userId: "alice",
page: 1,
pageSize: 100,
enableGraph: false,
output: "table",
});
expect(output).toContain("dark mode");
});
it("shows empty message", async () => {
(mockBackend.listMemories as ReturnType<typeof vi.fn>).mockResolvedValue([]);
const { cmdList } = await import("../src/commands/memory.js");
await cmdList(mockBackend, {
userId: "alice",
page: 1,
pageSize: 100,
enableGraph: false,
output: "text",
});
expect(errOutput).toContain("No memories found");
});
});
describe("cmdUpdate", () => {
it("updates memory", async () => {
const { cmdUpdate } = await import("../src/commands/memory.js");
await cmdUpdate(mockBackend, "abc-123", "New text", { output: "text" });
expect(output.toLowerCase()).toContain("updated");
});
});
describe("cmdDelete", () => {
it("deletes memory", async () => {
const { cmdDelete } = await import("../src/commands/memory.js");
await cmdDelete(mockBackend, "abc-123", { output: "text" });
expect(output.toLowerCase()).toContain("deleted");
});
});
describe("cmdDeleteAll", () => {
it("deletes all with force", async () => {
const { cmdDeleteAll } = await import("../src/commands/memory.js");
await cmdDeleteAll(mockBackend, {
force: true,
userId: "alice",
output: "text",
});
expect(output.toLowerCase()).toContain("deleted");
});
});
describe("cmdEntitiesList", () => {
it("lists users in table mode", async () => {
const { cmdEntitiesList } = await import("../src/commands/entities.js");
await cmdEntitiesList(mockBackend, "users", { output: "table" });
expect(output).toContain("alice");
});
it("lists in json mode", async () => {
const { cmdEntitiesList } = await import("../src/commands/entities.js");
await cmdEntitiesList(mockBackend, "users", { output: "json" });
expect(output).toContain("alice");
});
});
describe("cmdEventList", () => {
it("lists events in table mode", async () => {
const { cmdEventList } = await import("../src/commands/events.js");
await cmdEventList(mockBackend, { output: "table" });
expect(output).toContain("evt-abc-");
expect(output).toContain("ADD");
expect(output).toContain("SUCCEEDED");
});
it("lists events in json mode", async () => {
const { cmdEventList } = await import("../src/commands/events.js");
await cmdEventList(mockBackend, { output: "json" });
expect(output).toContain("evt-abc-123-def-456");
expect(output).toContain("evt-def-456-ghi-789");
});
it("shows empty message when no events", async () => {
(mockBackend.listEvents as ReturnType<typeof vi.fn>).mockResolvedValueOnce([]);
const { cmdEventList } = await import("../src/commands/events.js");
await cmdEventList(mockBackend, { output: "table" });
expect((output + errOutput).toLowerCase()).toContain("no events");
});
});
describe("cmdEventStatus", () => {
it("shows event details in text mode", async () => {
const { cmdEventStatus } = await import("../src/commands/events.js");
await cmdEventStatus(mockBackend, "evt-abc-123-def-456", { output: "text" });
expect(output).toContain("evt-abc-123-def-456");
expect(output).toContain("SUCCEEDED");
});
it("shows event details in json mode", async () => {
const { cmdEventStatus } = await import("../src/commands/events.js");
await cmdEventStatus(mockBackend, "evt-abc-123-def-456", { output: "json" });
expect(output).toContain("evt-abc-123-def-456");
expect(output).toContain("ADD");
});
});
describe("agent mode", () => {
it("cmdAdd outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdAdd } = await import("../src/commands/memory.js");
await cmdAdd(mockBackend, "test preference", {
userId: "alice",
immutable: false,
noInfer: false,
enableGraph: false,
output: "agent",
});
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("add");
expect(parsed.data).toBeDefined();
expect(parsed.scope).toMatchObject({ user_id: "alice" });
expect(Object.keys(parsed.data[0]).sort()).toEqual(["event", "id", "memory"].sort());
});
it("cmdSearch outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdSearch } = await import("../src/commands/memory.js");
await cmdSearch(mockBackend, "preferences", {
userId: "alice",
topK: 10,
threshold: 0.3,
rerank: false,
keyword: false,
enableGraph: false,
output: "agent",
});
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("search");
expect(Array.isArray(parsed.data)).toBe(true);
expect(parsed.count).toBe(2);
const keys = Object.keys(parsed.data[0]);
expect(keys).toContain("id");
expect(keys).toContain("memory");
expect(keys).toContain("score");
expect(keys).toContain("created_at");
expect(keys).toContain("categories");
expect(keys).not.toContain("user_id");
expect(keys).not.toContain("agent_id");
});
it("cmdList outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdList } = await import("../src/commands/memory.js");
await cmdList(mockBackend, {
userId: "alice",
page: 1,
pageSize: 100,
enableGraph: false,
output: "agent",
});
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("list");
expect(Array.isArray(parsed.data)).toBe(true);
expect(parsed.count).toBe(2);
expect(Object.keys(parsed.data[0]).sort()).toEqual(["categories", "created_at", "id", "memory"]);
});
it("cmdGet outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdGet } = await import("../src/commands/memory.js");
await cmdGet(mockBackend, "abc-123-def-456", { output: "agent" });
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("get");
expect(parsed.data).toBeDefined();
expect(parsed.data).toMatchObject({ id: "abc-123-def-456" });
expect(Object.keys(parsed.data)).not.toContain("user_id");
});
it("cmdUpdate outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdUpdate } = await import("../src/commands/memory.js");
await cmdUpdate(mockBackend, "abc-123", "Updated text", { output: "agent" });
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("update");
expect(parsed.data).toBeDefined();
});
it("cmdDelete outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdDelete } = await import("../src/commands/memory.js");
await cmdDelete(mockBackend, "abc-123", { output: "agent" });
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("delete");
expect(parsed.data).toBeDefined();
});
it("cmdEventList outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdEventList } = await import("../src/commands/events.js");
await cmdEventList(mockBackend, { output: "agent" });
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("event list");
expect(Array.isArray(parsed.data)).toBe(true);
expect(parsed.count).toBe(2);
expect(Object.keys(parsed.data[0]).sort()).toEqual(
["created_at", "event_type", "id", "latency", "status"],
);
expect(Object.keys(parsed.data[0])).not.toContain("updated_at");
});
it("cmdEventStatus outputs JSON envelope", async () => {
setAgentMode(true);
const { cmdEventStatus } = await import("../src/commands/events.js");
await cmdEventStatus(mockBackend, "evt-abc-123-def-456", { output: "agent" });
const parsed = JSON.parse(output.trim());
expect(parsed.status).toBe("success");
expect(parsed.command).toBe("event status");
expect(parsed.data).toBeDefined();
expect(parsed.data).toMatchObject({ id: "evt-abc-123-def-456" });
expect(parsed.data.results[0]).toHaveProperty("memory");
expect(parsed.data.results[0]).not.toHaveProperty("data");
});
});
+113
View File
@@ -0,0 +1,113 @@
/**
* Tests for configuration management.
*/
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import {
createDefaultConfig,
loadConfig,
saveConfig,
redactKey,
getNestedValue,
setNestedValue,
CONFIG_DIR,
CONFIG_FILE,
} from "../src/config.js";
// Use a temp directory for config during tests
let origConfigDir: string;
let origConfigFile: string;
let tmpDir: string;
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mem0-test-"));
// Monkey-patch the module-level constants
// We'll use env vars and direct file manipulation instead
// Clear MEM0_ env vars
for (const key of Object.keys(process.env)) {
if (key.startsWith("MEM0_")) {
delete process.env[key];
}
}
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
describe("redactKey", () => {
it("returns '(not set)' for empty key", () => {
expect(redactKey("")).toBe("(not set)");
});
it("redacts short key", () => {
expect(redactKey("abc")).toBe("ab***");
});
it("redacts normal key", () => {
const result = redactKey("m0-abcdefgh12345678");
expect(result).toBe("m0-a...5678");
expect(result).not.toContain("abcdefgh");
});
it("redacts exactly 8-char key as short", () => {
expect(redactKey("12345678")).toBe("12***");
});
});
describe("createDefaultConfig", () => {
it("has correct defaults", () => {
const config = createDefaultConfig();
expect(config.platform.baseUrl).toBe("https://api.mem0.ai");
expect(config.platform.apiKey).toBe("");
expect(config.defaults.userId).toBe("");
expect(config.defaults.enableGraph).toBe(false);
});
});
describe("getNestedValue", () => {
it("gets platform.api_key", () => {
const config = createDefaultConfig();
config.platform.apiKey = "test-key";
expect(getNestedValue(config, "platform.api_key")).toBe("test-key");
});
it("returns undefined for nonexistent key", () => {
const config = createDefaultConfig();
expect(getNestedValue(config, "nonexistent.key")).toBeUndefined();
});
it("gets defaults.user_id", () => {
const config = createDefaultConfig();
config.defaults.userId = "alice";
expect(getNestedValue(config, "defaults.user_id")).toBe("alice");
});
});
describe("setNestedValue", () => {
it("sets platform.api_key", () => {
const config = createDefaultConfig();
expect(setNestedValue(config, "platform.api_key", "new-key")).toBe(true);
expect(config.platform.apiKey).toBe("new-key");
});
it("returns false for nonexistent key", () => {
const config = createDefaultConfig();
expect(setNestedValue(config, "nonexistent.key", "val")).toBe(false);
});
it("sets defaults.user_id", () => {
const config = createDefaultConfig();
expect(setNestedValue(config, "defaults.user_id", "bob")).toBe(true);
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);
});
});
+198
View File
@@ -0,0 +1,198 @@
/**
* Tests for output formatting.
*/
import { describe, it, expect, beforeEach, afterEach } from "vitest";
import {
formatMemoriesText,
formatMemoriesTable,
formatJson,
formatSingleMemory,
formatAddResult,
printResultSummary,
sanitizeAgentData,
} from "../src/output.js";
let output: string;
const originalLog = console.log;
beforeEach(() => {
output = "";
console.log = (...args: unknown[]) => {
output += args.map(String).join(" ") + "\n";
};
});
afterEach(() => {
console.log = originalLog;
});
const sampleMemories = [
{
id: "abc-123-def-456",
memory: "User prefers dark mode",
score: 0.92,
created_at: "2026-02-15T10:30:00Z",
categories: ["preferences"],
},
{
id: "ghi-789-jkl-012",
memory: "User uses vim keybindings",
score: 0.78,
created_at: "2026-03-01T14:00:00Z",
categories: ["tools"],
},
];
describe("formatMemoriesText", () => {
it("shows count and memory content", () => {
formatMemoriesText(sampleMemories);
expect(output).toContain("Found 2");
expect(output).toContain("dark mode");
expect(output).toContain("vim keybindings");
});
it("shows scores and IDs", () => {
formatMemoriesText(sampleMemories);
expect(output).toContain("0.92");
expect(output).toContain("abc-123-");
});
});
describe("formatMemoriesTable", () => {
it("renders a table with memory content", () => {
formatMemoriesTable(sampleMemories);
expect(output).toContain("dark mode");
});
});
describe("formatJson", () => {
it("outputs valid JSON", () => {
formatJson({ key: "value" });
expect(JSON.parse(output)).toEqual({ key: "value" });
});
});
describe("formatSingleMemory", () => {
it("shows memory text in text mode", () => {
formatSingleMemory(sampleMemories[0], "text");
expect(output).toContain("dark mode");
});
it("outputs JSON in json mode", () => {
formatSingleMemory(sampleMemories[0], "json");
expect(output).toContain("memory");
});
});
describe("formatAddResult", () => {
it("shows ADD event", () => {
formatAddResult({
results: [{ id: "abc-123", memory: "Test", event: "ADD" }],
});
expect(output).toContain("Added");
});
it("shows PENDING event", () => {
formatAddResult({
results: [{ status: "PENDING", event_id: "evt-12345678" }],
});
expect(output).toContain("Queued");
});
it("deduplicates PENDING entries with same event_id", () => {
formatAddResult({
results: [
{ status: "PENDING", event_id: "evt-dup" },
{ status: "PENDING", event_id: "evt-dup" },
],
});
// Should show only one PENDING block despite two entries with same event_id
expect(output.match(/Queued/g)?.length).toBe(1);
expect(output.match(/evt-dup/g)?.length).toBe(2); // event_id line + status hint line
});
});
describe("printResultSummary", () => {
it("shows count and duration", () => {
printResultSummary({ count: 5, durationSecs: 1.23 });
expect(output).toContain("5 results");
expect(output).toContain("1.23s");
});
it("handles singular", () => {
printResultSummary({ count: 1 });
expect(output).toContain("1 result");
expect(output).not.toContain("results");
});
});
describe("sanitizeAgentData", () => {
it("projects add results", () => {
const raw = [{ id: "abc", memory: "test", event: "ADD", metadata: { x: 1 }, categories: ["a"] }];
const result = sanitizeAgentData("add", raw) as Record<string, unknown>[];
expect(result).toEqual([{ id: "abc", memory: "test", event: "ADD" }]);
});
it("passes through PENDING add items", () => {
const raw = [{ status: "PENDING", event_id: "evt-123", noise: "x" }];
const result = sanitizeAgentData("add", raw) as Record<string, unknown>[];
expect(result).toEqual([{ status: "PENDING", event_id: "evt-123" }]);
});
it("projects search results", () => {
const raw = [{ id: "abc", memory: "test", score: 0.9, created_at: "2026-01-01", categories: ["a"], user_id: "u1" }];
const result = sanitizeAgentData("search", raw) as Record<string, unknown>[];
expect(result[0]).not.toHaveProperty("user_id");
expect(result[0]).toHaveProperty("score");
});
it("projects list results", () => {
const raw = [{ id: "abc", memory: "test", created_at: "2026-01-01", categories: ["a"], user_id: "u1" }];
const result = sanitizeAgentData("list", raw) as Record<string, unknown>[];
expect(Object.keys(result[0]).sort()).toEqual(["categories", "created_at", "id", "memory"]);
});
it("projects get result", () => {
const raw = { id: "abc", memory: "test", created_at: "2026-01-01", updated_at: "2026-01-02", categories: ["a"], metadata: { k: "v" }, user_id: "u1" };
const result = sanitizeAgentData("get", raw) as Record<string, unknown>;
expect(result).not.toHaveProperty("user_id");
expect(result).toHaveProperty("metadata");
});
it("projects update result", () => {
const raw = { id: "abc", memory: "updated", extra: "noise" };
const result = sanitizeAgentData("update", raw);
expect(result).toEqual({ id: "abc", memory: "updated" });
});
it("projects event list results", () => {
const raw = [{ id: "evt-1", event_type: "ADD", status: "SUCCEEDED", graph_status: null, latency: 100, created_at: "2026-01-01", updated_at: "2026-01-02" }];
const result = sanitizeAgentData("event list", raw) as Record<string, unknown>[];
expect(result[0]).not.toHaveProperty("updated_at");
expect(result[0]).not.toHaveProperty("graph_status");
});
it("flattens event status results", () => {
const raw = {
id: "evt-1", event_type: "ADD", status: "SUCCEEDED",
latency: 100, created_at: "2026-01-01", updated_at: "2026-01-02",
results: [{ id: "mem-1", event: "ADD", user_id: "alice", data: { memory: "dark mode" } }],
};
const result = sanitizeAgentData("event status", raw) as Record<string, unknown>;
const firstResult = (result.results as Record<string, unknown>[])[0];
expect(firstResult).toHaveProperty("memory", "dark mode");
expect(firstResult).not.toHaveProperty("data");
});
it("passes through status/config/import commands unchanged", () => {
const data = { key: "value", other: "stuff" };
for (const cmd of ["status", "import", "config show", "config get", "config set"]) {
expect(sanitizeAgentData(cmd, data)).toEqual(data);
}
});
it("handles null data", () => {
expect(sanitizeAgentData("add", null)).toBeNull();
});
});
+112
View File
@@ -0,0 +1,112 @@
/**
* Shared test helpers and mock factories for mem0 CLI tests.
*/
import { vi } from "vitest";
import type { Backend } from "../src/backend/base.js";
/** Create a mock backend with all methods stubbed with sensible defaults. */
export function createMockBackend(): Backend {
return {
add: vi.fn().mockResolvedValue({
results: [
{
id: "abc-123-def-456",
memory: "User prefers dark mode",
event: "ADD",
},
],
}),
search: vi.fn().mockResolvedValue([
{
id: "abc-123-def-456",
memory: "User prefers dark mode",
score: 0.92,
created_at: "2026-02-15T10:30:00Z",
categories: ["preferences"],
},
{
id: "ghi-789-jkl-012",
memory: "User uses vim keybindings",
score: 0.78,
created_at: "2026-03-01T14:00:00Z",
categories: ["tools"],
},
]),
get: vi.fn().mockResolvedValue({
id: "abc-123-def-456",
memory: "User prefers dark mode",
created_at: "2026-02-15T10:30:00Z",
updated_at: "2026-02-20T08:00:00Z",
metadata: { source: "onboarding" },
categories: ["preferences"],
}),
listMemories: vi.fn().mockResolvedValue([
{
id: "abc-123-def-456",
memory: "User prefers dark mode",
created_at: "2026-02-15T10:30:00Z",
categories: ["preferences"],
},
{
id: "ghi-789-jkl-012",
memory: "User uses vim keybindings",
created_at: "2026-03-01T14:00:00Z",
categories: ["tools"],
},
]),
update: vi.fn().mockResolvedValue({ id: "abc-123-def-456", memory: "Updated memory" }),
delete: vi.fn().mockResolvedValue({ status: "deleted" }),
status: vi.fn().mockResolvedValue({
connected: true,
backend: "platform",
base_url: "https://api.mem0.ai",
}),
deleteEntities: vi.fn().mockResolvedValue({ message: "Entity deleted" }),
entities: vi.fn().mockResolvedValue([
{ name: "alice", count: 5 },
{ name: "bob", count: 3 },
]),
listEvents: vi.fn().mockResolvedValue([
{
id: "evt-abc-123-def-456",
event_type: "ADD",
status: "SUCCEEDED",
graph_status: null,
latency: 1234.5,
created_at: "2026-04-01T10:00:00Z",
updated_at: "2026-04-01T10:00:01Z",
},
{
id: "evt-def-456-ghi-789",
event_type: "SEARCH",
status: "PENDING",
graph_status: null,
latency: null,
created_at: "2026-04-01T10:01:00Z",
updated_at: "2026-04-01T10:01:00Z",
},
]),
getEvent: vi.fn().mockResolvedValue({
id: "evt-abc-123-def-456",
event_type: "ADD",
status: "SUCCEEDED",
graph_status: "SUCCEEDED",
latency: 1234.5,
created_at: "2026-04-01T10:00:00Z",
updated_at: "2026-04-01T10:00:01Z",
results: [
{
id: "mem-abc-123",
event: "ADD",
user_id: "alice",
data: { memory: "User prefers dark mode" },
},
],
}),
};
}
+18
View File
@@ -0,0 +1,18 @@
{
"compilerOptions": {
"target": "ES2022",
"module": "ESNext",
"moduleResolution": "bundler",
"strict": true,
"outDir": "dist",
"rootDir": "src",
"declaration": true,
"esModuleInterop": true,
"skipLibCheck": true,
"forceConsistentCasingInFileNames": true,
"resolveJsonModule": true,
"isolatedModules": true
},
"include": ["src/**/*.ts"],
"exclude": ["node_modules", "dist", "tests"]
}
+15
View File
@@ -0,0 +1,15 @@
import { defineConfig } from 'tsup';
import { createRequire } from 'node:module';
const _require = createRequire(import.meta.url);
const pkg = _require('./package.json');
export default defineConfig({
entry: ['src/index.ts'],
format: ['esm'],
dts: true,
clean: true,
define: {
__CLI_VERSION__: JSON.stringify(pkg.version),
},
});
+11
View File
@@ -0,0 +1,11 @@
import { createRequire } from "node:module";
import { defineConfig } from "vitest/config";
const _require = createRequire(import.meta.url);
const pkg = _require("./package.json") as { version: string };
export default defineConfig({
define: {
__CLI_VERSION__: JSON.stringify(pkg.version),
},
});
+43
View File
@@ -0,0 +1,43 @@
VENV := .venv
PYTHON := $(VENV)/bin/python
PIP := $(VENV)/bin/pip
.PHONY: install dev lint format test build clean publish publish-test shell
$(VENV)/bin/activate:
python3 -m venv $(VENV)
$(PIP) install -U pip
install: $(VENV)/bin/activate
$(PIP) install -e .
dev: $(VENV)/bin/activate
$(PIP) install -e ".[dev]"
lint: dev
$(VENV)/bin/ruff check .
$(VENV)/bin/ruff format --check .
format: dev
$(VENV)/bin/ruff check --fix .
$(VENV)/bin/ruff format .
test: dev
$(VENV)/bin/pytest
build: clean $(VENV)/bin/activate
$(PIP) install hatch
$(VENV)/bin/hatch build
clean:
rm -rf dist/
publish: build
$(VENV)/bin/hatch publish
publish-test: build
$(VENV)/bin/hatch publish --repo test
shell: $(VENV)/bin/activate
@echo "Spawning a new shell with the virtual environment activated..."
@VIRTUAL_ENV=$(CURDIR)/$(VENV) PATH=$(CURDIR)/$(VENV)/bin:$$PATH exec $(SHELL)
+46
View File
@@ -0,0 +1,46 @@
# mem0 CLI
The official command-line interface for [mem0](https://mem0.ai) — the memory layer for AI agents.
## Installation
### Using pipx (recommended)
```bash
pipx install mem0-cli
```
### Using pip
```bash
pip install mem0-cli
```
> **Note:** On macOS with Homebrew Python, `pip install` outside a virtual environment will fail with an `externally-managed-environment` error ([PEP 668](https://peps.python.org/pep-0668/)). Use `pipx` instead, or install inside a virtual environment.
## Quick Start
```bash
# Set up your configuration
mem0 init
# Add a memory
mem0 add "I prefer dark mode and use vim keybindings" --user-id alice
# Search memories
mem0 search "What are Alice's preferences?" --user-id alice
# List all memories
mem0 list --user-id alice
```
## Releasing
1. Update `version` in `pyproject.toml`
2. Create a GitHub Release with tag `cli-v<version>` (e.g. `cli-v0.2.0`)
For a pre-release, use a beta version like `0.2.0b1` and check the **pre-release** checkbox.
## License
Apache-2.0
+107
View File
@@ -0,0 +1,107 @@
# Development
## Prerequisites
- Python **3.10+**
- `make` (optional — you can use plain Python commands instead)
All commands below should be run from the `python/` directory:
```bash
cd python
```
## Setup
### Using Make (recommended)
All `make` targets automatically create a virtual environment (`.venv/`) and install the required dependencies — no manual setup needed.
```bash
# Install the CLI in editable mode
make install
# Install with dev tools (tests + linting)
make dev
```
### Using Python directly
```bash
python3 -m venv .venv
source .venv/bin/activate
python -m pip install -U pip
# Install in editable mode
pip install -e .
# With dev tools
pip install -e ".[dev]"
```
## Make targets
| Target | Description |
| ------------------- | ------------------------------------------------ |
| `make install` | Create venv and install the CLI (editable mode) |
| `make dev` | Create venv and install CLI + dev dependencies |
| `make test` | Run all tests (installs dev deps if needed) |
| `make lint` | Run linter and format check |
| `make format` | Auto-fix lint issues and format code |
| `make build` | Build distribution packages |
| `make clean` | Remove `dist/` |
| `make publish` | Build and publish to PyPI |
| `make publish-test` | Build and publish to Test PyPI |
| `make shell` | Open a new shell with the venv activated |
## Run tests
```bash
# Using Make
make test
# Using Python directly
pytest
# Run a specific test file
pytest tests/test_cli_integration.py
# Run a single test
pytest -k test_help
```
## Run the CLI
```bash
# Using Make — drop into an activated shell
make shell
mem0 --help
# Using Python directly (with venv activated)
source .venv/bin/activate
mem0 --help
mem0 version
# Or run without activating
.venv/bin/mem0 --help
```
## Lint
```bash
# Using Make
make lint # check only
make format # auto-fix
# Using Python directly (with venv activated)
ruff check .
ruff format .
```
## Optional extras
### OSS integration
```bash
pip install -e ".[oss]"
```
+77
View File
@@ -0,0 +1,77 @@
[build-system]
requires = ["hatchling"]
build-backend = "hatchling.build"
[project]
name = "mem0-cli"
version = "0.2.0"
description = "The official CLI for mem0 — the memory layer for AI agents"
readme = "README.md"
license = "Apache-2.0"
requires-python = ">=3.10"
authors = [
{ name = "mem0.ai", email = "founders@mem0.ai" },
]
keywords = ["mem0", "memory", "ai", "agents", "cli"]
classifiers = [
"Development Status :: 4 - Beta",
"Environment :: Console",
"Intended Audience :: Developers",
"License :: OSI Approved :: Apache Software License",
"Programming Language :: Python :: 3",
"Programming Language :: Python :: 3.10",
"Programming Language :: Python :: 3.11",
"Programming Language :: Python :: 3.12",
"Topic :: Software Development :: Libraries",
]
dependencies = [
"typer>=0.9.0",
"rich>=13.0.0",
"httpx>=0.24.0",
]
[project.optional-dependencies]
oss = ["mem0ai>=0.1.0"]
dev = [
"pytest>=7.0",
"pytest-asyncio>=0.21",
"ruff>=0.1.0",
]
[project.scripts]
mem0 = "mem0_cli.app:main"
[tool.hatch.build.targets.wheel]
packages = ["src/mem0_cli"]
[tool.hatch.build.targets.sdist]
include = ["src/mem0_cli"]
[tool.ruff]
target-version = "py310"
line-length = 100
[tool.ruff.lint]
select = [
"E", # pycodestyle errors
"F", # pyflakes
"I", # isort (import sorting)
"W", # pycodestyle warnings
"UP", # pyupgrade (modern Python syntax)
"B", # flake8-bugbear (common bugs)
"SIM", # flake8-simplify
"RUF", # ruff-specific rules
]
ignore = [
"E501", # line too long — handled by formatter
"B008", # function call in default arg — required by Typer's Option/Argument pattern
"SIM108", # ternary operator — sometimes less readable
]
[tool.ruff.lint.isort]
known-first-party = ["mem0_cli"]
[tool.ruff.format]
quote-style = "double"
indent-style = "space"
docstring-code-format = true
+3
View File
@@ -0,0 +1,3 @@
"""mem0 CLI — the command-line interface for the mem0 memory layer."""
__version__ = "0.1.0"
+5
View File
@@ -0,0 +1,5 @@
"""Allow running with `python -m mem0_cli`."""
from mem0_cli.app import main
main()
File diff suppressed because it is too large Load Diff
@@ -0,0 +1,5 @@
"""Backend abstraction layer for mem0 CLI."""
from mem0_cli.backend.base import Backend, get_backend
__all__ = ["Backend", "get_backend"]
+118
View File
@@ -0,0 +1,118 @@
"""Abstract backend interface and factory."""
from __future__ import annotations
from abc import ABC, abstractmethod
from typing import Any
from mem0_cli.config import Mem0Config
class Backend(ABC):
"""Abstract interface for mem0 backends."""
@abstractmethod
def add(
self,
content: str | None = None,
messages: list[dict] | None = None,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
metadata: dict | None = None,
immutable: bool = False,
infer: bool = True,
expires: str | None = None,
categories: list[str] | None = None,
enable_graph: bool = False,
) -> dict: ...
@abstractmethod
def search(
self,
query: str,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
top_k: int = 10,
threshold: float = 0.3,
rerank: bool = False,
keyword: bool = False,
filters: dict | None = None,
fields: list[str] | None = None,
enable_graph: bool = False,
) -> list[dict]: ...
@abstractmethod
def get(self, memory_id: str) -> dict: ...
@abstractmethod
def list_memories(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
page: int = 1,
page_size: int = 100,
category: str | None = None,
after: str | None = None,
before: str | None = None,
enable_graph: bool = False,
) -> list[dict]: ...
@abstractmethod
def update(
self, memory_id: str, content: str | None = None, metadata: dict | None = None
) -> dict: ...
@abstractmethod
def delete(
self,
memory_id: str | None = None,
*,
all: bool = False,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
) -> dict: ...
@abstractmethod
def delete_entities(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
) -> dict: ...
@abstractmethod
def status(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
) -> dict[str, Any]: ...
@abstractmethod
def entities(self, entity_type: str) -> list[dict]: ...
@abstractmethod
def list_events(self) -> list[dict]: ...
@abstractmethod
def get_event(self, event_id: str) -> dict: ...
def get_backend(config: Mem0Config) -> Backend:
"""Return the Platform backend."""
from mem0_cli.backend.platform import PlatformBackend
return PlatformBackend(config.platform)
+324
View File
@@ -0,0 +1,324 @@
"""Platform (SaaS) backend — communicates with api.mem0.ai."""
from __future__ import annotations
from typing import Any
import httpx
from mem0_cli.backend.base import Backend
from mem0_cli.config import PlatformConfig
class PlatformBackend(Backend):
"""Backend that talks to the mem0 Platform API."""
def __init__(self, config: PlatformConfig) -> None:
self.config = config
self.base_url = config.base_url.rstrip("/")
self._client = httpx.Client(
base_url=self.base_url,
headers={
"Authorization": f"Token {config.api_key}",
"Content-Type": "application/json",
},
timeout=30.0,
)
def _request(self, method: str, path: str, **kwargs: Any) -> Any:
resp = self._client.request(method, path, **kwargs)
if resp.status_code == 401:
raise AuthError("Authentication failed. Your API key may be invalid or expired.")
if resp.status_code == 404:
raise NotFoundError(f"Resource not found: {path}")
if resp.status_code == 400:
# Extract API error detail when available
try:
detail = resp.json().get("detail", resp.text)
except Exception:
detail = resp.text
raise APIError(f"Bad request to {path}: {detail}")
resp.raise_for_status()
if resp.status_code == 204:
return {}
return resp.json()
def add(
self,
content: str | None = None,
messages: list[dict] | None = None,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
metadata: dict | None = None,
immutable: bool = False,
infer: bool = True,
expires: str | None = None,
categories: list[str] | None = None,
enable_graph: bool = False,
) -> dict:
payload: dict[str, Any] = {}
if messages:
payload["messages"] = messages
elif content:
payload["messages"] = [{"role": "user", "content": content}]
if user_id:
payload["user_id"] = user_id
if agent_id:
payload["agent_id"] = agent_id
if app_id:
payload["app_id"] = app_id
if run_id:
payload["run_id"] = run_id
if metadata:
payload["metadata"] = metadata
if immutable:
payload["immutable"] = True
if not infer:
payload["infer"] = False
if expires:
payload["expiration_date"] = expires
if categories:
payload["categories"] = categories
if enable_graph:
payload["enable_graph"] = True
return self._request("POST", "/v1/memories/", json=payload)
def _build_filters(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
extra_filters: dict | None = None,
) -> dict | None:
"""Build a filters dict for v2 API endpoints.
Entity IDs are ANDed (all provided IDs must match).
Extra filters (date ranges, categories) are also ANDed.
"""
# If caller passed a pre-built filter structure (e.g. --filter from CLI), use it directly
if extra_filters and ("AND" in extra_filters or "OR" in extra_filters):
return extra_filters
# Build AND conditions for entity IDs
and_conditions: list[dict[str, Any]] = []
if user_id:
and_conditions.append({"user_id": user_id})
if agent_id:
and_conditions.append({"agent_id": agent_id})
if app_id:
and_conditions.append({"app_id": app_id})
if run_id:
and_conditions.append({"run_id": run_id})
# Append any extra filters (dates, categories)
if extra_filters:
for k, v in extra_filters.items():
and_conditions.append({k: v})
if len(and_conditions) == 1:
return and_conditions[0]
elif and_conditions:
return {"AND": and_conditions}
else:
return None
def search(
self,
query: str,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
top_k: int = 10,
threshold: float = 0.3,
rerank: bool = False,
keyword: bool = False,
filters: dict | None = None,
fields: list[str] | None = None,
enable_graph: bool = False,
) -> list[dict]:
payload: dict[str, Any] = {"query": query, "top_k": top_k, "threshold": threshold}
api_filters = self._build_filters(
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
extra_filters=filters,
)
if api_filters:
payload["filters"] = api_filters
if rerank:
payload["rerank"] = True
if keyword:
payload["keyword_search"] = True
if fields:
payload["fields"] = fields
if enable_graph:
payload["enable_graph"] = True
result = self._request("POST", "/v2/memories/search/", json=payload)
return (
result
if isinstance(result, list)
else result.get("results", result.get("memories", []))
)
def get(self, memory_id: str) -> dict:
return self._request("GET", f"/v1/memories/{memory_id}/")
def list_memories(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
page: int = 1,
page_size: int = 100,
category: str | None = None,
after: str | None = None,
before: str | None = None,
enable_graph: bool = False,
) -> list[dict]:
payload: dict[str, Any] = {}
params = {"page": str(page), "page_size": str(page_size)}
# Build filters for v2 API — entity IDs and date filters go inside "filters"
extra: dict[str, Any] = {}
if category:
extra["categories"] = {"contains": category}
if after:
extra["created_at"] = {**(extra.get("created_at", {})), "gte": after}
if before:
extra["created_at"] = {**(extra.get("created_at", {})), "lte": before}
api_filters = self._build_filters(
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
extra_filters=extra if extra else None,
)
if api_filters:
payload["filters"] = api_filters
if enable_graph:
payload["enable_graph"] = True
result = self._request("POST", "/v2/memories/", json=payload, params=params)
return (
result
if isinstance(result, list)
else result.get("results", result.get("memories", []))
)
def update(
self, memory_id: str, content: str | None = None, metadata: dict | None = None
) -> dict:
payload: dict[str, Any] = {}
if content:
payload["text"] = content
if metadata:
payload["metadata"] = metadata
return self._request("PUT", f"/v1/memories/{memory_id}/", json=payload)
def delete(
self,
memory_id: str | None = None,
*,
all: bool = False,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
) -> dict:
if all:
params: dict[str, str] = {}
if user_id:
params["user_id"] = user_id
if agent_id:
params["agent_id"] = agent_id
if app_id:
params["app_id"] = app_id
if run_id:
params["run_id"] = run_id
return self._request("DELETE", "/v1/memories/", params=params)
elif memory_id:
return self._request("DELETE", f"/v1/memories/{memory_id}/")
else:
raise ValueError("Either memory_id or --all is required")
def delete_entities(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
app_id: str | None = None,
run_id: str | None = None,
) -> dict:
# v2 endpoint: DELETE /v2/entities/{entity_type}/{entity_id}/
type_map = {
"user": user_id,
"agent": agent_id,
"app": app_id,
"run": run_id,
}
entities = {t: v for t, v in type_map.items() if v}
if not entities:
raise ValueError("At least one entity ID is required for delete_entities.")
# Delete each provided entity via the v2 path-based endpoint
result: dict = {}
for entity_type, entity_id in entities.items():
result = self._request("DELETE", f"/v2/entities/{entity_type}/{entity_id}/")
return result
def status(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
) -> dict[str, Any]:
"""Check connectivity using the ping endpoint."""
try:
self._request("GET", "/v1/ping/")
return {"connected": True, "backend": "platform", "base_url": self.base_url}
except Exception as e:
return {"connected": False, "backend": "platform", "error": str(e)}
def entities(self, entity_type: str) -> list[dict]:
result = self._request("GET", "/v1/entities/")
items = result if isinstance(result, list) else result.get("results", [])
# Filter by entity type client-side (API returns all types)
type_map = {"users": "user", "agents": "agent", "apps": "app", "runs": "run"}
target_type = type_map.get(entity_type)
if target_type:
items = [e for e in items if e.get("type", "").lower() == target_type]
return items
def list_events(self) -> list[dict]:
result = self._request("GET", "/v1/events/")
return result if isinstance(result, list) else result.get("results", [])
def get_event(self, event_id: str) -> dict:
return self._request("GET", f"/v1/event/{event_id}/")
class AuthError(Exception):
pass
class NotFoundError(Exception):
pass
class APIError(Exception):
pass
+176
View File
@@ -0,0 +1,176 @@
"""Branding and ASCII art for mem0 CLI."""
import os
import sys
import time
from contextlib import contextmanager
from rich.console import Console
from rich.panel import Panel
from rich.status import Status
from rich.text import Text
# stderr console for spinners, errors, and timing messages
_err = Console(stderr=True)
LOGO = r"""
███╗ ███╗███████╗███╗ ███╗ ██████╗ ██████╗██╗ ██╗
████╗ ████║██╔════╝████╗ ████║██╔═████╗ ██╔════╝██║ ██║
██╔████╔██║█████╗ ██╔████╔██║██║██╔██║ ██║ ██║ ██║
██║╚██╔╝██║██╔══╝ ██║╚██╔╝██║████╔╝██║ ██║ ██║ ██║
██║ ╚═╝ ██║███████╗██║ ╚═╝ ██║╚██████╔╝ ╚██████╗███████╗██║
╚═╝ ╚═╝╚══════╝╚═╝ ╚═╝ ╚═════╝ ╚═════╝╚══════╝╚═╝
"""
LOGO_MINI = "◆ mem0"
TAGLINE = "The Memory Layer for AI Agents"
BRAND_COLOR = "#F1C96C" # Golden
ACCENT_COLOR = "#F5D78E"
SUCCESS_COLOR = "#22c55e"
ERROR_COLOR = "#ef4444"
WARNING_COLOR = "#f59e0b"
DIM_COLOR = "#6b7280"
def _sym(fancy: str, plain: str) -> str:
"""Return *fancy* when stdout is a TTY with colour, else *plain*."""
if not sys.stdout.isatty() or os.environ.get("NO_COLOR") is not None:
return plain
return fancy
def print_banner(console: Console) -> None:
"""Print the mem0 welcome banner."""
from mem0_cli.state import is_agent_mode
if is_agent_mode():
return
logo_text = Text(LOGO, style=f"bold {BRAND_COLOR}")
tagline = Text(f" {TAGLINE}\n", style=f"{ACCENT_COLOR}")
content = Text()
content.append_text(logo_text)
content.append_text(tagline)
panel = Panel(
content,
border_style=BRAND_COLOR,
padding=(0, 2),
subtitle=f"[{DIM_COLOR}]Python SDK · v{_get_version()}[/]",
subtitle_align="right",
)
console.print(panel)
def print_success(console: Console, message: str) -> None:
from mem0_cli.state import is_agent_mode
if is_agent_mode():
return
sym = _sym("✓", "[ok]")
console.print(f"[{SUCCESS_COLOR}]{sym}[/] {message}")
def print_error(console: Console, message: str, hint: str | None = None) -> None:
from mem0_cli.state import get_current_command, is_agent_mode
if is_agent_mode():
import json as _json
envelope = {
"status": "error",
"command": get_current_command(),
"error": message,
"data": None,
}
print(_json.dumps(envelope))
return
sym = _sym("✗", "[error]")
console.print(f"[{ERROR_COLOR}]{sym} Error:[/] {message}")
if hint:
console.print(f" [{DIM_COLOR}]{hint}[/]")
def print_warning(console: Console, message: str) -> None:
from mem0_cli.state import is_agent_mode
if is_agent_mode():
return
sym = _sym("⚠", "[warn]")
console.print(f"[{WARNING_COLOR}]{sym}[/] {message}")
def print_info(console: Console, message: str) -> None:
from mem0_cli.state import is_agent_mode
if is_agent_mode():
return
sym = _sym("◆", "*")
console.print(f"[{BRAND_COLOR}]{sym}[/] {message}")
@contextmanager
def timed_status(console: Console, message: str):
"""Spinner with automatic timing. Yields a context object for setting the final message.
The spinner and timing output are sent to stderr (via ``_err``) so they
never contaminate machine-readable stdout. The *console* parameter is
kept for backward compatibility but is not used for spinner output.
In agent mode the spinner is suppressed entirely.
"""
from mem0_cli.state import is_agent_mode
class _Ctx:
def __init__(self):
self.success_msg = ""
self.error_msg = ""
ctx = _Ctx()
if is_agent_mode():
try:
yield ctx
except Exception:
raise
return
start = time.perf_counter()
try:
with Status(f"[{DIM_COLOR}]{message}[/]", console=_err):
yield ctx
except Exception:
elapsed = time.perf_counter() - start
if ctx.error_msg:
print_error(_err, f"{ctx.error_msg} ({elapsed:.2f}s)")
if "Authentication failed" in ctx.error_msg:
_err.print(
f" [{DIM_COLOR}]Run [bold]mem0 init[/bold] to reconfigure your API key"
f" · [bold]https://app.mem0.ai/dashboard/api-keys[/bold][/]"
)
raise
else:
elapsed = time.perf_counter() - start
if ctx.success_msg:
print_success(_err, f"{ctx.success_msg} ({elapsed:.2f}s)")
def print_scope(console: Console, **ids: str | None) -> None:
"""Show active entity scope if any IDs are set."""
from mem0_cli.state import is_agent_mode
if is_agent_mode():
return
parts = []
for key, val in ids.items():
if val:
parts.append(f"{key}={val}")
if parts:
scope_str = ", ".join(parts)
console.print(f" [{DIM_COLOR}]Scope: {scope_str}[/]")
def _get_version() -> str:
from mem0_cli import __version__
return __version__
@@ -0,0 +1 @@
"""CLI command modules."""
@@ -0,0 +1,132 @@
"""Config management commands: show, set, get."""
from __future__ import annotations
from rich.console import Console
from rich.table import Table
from mem0_cli.branding import ACCENT_COLOR, BRAND_COLOR, DIM_COLOR, print_error, print_success
from mem0_cli.config import (
get_nested_value,
load_config,
redact_key,
save_config,
set_nested_value,
)
console = Console()
err_console = Console(stderr=True)
def cmd_config_show(*, output: str = "text") -> None:
"""Display current configuration (secrets redacted)."""
from mem0_cli.output import format_agent_envelope
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("config show")
if is_agent_mode():
output = "agent"
config = load_config()
if output in ("json", "agent"):
format_agent_envelope(
console,
command="config show",
data={
"defaults": {
"user_id": config.defaults.user_id or None,
"agent_id": config.defaults.agent_id or None,
"app_id": config.defaults.app_id or None,
"run_id": config.defaults.run_id or None,
"enable_graph": config.defaults.enable_graph,
},
"platform": {
"api_key": redact_key(config.platform.api_key),
"base_url": config.platform.base_url,
},
},
)
return
console.print()
console.print(f" [{BRAND_COLOR}]◆ mem0 Configuration[/]\n")
table = Table(border_style=BRAND_COLOR, header_style=f"bold {ACCENT_COLOR}", padding=(0, 2))
table.add_column("Key", style="bold")
table.add_column("Value")
# Defaults
table.add_row(
"defaults.user_id",
config.defaults.user_id or f"[{DIM_COLOR}](not set)[/]",
)
table.add_row(
"defaults.agent_id",
config.defaults.agent_id or f"[{DIM_COLOR}](not set)[/]",
)
table.add_row(
"defaults.app_id",
config.defaults.app_id or f"[{DIM_COLOR}](not set)[/]",
)
table.add_row(
"defaults.run_id",
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("", "")
# Platform
table.add_row("[bold]platform.api_key[/]", redact_key(config.platform.api_key))
table.add_row("platform.base_url", config.platform.base_url)
console.print(table)
console.print()
def cmd_config_get(key: str) -> None:
"""Get a config value."""
from mem0_cli.output import format_agent_envelope
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("config get")
config = load_config()
value = get_nested_value(config, key)
if value is None:
print_error(err_console, f"Unknown config key: {key}")
return
display_value = (
redact_key(str(value)) if ("api_key" in key or "key" in key.split(".")[-1:]) else str(value)
)
if is_agent_mode():
format_agent_envelope(
console, command="config get", data={"key": key, "value": display_value}
)
else:
console.print(display_value)
def cmd_config_set(key: str, value: str) -> None:
"""Set a config value."""
from mem0_cli.output import format_agent_envelope
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("config set")
config = load_config()
if set_nested_value(config, key, value):
save_config(config)
display = redact_key(value) if "key" in key else value
if is_agent_mode():
format_agent_envelope(
console, command="config set", data={"key": key, "value": display}
)
else:
print_success(console, f"{key} = {display}")
else:
print_error(err_console, f"Unknown config key: {key}")
@@ -0,0 +1,168 @@
"""Entity management commands."""
from __future__ import annotations
import time as _time
import typer
from rich.console import Console
from rich.table import Table
from mem0_cli.backend.base import Backend
from mem0_cli.branding import (
ACCENT_COLOR,
BRAND_COLOR,
DIM_COLOR,
print_error,
print_info,
print_success,
timed_status,
)
from mem0_cli.output import format_agent_envelope, format_json
console = Console()
err_console = Console(stderr=True)
def cmd_entities_list(backend: Backend, entity_type: str, *, output: str) -> None:
"""List entities of a given type."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("entity list")
if is_agent_mode():
output = "agent"
valid_types = {"users", "agents", "apps", "runs"}
if entity_type not in valid_types:
print_error(
err_console, f"Invalid entity type: {entity_type}. Use: {', '.join(valid_types)}"
)
raise typer.Exit(1)
_start = _time.perf_counter()
with timed_status(err_console, f"Fetching {entity_type}...") as _ts:
try:
results = backend.entities(entity_type)
except Exception as e:
print_error(err_console, str(e), hint="This feature may require the mem0 Platform.")
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "agent":
format_agent_envelope(
console,
command="entity list",
data=results,
count=len(results),
duration_ms=int(_elapsed * 1000),
)
return
if output == "json":
format_json(console, results)
return
if not results:
print_info(console, f"No {entity_type} found.")
return
table = Table(border_style=BRAND_COLOR, header_style=f"bold {ACCENT_COLOR}", padding=(0, 1))
table.add_column("Name / ID", style="bold")
table.add_column("Created", max_width=12)
for entity in results:
name = entity.get("name", entity.get("id", "—"))
created = str(entity.get("created_at", "—"))[:10]
table.add_row(str(name), created)
console.print()
console.print(table)
console.print(f" [{DIM_COLOR}]{len(results)} {entity_type} ({_elapsed:.2f}s)[/]")
console.print()
def cmd_entities_delete(
backend: Backend,
*,
user_id: str | None,
agent_id: str | None,
app_id: str | None,
run_id: str | None,
force: bool,
dry_run: bool = False,
output: str,
) -> None:
"""Delete an entity and all its memories (cascade delete)."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("entity delete")
if is_agent_mode():
output = "agent"
if not force:
print_error(err_console, "Destructive operation requires --force in agent mode.")
raise typer.Exit(1)
if not any([user_id, agent_id, app_id, run_id]):
print_error(
err_console, "Provide at least one of --user-id, --agent-id, --app-id, --run-id."
)
raise typer.Exit(1)
scope_parts = []
if user_id:
scope_parts.append(f"user={user_id}")
if agent_id:
scope_parts.append(f"agent={agent_id}")
if app_id:
scope_parts.append(f"app={app_id}")
if run_id:
scope_parts.append(f"run={run_id}")
scope_str = ", ".join(scope_parts)
if dry_run:
print_info(console, f"Would delete entity {scope_str} and all its memories.")
print_info(console, "No changes made (dry run).")
return
if not force:
confirm = typer.confirm(
f"\n \u26a0 Delete entity {scope_str} AND all its memories? This cannot be undone."
)
if not confirm:
print_info(console, "Cancelled.")
raise typer.Exit(0)
_start = _time.perf_counter()
with timed_status(err_console, "Deleting entity...") as _ts:
try:
result = backend.delete_entities(
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
scope = {
k: v
for k, v in {
"user_id": user_id,
"agent_id": agent_id,
"app_id": app_id,
"run_id": run_id,
}.items()
if v
}
if output == "agent":
format_agent_envelope(
console,
command="entity delete",
data={"deleted": True},
scope=scope or None,
duration_ms=int(_elapsed * 1000),
)
elif output == "json":
format_json(console, result)
elif output != "quiet":
print_success(console, f"Entity deleted with all memories ({_elapsed:.2f}s)")
@@ -0,0 +1,176 @@
"""Event commands: list and status."""
from __future__ import annotations
import typer
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from mem0_cli.backend.base import Backend
from mem0_cli.branding import (
ACCENT_COLOR,
BRAND_COLOR,
DIM_COLOR,
ERROR_COLOR,
SUCCESS_COLOR,
WARNING_COLOR,
print_info,
timed_status,
)
from mem0_cli.output import format_agent_envelope, format_json
console = Console()
err_console = Console(stderr=True)
_STATUS_STYLE = {
"SUCCEEDED": f"[{SUCCESS_COLOR}]SUCCEEDED[/]",
"PENDING": f"[{ACCENT_COLOR}]PENDING[/]",
"FAILED": f"[{ERROR_COLOR}]FAILED[/]",
"PROCESSING": f"[{WARNING_COLOR}]PROCESSING[/]",
}
def _status_styled(status: str) -> str:
return _STATUS_STYLE.get(status.upper(), status)
def cmd_event_list(backend: Backend, *, output: str = "table") -> None:
"""List recent background events."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("event list")
if is_agent_mode():
output = "agent"
import time as _time
_start = _time.perf_counter()
with timed_status(err_console, "Fetching events...") as _ts:
try:
results = backend.list_events()
except Exception as e:
_ts.error_msg = str(e)
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "agent":
format_agent_envelope(
console,
command="event list",
data=results,
count=len(results),
duration_ms=int(_elapsed * 1000),
)
return
if output == "json":
format_json(console, results)
return
if not results:
console.print()
print_info(console, "No events found.")
console.print()
return
table = Table(
border_style=BRAND_COLOR,
header_style=f"bold {ACCENT_COLOR}",
row_styles=["", "dim"],
padding=(0, 1),
)
table.add_column("Event ID", style="dim", max_width=10, no_wrap=True)
table.add_column("Type", max_width=14)
table.add_column("Status", max_width=12)
table.add_column("Latency", max_width=10, justify="right")
table.add_column("Created", max_width=20)
for ev in results:
ev_id = str(ev.get("id", ""))[:8]
ev_type = str(ev.get("event_type", "—"))
status = str(ev.get("status", "—"))
latency = ev.get("latency")
latency_str = f"{latency:.0f}ms" if isinstance(latency, (int, float)) else "—"
created = str(ev.get("created_at", "—"))[:19].replace("T", " ")
table.add_row(ev_id, ev_type, _status_styled(status), latency_str, created)
console.print()
console.print(table)
console.print(f" [{DIM_COLOR}]{len(results)} event{'s' if len(results) != 1 else ''}[/]")
console.print()
def cmd_event_status(backend: Backend, event_id: str, *, output: str = "text") -> None:
"""Get the status of a specific background event."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("event status")
if is_agent_mode():
output = "agent"
import time as _time
_start = _time.perf_counter()
with timed_status(err_console, "Fetching event...") as _ts:
try:
ev = backend.get_event(event_id)
except Exception as e:
_ts.error_msg = str(e)
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "agent":
format_agent_envelope(
console,
command="event status",
data=ev,
duration_ms=int(_elapsed * 1000),
)
return
if output == "json":
format_json(console, ev)
return
status = str(ev.get("status", "—"))
ev_type = str(ev.get("event_type", "—"))
latency = ev.get("latency")
latency_str = f"{latency:.0f}ms" if isinstance(latency, (int, float)) else "—"
created = str(ev.get("created_at", "—"))[:19].replace("T", " ")
updated = str(ev.get("updated_at", "—"))[:19].replace("T", " ")
results = ev.get("results")
lines = []
lines.append(f" [{DIM_COLOR}]Event ID:[/] {event_id}")
lines.append(f" [{DIM_COLOR}]Type:[/] {ev_type}")
lines.append(f" [{DIM_COLOR}]Status:[/] {_status_styled(status)}")
lines.append(f" [{DIM_COLOR}]Latency:[/] {latency_str}")
lines.append(f" [{DIM_COLOR}]Created:[/] {created}")
lines.append(f" [{DIM_COLOR}]Updated:[/] {updated}")
if results:
lines.append("")
lines.append(f" [{DIM_COLOR}]Results ({len(results)}):[/]")
for r in results:
mem_id = str(r.get("id", ""))[:8]
data = r.get("data", {})
memory = data.get("memory", "") if isinstance(data, dict) else str(data)
ev_name = str(r.get("event", ""))
user = str(r.get("user_id", ""))
detail = f"{ev_name} {memory}"
if user:
detail += f" [{DIM_COLOR}](user_id={user})[/]"
lines.append(f" [{SUCCESS_COLOR}]·[/] {detail} [{DIM_COLOR}]({mem_id})[/]")
content = "\n".join(lines)
panel = Panel(
content,
title=f"[{BRAND_COLOR}]Event Status[/]",
title_align="left",
border_style=BRAND_COLOR,
padding=(1, 1),
)
console.print()
console.print(panel)
console.print()
@@ -0,0 +1,394 @@
"""mem0 init — interactive setup wizard."""
from __future__ import annotations
import os
import re
import sys
import httpx
import typer
from rich.console import Console
from rich.prompt import Prompt
from mem0_cli.branding import (
BRAND_COLOR,
DIM_COLOR,
print_banner,
print_error,
print_info,
print_success,
)
from mem0_cli.config import CONFIG_FILE, DEFAULT_BASE_URL, Mem0Config, load_config, save_config
console = Console()
err_console = Console(stderr=True)
def _prompt_secret(label: str) -> str:
"""Prompt for a secret value, echoing '*' for each character typed."""
sys.stdout.write(label)
sys.stdout.flush()
chars: list[str] = []
if sys.platform == "win32":
import msvcrt
while True:
ch = msvcrt.getwch()
if ch in ("\r", "\n"):
sys.stdout.write("\n")
sys.stdout.flush()
break
if ch == "\x03":
raise KeyboardInterrupt
if ch in ("\x08", "\x7f"): # backspace
if chars:
chars.pop()
sys.stdout.write("\b \b")
sys.stdout.flush()
else:
chars.append(ch)
sys.stdout.write("*")
sys.stdout.flush()
else:
import termios
import tty
fd = sys.stdin.fileno()
old_settings = termios.tcgetattr(fd)
try:
tty.setraw(fd)
while True:
ch = sys.stdin.read(1)
if ch in ("\r", "\n"):
sys.stdout.write("\r\n")
sys.stdout.flush()
break
if ch == "\x03":
raise KeyboardInterrupt
if ch in ("\x7f", "\x08"): # backspace/delete
if chars:
chars.pop()
sys.stdout.write("\b \b")
sys.stdout.flush()
elif ch == "\x15": # Ctrl+U — clear line
sys.stdout.write("\b \b" * len(chars))
sys.stdout.flush()
chars = []
elif ch >= " ": # ignore other control characters
chars.append(ch)
sys.stdout.write("*")
sys.stdout.flush()
finally:
termios.tcsetattr(fd, termios.TCSADRAIN, old_settings)
return "".join(chars)
_EMAIL_RE = re.compile(r"^[^@\s]+@[^@\s]+\.[^@\s]+$")
def _validate_email(email: str) -> None:
"""Exit with an error if *email* doesn't look like a valid address."""
if not _EMAIL_RE.match(email):
print_error(err_console, f"Invalid email address: {email!r}")
raise typer.Exit(1)
def _email_login(
email: str,
code: str | None,
base_url: str,
) -> dict:
"""Run the email verification code login flow.
Returns the parsed JSON response from the verify endpoint.
The caller expects at minimum an ``api_key`` field.
"""
url = base_url.rstrip("/")
with httpx.Client(timeout=30.0) as client:
# If code is already provided, skip sending — user already has a code
if not code:
# Step 1: Request verification code
resp = client.post(
f"{url}/api/v1/auth/email_code/",
json={"email": email},
)
if resp.status_code == 429:
print_error(err_console, "Too many attempts. Try again in a few minutes.")
raise typer.Exit(1)
if resp.status_code != 200:
try:
detail = resp.json().get("error", resp.text)
except Exception:
detail = resp.text
print_error(err_console, f"Failed to send code: {detail}")
raise typer.Exit(1)
print_success(console, "Verification code sent! Check your email.")
# Step 2: Get code from user
if not sys.stdin.isatty():
print_error(
err_console,
"No --code provided and terminal is non-interactive.",
hint="Run: mem0 init --email <email> --code <code>",
)
raise typer.Exit(1)
console.print()
code = Prompt.ask(f" [{BRAND_COLOR}]Verification Code[/]")
if not code:
print_error(err_console, "Code is required.")
raise typer.Exit(1)
# Step 3: Verify code
resp = client.post(
f"{url}/api/v1/auth/email_code/verify/",
json={"email": email, "code": code.strip()},
)
if resp.status_code == 429:
print_error(err_console, "Too many attempts. Try again in a few minutes.")
raise typer.Exit(1)
if resp.status_code != 200:
try:
detail = resp.json().get("error", resp.text)
except Exception:
detail = resp.text
print_error(err_console, f"Verification failed: {detail}")
raise typer.Exit(1)
return resp.json()
def run_init(
*,
api_key: str | None = None,
user_id: str | None = None,
email: str | None = None,
code: str | None = None,
force: bool = False,
) -> None:
"""Interactive setup wizard for mem0 CLI.
When both *api_key* and *user_id* are supplied, all prompts are skipped
(non-interactive mode). When running in a non-TTY without the required
flags, an error message is printed.
"""
config = Mem0Config()
base_url = os.environ.get("MEM0_BASE_URL", config.platform.base_url or DEFAULT_BASE_URL)
if code and not email:
print_error(err_console, "--code requires --email.")
raise typer.Exit(1)
# Warn if an existing config with an API key would be overwritten
if not force and CONFIG_FILE.exists():
existing = load_config()
if existing.platform.api_key:
from mem0_cli.config import redact_key
console.print(
f"\n [{BRAND_COLOR}]Existing configuration found[/] "
f"[{DIM_COLOR}](API key: {redact_key(existing.platform.api_key)})[/]"
)
if sys.stdin.isatty():
confirm = typer.confirm(" Overwrite existing config? This cannot be undone.")
if not confirm:
print_info(console, "Cancelled. Use --force to skip this check.")
raise typer.Exit(0)
else:
print_error(
err_console,
"Existing config would be overwritten.",
hint="Use --force to overwrite.",
)
raise typer.Exit(1)
# ── Email login flow ──────────────────────────────────────────────
if email:
if api_key:
print_error(err_console, "Cannot use both --api-key and --email.")
raise typer.Exit(1)
email = email.strip().lower()
_validate_email(email)
print_banner(console)
console.print()
print_info(console, f"Logging in as {email}...\n")
result = _email_login(email, code, base_url)
api_key_val = result.get("api_key")
if not api_key_val:
print_error(err_console, "Auth succeeded but no API key was returned. Contact support.")
raise typer.Exit(1)
config.platform.api_key = api_key_val
config.platform.base_url = base_url
config.defaults.user_id = (
user_id or os.environ.get("USER") or os.environ.get("USERNAME") or "mem0-cli"
)
save_config(config)
console.print()
print_success(console, "Authenticated! Configuration saved to ~/.mem0/config.json")
console.print()
console.print(f" [{DIM_COLOR}]Get started:[/]")
console.print(f' [{DIM_COLOR}] mem0 add "I prefer dark mode"[/]')
console.print(f' [{DIM_COLOR}] mem0 search "preferences"[/]')
console.print()
return
# ── API key flow (existing) ───────────────────────────────────────
# Non-TTY: resolve defaults so partial flags work in pipelines / CI
if not sys.stdin.isatty():
if not api_key:
print_error(
err_console,
"Non-interactive terminal detected and --api-key is required.",
hint="Run: mem0 init --api-key <key> [--user-id <id>]",
)
raise typer.Exit(1)
user_id = user_id or os.environ.get("USER") or os.environ.get("USERNAME") or "mem0-cli"
# Fully non-interactive when both flags provided
if api_key and user_id:
config.platform.api_key = api_key
config.defaults.user_id = user_id
_validate_platform(config)
save_config(config)
print_success(console, "Configuration saved to ~/.mem0/config.json")
return
print_banner(console)
console.print()
print_info(console, "Welcome! Let's set up your mem0 CLI.\n")
# If no flags at all, ask user how they want to authenticate
if not api_key:
console.print(f" [{BRAND_COLOR}]How would you like to authenticate?[/]")
console.print(f" [{DIM_COLOR}]1.[/] Login with email [{DIM_COLOR}](recommended)[/]")
console.print(f" [{DIM_COLOR}]2.[/] Enter API key manually")
console.print()
choice = Prompt.ask(f" [{BRAND_COLOR}]Choose[/]", choices=["1", "2"], default="1")
if choice == "1":
console.print()
email_addr = Prompt.ask(f" [{BRAND_COLOR}]Email[/]")
if not email_addr:
print_error(err_console, "Email is required.")
raise typer.Exit(1)
email_addr = email_addr.strip().lower()
_validate_email(email_addr)
print_info(console, f"Logging in as {email_addr}...\n")
result = _email_login(email_addr, None, base_url)
api_key_val = result.get("api_key")
if not api_key_val:
print_error(
err_console, "Auth succeeded but no API key was returned. Contact support."
)
raise typer.Exit(1)
config.platform.api_key = api_key_val
config.platform.base_url = base_url
config.defaults.user_id = (
user_id or os.environ.get("USER") or os.environ.get("USERNAME") or "mem0-cli"
)
save_config(config)
console.print()
print_success(console, "Authenticated! Configuration saved to ~/.mem0/config.json")
console.print()
console.print(f" [{DIM_COLOR}]Get started:[/]")
console.print(f' [{DIM_COLOR}] mem0 add "I prefer dark mode"[/]')
console.print(f' [{DIM_COLOR}] mem0 search "preferences"[/]')
console.print()
return
# API key flow
if api_key:
config.platform.api_key = api_key
else:
_setup_platform(config)
if user_id:
config.defaults.user_id = user_id
else:
_setup_defaults(config)
_validate_platform(config)
save_config(config)
console.print()
print_success(console, "Configuration saved to ~/.mem0/config.json")
console.print()
console.print(f" [{DIM_COLOR}]Get started:[/]")
if config.defaults.user_id:
console.print(f' [{DIM_COLOR}] mem0 add "I prefer dark mode"[/]')
console.print(f' [{DIM_COLOR}] mem0 search "preferences"[/]')
else:
console.print(f' [{DIM_COLOR}] mem0 add "I prefer dark mode" --user-id alice[/]')
console.print(f' [{DIM_COLOR}] mem0 search "preferences" --user-id alice[/]')
console.print()
def _setup_platform(config: Mem0Config) -> None:
"""Platform setup flow."""
console.print()
console.print(f" [{DIM_COLOR}]Get your API key at https://app.mem0.ai/dashboard/api-keys[/]")
console.print()
console.print(f" [{BRAND_COLOR}]API Key[/]: ", end="")
api_key = _prompt_secret("")
if not api_key:
print_error(err_console, "API key is required.")
raise typer.Exit(1)
config.platform.api_key = api_key
def _setup_defaults(config: Mem0Config) -> None:
"""Collect default entity IDs."""
console.print()
print_info(console, "Set default entity IDs (press Enter to skip).\n")
_default_user = os.environ.get("USER") or os.environ.get("USERNAME") or "mem0-cli"
user_id = Prompt.ask(
f" [{BRAND_COLOR}]Default User ID[/] [{DIM_COLOR}](recommended)[/]",
default=_default_user,
)
if user_id:
config.defaults.user_id = user_id
def _validate_platform(config: Mem0Config) -> None:
"""Validate platform connection after all inputs are collected."""
console.print()
print_info(console, "Validating connection...")
try:
from mem0_cli.backend.platform import PlatformBackend
backend = PlatformBackend(config.platform)
status = backend.status(
user_id=config.defaults.user_id or None,
agent_id=config.defaults.agent_id or None,
)
if status.get("connected"):
print_success(console, "Connected to mem0 Platform!")
else:
print_error(
err_console,
f"Could not connect: {status.get('error', 'Unknown error')}",
hint="Visit https://app.mem0.ai/dashboard/api-keys to get a new key, then run mem0 init again.",
)
except Exception as e:
print_error(err_console, f"Connection test failed: {e}")
+677
View File
@@ -0,0 +1,677 @@
"""Memory CRUD commands: add, search, get, list, update, delete."""
from __future__ import annotations
import json
import os
import stat as _stat_mod
import sys
import time as _time
from pathlib import Path
import typer
from rich.console import Console
from mem0_cli.backend.base import Backend
from mem0_cli.branding import (
print_error,
print_info,
print_scope,
print_success,
timed_status,
)
from mem0_cli.output import (
format_add_result,
format_agent_envelope,
format_json,
format_memories_table,
format_memories_text,
format_single_memory,
print_result_summary,
)
console = Console()
err_console = Console(stderr=True)
def _stdin_is_piped() -> bool:
"""Return True only when stdin is an actual pipe or file redirect."""
from mem0_cli.state import is_agent_mode
if is_agent_mode():
return False
try:
mode = os.fstat(sys.stdin.fileno()).st_mode
return _stat_mod.S_ISFIFO(mode) or _stat_mod.S_ISREG(mode)
except Exception:
return False
def cmd_add(
backend: Backend,
text: str | None,
*,
user_id: str | None,
agent_id: str | None,
app_id: str | None,
run_id: str | None,
messages: str | None,
file: Path | None,
metadata: str | None,
immutable: bool,
no_infer: bool,
expires: str | None,
categories: str | None,
enable_graph: bool = False,
output: str = "text",
) -> None:
"""Add a memory."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("add")
if is_agent_mode():
output = "agent"
msgs = None
content = text
# Read from file
if file:
try:
raw = Path(file).read_text()
msgs = json.loads(raw)
except (FileNotFoundError, json.JSONDecodeError) as e:
print_error(err_console, f"Failed to read file: {e}")
raise typer.Exit(1) from None
# Parse messages JSON
elif messages:
try:
msgs = json.loads(messages)
except json.JSONDecodeError as e:
print_error(err_console, f"Invalid JSON in --messages: {e}")
raise typer.Exit(1) from None
# Read from stdin only if stdin is an actual pipe or file redirect
elif not content and _stdin_is_piped():
content = sys.stdin.read().strip()
if not content and not msgs:
print_error(
err_console, "No content provided. Pass text, --messages, --file, or pipe via stdin."
)
raise typer.Exit(1)
meta = None
if metadata:
try:
meta = json.loads(metadata)
except json.JSONDecodeError:
print_error(err_console, "Invalid JSON in --metadata.")
raise typer.Exit(1) from None
cats = None
if categories:
try:
cats = json.loads(categories)
except json.JSONDecodeError:
cats = [c.strip() for c in categories.split(",")]
# Validate --expires
if expires:
import re
if not re.match(r"^\d{4}-\d{2}-\d{2}$", expires):
print_error(
err_console, "Invalid date format for --expires. Use YYYY-MM-DD (e.g. 2025-12-31)."
)
raise typer.Exit(1)
from datetime import date
if date.fromisoformat(expires) <= date.today():
print_error(err_console, "--expires date must be in the future.")
raise typer.Exit(1)
with timed_status(err_console, "Adding memory...") as ts:
try:
result = backend.add(
content=content,
messages=msgs,
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
metadata=meta,
immutable=immutable,
infer=not no_infer,
expires=expires,
categories=cats,
enable_graph=enable_graph,
)
except Exception as e:
ts.error_msg = str(e)
raise typer.Exit(1) from None
if output == "quiet":
return
# Deduplicate PENDING entries sharing the same event_id across all output modes
results_list = result if isinstance(result, list) else result.get("results", [result])
seen_events: set[str] = set()
deduped: list[dict] = []
for r in results_list:
if r.get("status") == "PENDING":
eid = r.get("event_id", "")
if eid and eid in seen_events:
continue
if eid:
seen_events.add(eid)
deduped.append(r)
# Write back so downstream formatters see deduplicated data
if isinstance(result, dict) and "results" in result:
result = {**result, "results": deduped}
else:
result = deduped
if output == "agent":
scope = {
k: v
for k, v in {
"user_id": user_id,
"agent_id": agent_id,
"app_id": app_id,
"run_id": run_id,
}.items()
if v
}
format_agent_envelope(
console,
command="add",
data=deduped,
scope=scope or None,
count=len(deduped),
)
return
if output == "json":
format_add_result(console, result, output)
return
console.print()
print_scope(console, user_id=user_id, agent_id=agent_id, app_id=app_id, run_id=run_id)
count = len(deduped)
all_pending = count > 0 and all(r.get("status") == "PENDING" for r in deduped)
if all_pending:
print_success(
console,
f"Memory queued — {count} event{'s' if count != 1 else ''} pending",
)
else:
print_success(
console, f"Memory processed — {count} memor{'y' if count == 1 else 'ies'} extracted"
)
format_add_result(console, result, output)
def cmd_search(
backend: Backend,
query: str,
*,
user_id: str | None,
agent_id: str | None,
app_id: str | None,
run_id: str | None,
top_k: int,
threshold: float,
rerank: bool,
keyword: bool,
filter_json: str | None,
fields: str | None,
enable_graph: bool = False,
output: str = "text",
) -> None:
"""Search memories."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("search")
if is_agent_mode():
output = "agent"
filters = None
if filter_json:
try:
filters = json.loads(filter_json)
except json.JSONDecodeError:
print_error(err_console, "Invalid JSON in --filter.")
raise typer.Exit(1) from None
field_list = None
if fields:
field_list = [f.strip() for f in fields.split(",")]
if top_k < 1:
print_error(err_console, "--top-k must be >= 1.")
raise typer.Exit(1)
if not (0.0 <= threshold <= 1.0):
print_error(err_console, "--threshold must be between 0.0 and 1.0.")
raise typer.Exit(1)
_start = _time.perf_counter()
with timed_status(err_console, "Searching memories...") as _ts:
try:
results = backend.search(
query,
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
top_k=top_k,
threshold=threshold,
rerank=rerank,
keyword=keyword,
filters=filters,
fields=field_list,
enable_graph=enable_graph,
)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "quiet":
return
if output == "agent":
scope = {
k: v
for k, v in {
"user_id": user_id,
"agent_id": agent_id,
"app_id": app_id,
"run_id": run_id,
}.items()
if v
}
format_agent_envelope(
console,
command="search",
data=results,
scope=scope or None,
count=len(results),
duration_ms=int(_elapsed * 1000),
)
return
if output == "json":
format_json(console, results)
elif output == "table":
if results:
format_memories_table(console, results, show_score=True)
print_result_summary(
console, len(results), duration_secs=_elapsed, user_id=user_id, agent_id=agent_id
)
else:
console.print()
print_info(console, "No memories found matching your query.")
console.print()
else:
if results:
format_memories_text(console, results)
print_result_summary(
console, len(results), duration_secs=_elapsed, user_id=user_id, agent_id=agent_id
)
else:
console.print()
print_info(console, "No memories found matching your query.")
console.print()
def cmd_get(backend: Backend, memory_id: str, *, output: str) -> None:
"""Get a specific memory by ID."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("get")
if is_agent_mode():
output = "agent"
with timed_status(err_console, "Fetching memory...") as _ts:
try:
result = backend.get(memory_id)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
if output == "agent":
format_agent_envelope(console, command="get", data=result)
else:
format_single_memory(console, result, output)
def cmd_list(
backend: Backend,
*,
user_id: str | None,
agent_id: str | None,
app_id: str | None,
run_id: str | None,
page: int,
page_size: int,
category: str | None,
after: str | None,
before: str | None,
enable_graph: bool = False,
output: str = "table",
) -> None:
"""List memories."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("list")
if is_agent_mode():
output = "agent"
if page_size < 1:
print_error(err_console, "--page-size must be >= 1.")
raise typer.Exit(1)
if page < 1:
print_error(err_console, "--page must be >= 1.")
raise typer.Exit(1)
_start = _time.perf_counter()
with timed_status(err_console, "Listing memories...") as _ts:
try:
results = backend.list_memories(
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
page=page,
page_size=page_size,
category=category,
after=after,
before=before,
enable_graph=enable_graph,
)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "quiet":
return
if output in ("json", "agent"):
scope = {
k: v
for k, v in {
"user_id": user_id,
"agent_id": agent_id,
"app_id": app_id,
"run_id": run_id,
}.items()
if v
}
format_agent_envelope(
console,
command="list",
data=results,
scope=scope or None,
count=len(results),
duration_ms=int(_elapsed * 1000),
)
elif output == "table":
if results:
format_memories_table(console, results)
print_result_summary(
console,
len(results),
duration_secs=_elapsed,
page=page,
user_id=user_id,
agent_id=agent_id,
)
else:
console.print()
print_info(console, "No memories found.")
console.print()
else:
if results:
format_memories_text(console, results, title="memories")
print_result_summary(
console,
len(results),
duration_secs=_elapsed,
page=page,
user_id=user_id,
agent_id=agent_id,
)
else:
console.print()
print_info(console, "No memories found.")
console.print()
def cmd_update(
backend: Backend,
memory_id: str,
text: str | None,
*,
metadata: str | None,
output: str,
) -> None:
"""Update a memory."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("update")
if is_agent_mode():
output = "agent"
meta = None
if metadata:
try:
meta = json.loads(metadata)
except json.JSONDecodeError:
print_error(err_console, "Invalid JSON in --metadata.")
raise typer.Exit(1) from None
_start = _time.perf_counter()
with timed_status(err_console, "Updating memory...") as _ts:
try:
result = backend.update(memory_id, content=text, metadata=meta)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "agent":
format_agent_envelope(
console,
command="update",
data=result,
duration_ms=int(_elapsed * 1000),
)
elif output == "json":
format_json(console, result)
elif output != "quiet":
print_success(console, f"Memory {memory_id[:8]} updated ({_elapsed:.2f}s)")
def cmd_delete(
backend: Backend,
memory_id: str,
*,
dry_run: bool = False,
force: bool = False,
output: str,
) -> None:
"""Delete a single memory by ID."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("delete")
if is_agent_mode():
output = "agent"
if dry_run:
# Fetch and display what would be deleted
try:
mem = backend.get(memory_id)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
format_single_memory(console, mem, output)
print_info(console, "No changes made (dry run).")
return
_start = _time.perf_counter()
with timed_status(err_console, "Deleting...") as _ts:
try:
result = backend.delete(memory_id=memory_id)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "agent":
format_agent_envelope(
console,
command="delete",
data={"id": memory_id, "deleted": True},
duration_ms=int(_elapsed * 1000),
)
elif output == "json":
format_json(console, result)
elif output != "quiet":
print_success(console, f"Memory {memory_id[:8]} deleted ({_elapsed:.2f}s)")
def cmd_delete_all(
backend: Backend,
*,
force: bool,
dry_run: bool = False,
all_: bool = False,
user_id: str | None,
agent_id: str | None,
app_id: str | None,
run_id: str | None,
output: str,
) -> None:
"""Delete all memories matching a scope."""
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("delete-all")
if is_agent_mode():
output = "agent"
if not force:
print_error(err_console, "Destructive operation requires --force in agent mode.")
raise typer.Exit(1)
if all_:
# Project-wide wipe using wildcard entity IDs
# Note: --dry-run is ignored here because the API has no count-before-delete endpoint.
if not force:
confirm = typer.confirm(
"\n ⚠ Delete ALL memories across the ENTIRE project? This cannot be undone."
)
if not confirm:
print_info(console, "Cancelled.")
raise typer.Exit(0)
_start = _time.perf_counter()
with timed_status(err_console, "Deleting all memories project-wide...") as _ts:
try:
result = backend.delete(
all=True,
user_id="*",
agent_id="*",
app_id="*",
run_id="*",
)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
if output == "agent":
format_agent_envelope(
console,
command="delete-all",
data={"deleted": True, "scope": "project"},
duration_ms=int(_elapsed * 1000),
)
elif output == "json":
format_json(console, result)
elif output != "quiet":
if isinstance(result, dict) and "message" in result:
print_info(console, "Deletion started. Memories will be removed in the background.")
else:
print_success(console, f"All project memories deleted ({_elapsed:.2f}s)")
return
if dry_run:
# List matching memories and show count
try:
results = backend.list_memories(
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
count = len(results)
print_info(console, f"Would delete {count} memor{'y' if count == 1 else 'ies'}.")
print_info(console, "No changes made (dry run).")
return
if not force:
scope_parts = []
if user_id:
scope_parts.append(f"user={user_id}")
if agent_id:
scope_parts.append(f"agent={agent_id}")
if app_id:
scope_parts.append(f"app={app_id}")
if run_id:
scope_parts.append(f"run={run_id}")
scope = ", ".join(scope_parts) if scope_parts else "ALL entities"
confirm = typer.confirm(f"\n ⚠ Delete ALL memories for {scope}? This cannot be undone.")
if not confirm:
print_info(console, "Cancelled.")
raise typer.Exit(0)
_start = _time.perf_counter()
with timed_status(err_console, "Deleting all memories...") as _ts:
try:
result = backend.delete(
all=True,
user_id=user_id,
agent_id=agent_id,
app_id=app_id,
run_id=run_id,
)
except Exception as e:
print_error(err_console, str(e))
raise typer.Exit(1) from None
_elapsed = _time.perf_counter() - _start
scope = {
k: v
for k, v in {
"user_id": user_id,
"agent_id": agent_id,
"app_id": app_id,
"run_id": run_id,
}.items()
if v
}
if output == "agent":
format_agent_envelope(
console,
command="delete-all",
data={"deleted": True},
scope=scope or None,
duration_ms=int(_elapsed * 1000),
)
elif output == "json":
format_json(console, result)
elif output != "quiet":
if isinstance(result, dict) and "message" in result:
print_info(console, "Deletion started. Memories will be removed in the background.")
else:
print_success(console, f"All matching memories deleted ({_elapsed:.2f}s)")
+162
View File
@@ -0,0 +1,162 @@
"""Utility commands: status, version, import."""
from __future__ import annotations
import json
import time as _time
from pathlib import Path
import typer
from rich.console import Console
from rich.panel import Panel
from rich.progress import track
from mem0_cli import __version__
from mem0_cli.backend.base import Backend
from mem0_cli.branding import (
BRAND_COLOR,
DIM_COLOR,
ERROR_COLOR,
SUCCESS_COLOR,
print_error,
print_success,
timed_status,
)
console = Console()
err_console = Console(stderr=True)
def cmd_status(
backend: Backend,
*,
user_id: str | None = None,
agent_id: str | None = None,
output: str = "text",
) -> None:
"""Check connectivity and auth."""
from mem0_cli.output import format_agent_envelope
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("status")
if is_agent_mode():
output = "agent"
_start = _time.perf_counter()
with timed_status(err_console, "Checking connection...") as _ts:
result = backend.status(user_id=user_id, agent_id=agent_id)
_elapsed = _time.perf_counter() - _start
if output in ("json", "agent"):
format_agent_envelope(
console,
command="status",
data={
"connected": result.get("connected", False),
"backend": result.get("backend", "?"),
"base_url": result.get("base_url", ""),
},
duration_ms=int(_elapsed * 1000),
)
return
lines = []
if result.get("connected"):
lines.append(f" [{SUCCESS_COLOR}]●[/] Connected")
else:
lines.append(f" [{ERROR_COLOR}]●[/] Disconnected")
lines.append(f" [{DIM_COLOR}]Backend:[/] {result.get('backend', '?')}")
if result.get("base_url"):
lines.append(f" [{DIM_COLOR}]API URL:[/] {result['base_url']}")
if result.get("error"):
lines.append(f" [{ERROR_COLOR}]Error:[/] {result['error']}")
if "Authentication failed" in str(result["error"]):
lines.append("")
lines.append(
f" [{DIM_COLOR}]Run [bold]mem0 init[/bold] to reconfigure your API key[/]"
)
lines.append(
f" [{DIM_COLOR}]Get a key at [bold]https://app.mem0.ai/dashboard/api-keys[/bold][/]"
)
lines.append(f" [{DIM_COLOR}]Latency:[/] {_elapsed:.2f}s")
content = "\n".join(lines)
panel = Panel(
content,
title=f"[{BRAND_COLOR}]Connection Status[/]",
title_align="left",
border_style=BRAND_COLOR,
padding=(1, 1),
)
console.print()
console.print(panel)
console.print()
def cmd_version() -> None:
"""Show version."""
console.print(f" [{BRAND_COLOR}]◆ Mem0[/] CLI v{__version__}")
def cmd_import(
backend: Backend,
file_path: str,
*,
user_id: str | None,
agent_id: str | None,
output: str = "text",
) -> None:
"""Import memories from a JSON file."""
from mem0_cli.output import format_agent_envelope
from mem0_cli.state import is_agent_mode, set_current_command
set_current_command("import")
if is_agent_mode():
output = "agent"
try:
data = json.loads(Path(file_path).read_text())
except (FileNotFoundError, json.JSONDecodeError) as e:
print_error(err_console, f"Failed to read file: {e}")
raise typer.Exit(1) from None
if not isinstance(data, list):
data = [data]
added = 0
failed = 0
_start = _time.perf_counter()
for item in track(
data, description=f"[{DIM_COLOR}]Importing memories...[/]", console=err_console
):
content = item.get("memory", item.get("text", item.get("content", "")))
if not content:
failed += 1
continue
try:
backend.add(
content=content,
user_id=user_id or item.get("user_id"),
agent_id=agent_id or item.get("agent_id"),
metadata=item.get("metadata"),
)
added += 1
except Exception:
failed += 1
_elapsed = _time.perf_counter() - _start
if output in ("json", "agent"):
scope = {k: v for k, v in {"user_id": user_id, "agent_id": agent_id}.items() if v}
format_agent_envelope(
console,
command="import",
data={"added": added, "failed": failed},
scope=scope or None,
duration_ms=int(_elapsed * 1000),
)
return
print_success(err_console, f"Imported {added} memories ({_elapsed:.2f}s)")
if failed:
print_error(err_console, f"{failed} memories failed to import.")
+189
View File
@@ -0,0 +1,189 @@
"""Configuration management for mem0 CLI.
Config precedence (highest to lowest):
1. CLI flags (--api-key, --base-url, etc.)
2. Environment variables (MEM0_API_KEY, etc.)
3. Config file (~/.mem0/config.json)
4. Defaults
"""
from __future__ import annotations
import json
import os
import stat
from dataclasses import dataclass, field
from pathlib import Path
from typing import Any
CONFIG_DIR = Path.home() / ".mem0"
CONFIG_FILE = CONFIG_DIR / "config.json"
DEFAULT_BASE_URL = "https://api.mem0.ai"
CONFIG_VERSION = 1
@dataclass
class PlatformConfig:
api_key: str = ""
base_url: str = DEFAULT_BASE_URL
@dataclass
class DefaultsConfig:
user_id: str = ""
agent_id: str = ""
app_id: str = ""
run_id: str = ""
enable_graph: bool = False
@dataclass
class Mem0Config:
version: int = CONFIG_VERSION
defaults: DefaultsConfig = field(default_factory=DefaultsConfig)
platform: PlatformConfig = field(default_factory=PlatformConfig)
SHORT_KEY_ALIASES: dict[str, str] = {
"api_key": "platform.api_key",
"base_url": "platform.base_url",
"user_id": "defaults.user_id",
"agent_id": "defaults.agent_id",
"app_id": "defaults.app_id",
"run_id": "defaults.run_id",
"enable_graph": "defaults.enable_graph",
}
def ensure_config_dir() -> Path:
"""Create ~/.mem0 directory with secure permissions if it doesn't exist."""
CONFIG_DIR.mkdir(parents=True, exist_ok=True)
os.chmod(CONFIG_DIR, stat.S_IRWXU) # 0700
return CONFIG_DIR
def load_config() -> Mem0Config:
"""Load config from file, applying env var overrides."""
config = Mem0Config()
if CONFIG_FILE.exists():
with open(CONFIG_FILE) as f:
data = json.load(f)
config.version = data.get("version", CONFIG_VERSION)
plat = data.get("platform", {})
config.platform.api_key = plat.get("api_key", "")
config.platform.base_url = plat.get("base_url", DEFAULT_BASE_URL)
defaults = data.get("defaults", {})
config.defaults.user_id = defaults.get("user_id", "")
config.defaults.agent_id = defaults.get("agent_id", "")
config.defaults.app_id = defaults.get("app_id", "")
config.defaults.run_id = defaults.get("run_id", "")
config.defaults.enable_graph = defaults.get("enable_graph", False)
# Environment variable overrides
env_key = os.environ.get("MEM0_API_KEY")
if env_key:
config.platform.api_key = env_key
env_base = os.environ.get("MEM0_BASE_URL")
if env_base:
config.platform.base_url = env_base
env_user_id = os.environ.get("MEM0_USER_ID")
if env_user_id:
config.defaults.user_id = env_user_id
env_agent_id = os.environ.get("MEM0_AGENT_ID")
if env_agent_id:
config.defaults.agent_id = env_agent_id
env_app_id = os.environ.get("MEM0_APP_ID")
if env_app_id:
config.defaults.app_id = env_app_id
env_run_id = os.environ.get("MEM0_RUN_ID")
if 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
def save_config(config: Mem0Config) -> None:
"""Write config to disk with secure permissions."""
ensure_config_dir()
data: dict[str, Any] = {
"version": config.version,
"defaults": {
"user_id": config.defaults.user_id,
"agent_id": config.defaults.agent_id,
"app_id": config.defaults.app_id,
"run_id": config.defaults.run_id,
"enable_graph": config.defaults.enable_graph,
},
"platform": {
"api_key": config.platform.api_key,
"base_url": config.platform.base_url,
},
}
with open(CONFIG_FILE, "w") as f:
json.dump(data, f, indent=2)
os.chmod(CONFIG_FILE, stat.S_IRUSR | stat.S_IWUSR) # 0600
def redact_key(key: str) -> str:
"""Redact an API key for display: m0-xxx...xxx"""
if not key:
return "(not set)"
if len(key) <= 8:
return key[:2] + "***"
return key[:4] + "..." + key[-4:]
def get_nested_value(config: Mem0Config, dotted_key: str) -> Any:
"""Get a config value by dotted path, e.g. 'platform.api_key' or short form 'api_key'."""
dotted_key = SHORT_KEY_ALIASES.get(dotted_key, dotted_key)
parts = dotted_key.split(".")
obj: Any = config
for part in parts:
if hasattr(obj, part):
obj = getattr(obj, part)
else:
return None
return obj
def set_nested_value(config: Mem0Config, dotted_key: str, value: str) -> bool:
"""Set a config value by dotted path. Returns True on success."""
dotted_key = SHORT_KEY_ALIASES.get(dotted_key, dotted_key)
parts = dotted_key.split(".")
obj: Any = config
for part in parts[:-1]:
if hasattr(obj, part):
obj = getattr(obj, part)
else:
return False
final_key = parts[-1]
if not hasattr(obj, final_key):
return False
current = getattr(obj, final_key)
# Type coercion
if isinstance(current, bool):
value = value.lower() in ("true", "1", "yes") # type: ignore[assignment]
elif isinstance(current, int):
value = int(value) # type: ignore[assignment]
setattr(obj, final_key, value)
return True
+359
View File
@@ -0,0 +1,359 @@
"""Output formatting for mem0 CLI — text, JSON, table, quiet modes."""
from __future__ import annotations
import json
from datetime import datetime
from typing import Any
from rich.console import Console
from rich.panel import Panel
from rich.table import Table
from rich.text import Text
from mem0_cli.branding import ACCENT_COLOR, BRAND_COLOR, DIM_COLOR, SUCCESS_COLOR, _sym
def format_memories_text(console: Console, memories: list[dict], title: str = "memories") -> None:
"""Render memories in human-friendly text mode."""
count = len(memories)
console.print(f"\n[{BRAND_COLOR}]Found {count} {title}:[/]\n")
for i, mem in enumerate(memories, 1):
memory_text = mem.get("memory", mem.get("text", ""))
mem_id = mem.get("id", "")[:8]
score = mem.get("score")
created = _format_date(mem.get("created_at"))
category = mem.get("categories", [None])
if isinstance(category, list):
category = category[0] if category else None
line = Text()
line.append(f" {i}. ", style="bold")
line.append(memory_text, style="white")
console.print(line)
details = []
if score is not None:
details.append(f"Score: {score:.2f}")
if mem_id:
details.append(f"ID: {mem_id}")
if created:
details.append(f"Created: {created}")
if category:
details.append(f"Category: {category}")
if details:
detail_str = " · ".join(details)
console.print(f" [{DIM_COLOR}]{detail_str}[/]")
console.print()
def format_memories_table(
console: Console, memories: list[dict], *, show_score: bool = False
) -> None:
"""Render memories in a rich table."""
table = Table(
border_style=BRAND_COLOR,
header_style=f"bold {ACCENT_COLOR}",
row_styles=["", "dim"],
padding=(0, 1),
)
table.add_column("ID", style="dim", max_width=38, no_wrap=True)
if show_score:
table.add_column("Score", max_width=7, justify="right")
table.add_column("Memory", max_width=50, no_wrap=False)
table.add_column("Category", max_width=14)
table.add_column("Created", max_width=12)
for mem in memories:
mem_id = mem.get("id", "")
memory_text = mem.get("memory", mem.get("text", ""))
if len(memory_text) > 60:
memory_text = memory_text[:57] + "..."
categories = mem.get("categories", [])
if isinstance(categories, list) and categories:
cat = (
categories[0]
if len(categories) == 1
else f"{categories[0]} (+{len(categories) - 1})"
)
else:
cat = "—"
created = _format_date(mem.get("created_at")) or "—"
if show_score:
score = mem.get("score")
score_str = f"{score:.2f}" if score is not None else "—"
table.add_row(mem_id, score_str, memory_text, cat, created)
else:
table.add_row(mem_id, memory_text, cat, created)
console.print()
console.print(table)
console.print()
def format_json(console: Console, data: Any) -> None:
"""Output data as pretty-printed JSON."""
console.print_json(json.dumps(data, default=str))
def format_single_memory(console: Console, mem: dict, output: str = "text") -> None:
"""Format a single memory for display."""
if output == "json":
format_json(console, mem)
return
memory_text = mem.get("memory", mem.get("text", ""))
mem_id = mem.get("id", "")
lines = []
lines.append(f" [white bold]{memory_text}[/]")
lines.append("")
if mem_id:
lines.append(f" [{DIM_COLOR}]ID:[/] {mem_id}")
created = _format_date(mem.get("created_at"))
if created:
lines.append(f" [{DIM_COLOR}]Created:[/] {created}")
updated = _format_date(mem.get("updated_at"))
if updated:
lines.append(f" [{DIM_COLOR}]Updated:[/] {updated}")
meta = mem.get("metadata")
if meta:
lines.append(f" [{DIM_COLOR}]Metadata:[/] {json.dumps(meta)}")
categories = mem.get("categories")
if categories:
cat_str = ", ".join(categories) if isinstance(categories, list) else categories
lines.append(f" [{DIM_COLOR}]Categories:[/] {cat_str}")
content = "\n".join(lines)
panel = Panel(
content,
title=f"[{BRAND_COLOR}]Memory[/]",
title_align="left",
border_style=BRAND_COLOR,
padding=(1, 1),
)
console.print()
console.print(panel)
console.print()
def format_add_result(console: Console, result: dict | list, output: str = "text") -> None:
"""Format the result of an add operation."""
if output == "json":
format_json(console, result)
return
if output == "quiet":
return
# result from API is typically {"results": [...]}
results = result if isinstance(result, list) else result.get("results", [result])
if not results:
console.print(f" [{DIM_COLOR}]No memories extracted.[/]")
return
console.print()
seen_pending_events: set[str] = set()
for r in results:
# Detect async PENDING response from Platform API
if r.get("status") == "PENDING":
event_id = r.get("event_id", "")
# Deduplicate PENDING entries with the same event_id
if event_id and event_id in seen_pending_events:
continue
if event_id:
seen_pending_events.add(event_id)
icon = f"[{ACCENT_COLOR}]{_sym('⧗', '...')}[/]"
parts = [f" {icon} [{DIM_COLOR}]{'Queued':<10}[/]"]
parts.append("[white]Processing in background[/]")
console.print(" ".join(parts))
if event_id:
console.print(f" [{DIM_COLOR}] event_id: {event_id}[/]")
console.print(f" [{DIM_COLOR}] → Check status: mem0 event status {event_id}[/]")
continue
event = r.get("event", "ADD")
memory = r.get("memory") or r.get("text") or r.get("content") or r.get("data") or ""
mem_id = (r.get("id") or r.get("memory_id") or "")[:8]
if event == "ADD":
icon = f"[{SUCCESS_COLOR}]+[/]"
label = "Added"
elif event == "UPDATE":
icon = f"[{ACCENT_COLOR}]~[/]"
label = "Updated"
elif event == "DELETE":
icon = "[red]-[/]"
label = "Deleted"
elif event == "NOOP":
icon = f"[{DIM_COLOR}]·[/]"
label = "No change"
else:
icon = f"[{DIM_COLOR}]?[/]"
label = event
# Build the display line
parts = [f" {icon} [{DIM_COLOR}]{label:<10}[/]"]
if memory:
parts.append(f"[white]{memory}[/]")
if mem_id:
parts.append(f"[{DIM_COLOR}]({mem_id})[/]")
console.print(" ".join(parts))
console.print()
def format_json_envelope(
console: Console,
*,
command: str,
data: Any,
duration_ms: int | None = None,
scope: dict | None = None,
count: int | None = None,
status: str = "success",
error: str | None = None,
) -> None:
"""Output structured JSON envelope for AI agent consumption."""
envelope: dict[str, Any] = {
"status": status,
"command": command,
}
if duration_ms is not None:
envelope["duration_ms"] = duration_ms
if scope is not None:
envelope["scope"] = scope
if count is not None:
envelope["count"] = count
if error:
envelope["error"] = error
envelope["data"] = data
console.print_json(json.dumps(envelope, default=str))
def sanitize_agent_data(command: str, data: Any) -> Any:
"""Project API response data to minimal relevant fields for agent consumption."""
def pick(obj: dict, keys: list) -> dict:
return {k: obj[k] for k in keys if k in obj}
if data is None:
return data
if command == "add":
items = data if isinstance(data, list) else [data]
result = []
for item in items:
if item.get("status") == "PENDING":
result.append(pick(item, ["status", "event_id"]))
else:
result.append(pick(item, ["id", "memory", "event"]))
return result
if command == "search":
return [pick(r, ["id", "memory", "score", "created_at", "categories"]) for r in data]
if command == "list":
return [pick(r, ["id", "memory", "created_at", "categories"]) for r in data]
if command == "get":
return pick(data, ["id", "memory", "created_at", "updated_at", "categories", "metadata"])
if command == "update":
return pick(data, ["id", "memory"])
if command in ("delete", "delete-all", "entity delete"):
return data
if command == "entity list":
result = []
for r in data:
item = pick(r, ["type", "count"])
item["name"] = r.get("name") or r.get("id", "")
result.append(item)
return result
if command == "event list":
return [pick(r, ["id", "event_type", "status", "latency", "created_at"]) for r in data]
if command == "event status":
ev = data
raw_results = ev.get("results") or []
sanitized_results = []
for r in raw_results:
nested = r.get("data") or {}
memory = nested.get("memory") if isinstance(nested, dict) else None
sanitized_results.append(
{
"id": r.get("id"),
"event": r.get("event"),
"user_id": r.get("user_id"),
"memory": memory,
}
)
result = pick(ev, ["id", "event_type", "status", "latency", "created_at", "updated_at"])
result["results"] = sanitized_results
return result
# Pass-through: status, import, config show/get/set
return data
def format_agent_envelope(
console: Console,
*,
command: str,
data: Any,
duration_ms: int | None = None,
scope: dict | None = None,
count: int | None = None,
) -> None:
"""Output structured JSON envelope for agent/programmatic use (--json/--agent mode)."""
envelope: dict[str, Any] = {
"status": "success",
"command": command,
}
if duration_ms is not None:
envelope["duration_ms"] = duration_ms
if scope:
filtered = {k: v for k, v in scope.items() if v}
if filtered:
envelope["scope"] = filtered
if count is not None:
envelope["count"] = count
envelope["data"] = sanitize_agent_data(command, data)
console.print_json(json.dumps(envelope, default=str))
def print_result_summary(
console: Console,
count: int,
*,
duration_secs: float | None = None,
page: int | None = None,
**scope_ids: str | None,
) -> None:
"""Print a summary footer after result lists."""
parts = [f"{count} result{'s' if count != 1 else ''}"]
if page is not None:
parts.append(f"page {page}")
scope_parts = [f"{k}={v}" for k, v in scope_ids.items() if v]
if scope_parts:
parts.append(", ".join(scope_parts))
if duration_secs is not None:
parts.append(f"{duration_secs:.2f}s")
summary = " · ".join(parts)
console.print(f" [{DIM_COLOR}]{summary}[/]")
console.print()
def _format_date(dt_str: str | None) -> str | None:
if not dt_str:
return None
try:
dt = datetime.fromisoformat(dt_str.replace("Z", "+00:00"))
return dt.strftime("%Y-%m-%d")
except (ValueError, AttributeError):
return str(dt_str)[:10] if dt_str else None
+24
View File
@@ -0,0 +1,24 @@
"""Agent mode state — set by the root callback, read by commands and branding."""
from __future__ import annotations
_agent_mode: bool = False
_current_command: str = ""
def is_agent_mode() -> bool:
return _agent_mode
def set_agent_mode(val: bool) -> None:
global _agent_mode
_agent_mode = val
def get_current_command() -> str:
return _current_command
def set_current_command(name: str) -> None:
global _current_command
_current_command = name
+146
View File
@@ -0,0 +1,146 @@
"""Shared fixtures for mem0 CLI tests."""
from __future__ import annotations
import os
from unittest.mock import MagicMock
import pytest
from mem0_cli.backend.base import Backend
from mem0_cli.config import Mem0Config
@pytest.fixture(autouse=True)
def isolate_config(tmp_path, monkeypatch):
"""Redirect config to a temp directory so tests don't touch real config."""
fake_config_dir = tmp_path / ".mem0"
fake_config_file = fake_config_dir / "config.json"
monkeypatch.setattr("mem0_cli.config.CONFIG_DIR", fake_config_dir)
monkeypatch.setattr("mem0_cli.config.CONFIG_FILE", fake_config_file)
# Also patch the commands that import config
monkeypatch.setattr("mem0_cli.commands.config_cmd.CONFIG_DIR", fake_config_dir, raising=False)
# Clear any MEM0 env vars
for key in list(os.environ.keys()):
if key.startswith("MEM0_"):
monkeypatch.delenv(key, raising=False)
return fake_config_dir
@pytest.fixture
def mock_backend():
"""Return a mock backend with all methods stubbed."""
backend = MagicMock(spec=Backend)
# Default return values
backend.add.return_value = {
"results": [
{
"id": "abc-123-def-456",
"memory": "User prefers dark mode",
"event": "ADD",
}
]
}
backend.search.return_value = [
{
"id": "abc-123-def-456",
"memory": "User prefers dark mode",
"score": 0.92,
"created_at": "2026-02-15T10:30:00Z",
"categories": ["preferences"],
},
{
"id": "ghi-789-jkl-012",
"memory": "User uses vim keybindings",
"score": 0.78,
"created_at": "2026-03-01T14:00:00Z",
"categories": ["tools"],
},
]
backend.get.return_value = {
"id": "abc-123-def-456",
"memory": "User prefers dark mode",
"created_at": "2026-02-15T10:30:00Z",
"updated_at": "2026-02-20T08:00:00Z",
"metadata": {"source": "onboarding"},
"categories": ["preferences"],
}
backend.list_memories.return_value = [
{
"id": "abc-123-def-456",
"memory": "User prefers dark mode",
"created_at": "2026-02-15T10:30:00Z",
"categories": ["preferences"],
},
{
"id": "ghi-789-jkl-012",
"memory": "User uses vim keybindings",
"created_at": "2026-03-01T14:00:00Z",
"categories": ["tools"],
},
]
backend.update.return_value = {"id": "abc-123-def-456", "memory": "Updated memory"}
backend.delete.return_value = {"status": "deleted"}
backend.status.return_value = {
"connected": True,
"backend": "platform",
"base_url": "https://api.mem0.ai",
}
backend.delete_entities.return_value = {"message": "Entity deleted"}
backend.entities.return_value = [
{"name": "alice", "count": 5},
{"name": "bob", "count": 3},
]
backend.list_events.return_value = [
{
"id": "evt-abc-123-def-456",
"event_type": "ADD",
"status": "SUCCEEDED",
"graph_status": None,
"latency": 1234.5,
"created_at": "2026-04-01T10:00:00Z",
"updated_at": "2026-04-01T10:00:01Z",
},
{
"id": "evt-def-456-ghi-789",
"event_type": "SEARCH",
"status": "PENDING",
"graph_status": None,
"latency": None,
"created_at": "2026-04-01T10:01:00Z",
"updated_at": "2026-04-01T10:01:00Z",
},
]
backend.get_event.return_value = {
"id": "evt-abc-123-def-456",
"event_type": "ADD",
"status": "SUCCEEDED",
"graph_status": "SUCCEEDED",
"latency": 1234.5,
"created_at": "2026-04-01T10:00:00Z",
"updated_at": "2026-04-01T10:00:01Z",
"results": [
{
"id": "mem-abc-123",
"event": "ADD",
"user_id": "alice",
"data": {"memory": "User prefers dark mode"},
}
],
}
return backend
@pytest.fixture
def sample_config():
"""Return a sample config object."""
config = Mem0Config()
config.platform.api_key = "m0-test-key-12345678"
config.platform.base_url = "https://api.mem0.ai"
return config
+54
View File
@@ -0,0 +1,54 @@
"""Tests for branding and output helpers."""
from __future__ import annotations
from io import StringIO
from rich.console import Console
from mem0_cli.branding import print_banner, print_error, print_info, print_success, print_warning
def _make_console() -> tuple[Console, StringIO]:
buf = StringIO()
return Console(file=buf, force_terminal=False, no_color=True, width=80), buf
class TestBranding:
def test_print_banner(self):
console, buf = _make_console()
print_banner(console)
output = buf.getvalue()
# Banner contains the mem0 ASCII art and tagline
assert "Memory Layer" in output or "mem" in output.lower()
def test_print_success(self):
console, buf = _make_console()
print_success(console, "It worked!")
output = buf.getvalue()
assert "It worked!" in output
def test_print_error(self):
console, buf = _make_console()
print_error(console, "Something failed", hint="Try this fix")
output = buf.getvalue()
assert "Something failed" in output
assert "Try this fix" in output
def test_print_error_no_hint(self):
console, buf = _make_console()
print_error(console, "Failed")
output = buf.getvalue()
assert "Failed" in output
def test_print_warning(self):
console, buf = _make_console()
print_warning(console, "Watch out")
output = buf.getvalue()
assert "Watch out" in output
def test_print_info(self):
console, buf = _make_console()
print_info(console, "FYI")
output = buf.getvalue()
assert "FYI" in output
+263
View File
@@ -0,0 +1,263 @@
"""Integration tests — invoke CLI as subprocess to test end-to-end.
These tests launch the CLI as a real subprocess, so they must manage
environment isolation themselves (monkeypatch doesn't cross process
boundaries).
"""
from __future__ import annotations
import os
import re
import subprocess
import sys
import pytest
_ANSI_RE = re.compile(r"\x1b\[[0-9;]*[mKJHABCDfsu]")
def _strip_ansi(text: str) -> str:
"""Remove ANSI escape codes so substring checks work regardless of color mode."""
return _ANSI_RE.sub("", text)
def _run(
args: list[str],
env_override: dict | None = None,
home_dir: str | None = None,
) -> subprocess.CompletedProcess:
"""Run mem0 CLI command and capture output.
Args:
args: CLI arguments.
env_override: Extra env vars to set.
home_dir: If provided, set HOME to this path so the subprocess
reads config from ``<home_dir>/.mem0/config.json`` instead
of the user's real config. This is critical for tests that
depend on a clean (no API key) or custom config state.
Returns a CompletedProcess whose stdout/stderr have ANSI escape codes
stripped. GitHub Actions sets FORCE_COLOR=1 which causes Rich/Typer to
fragment option names like --user-id into separately-styled ANSI segments,
making plain ``in`` checks fail. Stripping here is version-agnostic and
ensures all assertions see the same plain text regardless of terminal env.
"""
env = os.environ.copy()
# Strip all MEM0_ env vars so tests start clean
for key in list(env.keys()):
if key.startswith("MEM0_"):
del env[key]
env.pop("FORCE_COLOR", None)
if home_dir:
env["HOME"] = home_dir
if env_override:
env.update(env_override)
result = subprocess.run(
[sys.executable, "-m", "mem0_cli", *args],
capture_output=True,
text=True,
env=env,
)
return subprocess.CompletedProcess(
args=result.args,
returncode=result.returncode,
stdout=_strip_ansi(result.stdout),
stderr=_strip_ansi(result.stderr),
)
@pytest.fixture
def clean_home(tmp_path):
"""Return a temp directory to use as HOME, ensuring no ~/.mem0 exists."""
return str(tmp_path)
class TestCLIIntegration:
"""Tests that only inspect help text / version — no config needed."""
def test_help(self):
result = _run(["--help"])
assert result.returncode == 0
assert "mem0" in result.stdout
assert "add" in result.stdout
assert "search" in result.stdout
def test_version_flag(self):
result = _run(["--version"])
assert result.returncode == 0
assert "0.1.0" in result.stdout
def test_add_help(self):
result = _run(["add", "--help"])
assert result.returncode == 0
assert "user-id" in result.stdout
assert "messages" in result.stdout
def test_add_help_has_scope_panel(self):
"""Verify rich_help_panel grouping shows in help output."""
result = _run(["add", "--help"])
assert result.returncode == 0
assert "Scope" in result.stdout
def test_search_help(self):
result = _run(["search", "--help"])
assert result.returncode == 0
assert "top-k" in result.stdout
def test_list_help(self):
result = _run(["list", "--help"])
assert result.returncode == 0
assert "page-size" in result.stdout
def test_delete_help(self):
result = _run(["delete", "--help"])
assert result.returncode == 0
assert "--all" in result.stdout
assert "--entity" in result.stdout
assert "--project" in result.stdout
assert "--force" in result.stdout
assert "--dry-run" in result.stdout
def test_entity_list_help(self):
result = _run(["entity", "list", "--help"])
assert result.returncode == 0
assert "entity-type" in result.stdout.lower() or "entity_type" in result.stdout.lower()
def test_entity_delete_help(self):
result = _run(["entity", "delete", "--help"])
assert result.returncode == 0
assert "--user-id" in result.stdout
assert "--force" in result.stdout
def test_import_help(self):
result = _run(["import", "--help"])
assert result.returncode == 0
def test_no_args_shows_help(self):
"""no_args_is_help=True makes Typer print help and exit with code 2."""
result = _run([])
# Typer returns exit code 2 for "no command given" — this is standard
# Click/Typer behaviour and not an error.
assert result.returncode in (0, 2)
assert "Usage" in result.stdout
class TestCLIIsolated:
"""Tests that need a clean HOME to avoid reading the user's real config."""
def test_add_no_key_errors(self, clean_home):
"""Without an API key, `mem0 add` must fail with a helpful message."""
result = _run(
["add", "test", "--user-id", "alice"],
home_dir=clean_home,
)
assert result.returncode != 0
combined = result.stderr + result.stdout
assert "API key" in combined or "api" in combined.lower() or "Error" in combined
def test_search_no_key_errors(self, clean_home):
"""Without an API key, `mem0 search` must fail."""
result = _run(
["search", "preferences", "--user-id", "alice"],
home_dir=clean_home,
)
assert result.returncode != 0
combined = result.stderr + result.stdout
assert "API key" in combined or "Error" in combined
def test_list_no_key_errors(self, clean_home):
"""Without an API key, `mem0 list` must fail."""
result = _run(["list"], home_dir=clean_home)
assert result.returncode != 0
combined = result.stderr + result.stdout
assert "API key" in combined or "Error" in combined
def test_delete_no_id_no_all_errors(self, clean_home):
"""Delete without memory_id, --all, or --entity must fail."""
result = _run(
["delete", "--api-key", "m0-fake-key"],
home_dir=clean_home,
)
assert result.returncode != 0
combined = result.stderr + result.stdout
assert (
"memory ID" in combined.lower()
or "--all" in combined
or "--entity" in combined
or "Error" in combined
)
def test_config_show_clean(self, clean_home):
"""config show with no config should still work."""
result = _run(["config", "show"], home_dir=clean_home)
assert result.returncode == 0
assert "backend" in result.stdout.lower() or "platform" in result.stdout.lower()
def test_config_set_and_get_roundtrip(self, clean_home):
"""config set then config get should return the set value."""
_run(
["config", "set", "defaults.user_id", "integration-test-user"],
home_dir=clean_home,
)
result = _run(
["config", "get", "defaults.user_id"],
home_dir=clean_home,
)
assert result.returncode == 0
assert "integration-test-user" in result.stdout
def test_import_nonexistent_file(self, clean_home):
"""Importing a nonexistent file should fail gracefully."""
result = _run(
["import", "/nonexistent/file.json", "--api-key", "m0-fake"],
home_dir=clean_home,
)
assert result.returncode != 0
combined = result.stderr + result.stdout
assert "Failed" in combined or "Error" in combined or "error" in combined
def test_add_no_content_errors(self, clean_home):
"""add with no text/messages/file should fail."""
result = _run(
["add", "--user-id", "alice", "--api-key", "m0-fake"],
home_dir=clean_home,
)
assert result.returncode != 0
combined = result.stderr + result.stdout
assert "No content" in combined or "Error" in combined
class TestCLINewFeatures:
"""Tests for MCP parity features: --graph, --limit, entities delete."""
def test_add_help_has_graph(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"])
assert result.returncode == 0
assert "--graph" 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):
"""delete --entity should appear in help output."""
result = _run(["delete", "--help"])
assert result.returncode == 0
assert "--entity" in result.stdout
def test_entity_delete_has_scope_options(self):
"""entity delete should expose scope options."""
result = _run(["entity", "delete", "--help"])
assert result.returncode == 0
assert "--user-id" in result.stdout
assert "--force" in result.stdout
assert "--app-id" in result.stdout
assert "--run-id" in result.stdout
File diff suppressed because it is too large Load Diff
+240
View File
@@ -0,0 +1,240 @@
"""Tests for configuration management."""
from __future__ import annotations
import os
from mem0_cli.config import (
Mem0Config,
get_nested_value,
load_config,
redact_key,
save_config,
set_nested_value,
)
class TestRedactKey:
def test_empty_key(self):
assert redact_key("") == "(not set)"
def test_short_key(self):
assert redact_key("abc") == "ab***"
def test_normal_key(self):
result = redact_key("m0-abcdefgh12345678")
assert result == "m0-a...5678"
assert "abcdefgh" not in result
def test_exact_8_chars(self):
# 8 chars is <= 8, so it gets the short redaction
assert redact_key("12345678") == "12***"
class TestConfig:
def test_default_config(self):
config = Mem0Config()
assert config.platform.base_url == "https://api.mem0.ai"
assert config.platform.api_key == ""
def test_save_and_load(self, isolate_config):
config = Mem0Config()
config.platform.api_key = "m0-test-key"
save_config(config)
loaded = load_config()
assert loaded.platform.api_key == "m0-test-key"
def test_env_var_override(self, isolate_config, monkeypatch):
config = Mem0Config()
config.platform.api_key = "file-key"
save_config(config)
monkeypatch.setenv("MEM0_API_KEY", "env-key")
loaded = load_config()
assert loaded.platform.api_key == "env-key"
def test_load_nonexistent_config(self, isolate_config):
config = load_config()
assert config.platform.api_key == ""
def test_config_file_permissions(self, isolate_config):
config = Mem0Config()
config.platform.api_key = "secret"
save_config(config)
from mem0_cli.config import CONFIG_FILE
mode = os.stat(CONFIG_FILE).st_mode & 0o777
assert mode == 0o600
def test_defaults_save_and_load(self, isolate_config):
config = Mem0Config()
config.defaults.user_id = "alice"
config.defaults.agent_id = "support-bot"
config.defaults.app_id = "my-app"
config.defaults.run_id = "run-001"
save_config(config)
loaded = load_config()
assert loaded.defaults.user_id == "alice"
assert loaded.defaults.agent_id == "support-bot"
assert loaded.defaults.app_id == "my-app"
assert loaded.defaults.run_id == "run-001"
def test_defaults_env_var_override(self, isolate_config, monkeypatch):
config = Mem0Config()
config.defaults.user_id = "file-user"
save_config(config)
monkeypatch.setenv("MEM0_USER_ID", "env-user")
monkeypatch.setenv("MEM0_AGENT_ID", "env-agent")
loaded = load_config()
assert loaded.defaults.user_id == "env-user"
assert loaded.defaults.agent_id == "env-agent"
def test_backward_compat_no_defaults_key(self, isolate_config):
"""Old config files without 'defaults' key should load fine."""
import json
from mem0_cli.config import CONFIG_FILE, ensure_config_dir
ensure_config_dir()
# Write a config without the "defaults" key
data = {
"version": 1,
"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.platform.api_key == "m0-test"
assert loaded.defaults.user_id == ""
assert loaded.defaults.agent_id == ""
def test_default_config_has_empty_defaults(self):
config = Mem0Config()
assert config.defaults.user_id == ""
assert config.defaults.agent_id == ""
assert config.defaults.app_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:
def test_get_nested_value(self):
config = Mem0Config()
config.platform.api_key = "test-key"
assert get_nested_value(config, "platform.api_key") == "test-key"
def test_get_nonexistent_key(self):
config = Mem0Config()
assert get_nested_value(config, "nonexistent.key") is None
def test_set_nested_value(self):
config = Mem0Config()
assert set_nested_value(config, "platform.api_key", "new-key")
assert config.platform.api_key == "new-key"
def test_set_nonexistent_key(self):
config = Mem0Config()
assert set_nested_value(config, "nonexistent.key", "val") is False
def test_get_defaults_user_id(self):
config = Mem0Config()
config.defaults.user_id = "alice"
assert get_nested_value(config, "defaults.user_id") == "alice"
def test_set_defaults_user_id(self):
config = Mem0Config()
assert set_nested_value(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:
def test_cli_flag_overrides_default(self):
from mem0_cli.app import _resolve_ids
config = Mem0Config()
config.defaults.user_id = "default-user"
ids = _resolve_ids(
config,
user_id="cli-user",
agent_id=None,
)
assert ids["user_id"] == "cli-user"
def test_default_used_when_flag_is_none(self):
from mem0_cli.app import _resolve_ids
config = Mem0Config()
config.defaults.user_id = "default-user"
config.defaults.agent_id = "default-agent"
ids = _resolve_ids(config, user_id=None, agent_id=None)
assert ids["user_id"] == "default-user"
assert ids["agent_id"] == "default-agent"
def test_none_when_neither_set(self):
from mem0_cli.app import _resolve_ids
config = Mem0Config()
ids = _resolve_ids(config, user_id=None, agent_id=None)
assert ids["user_id"] is None
assert ids["agent_id"] is None
assert ids["app_id"] is None
assert ids["run_id"] is None
def test_empty_string_treated_as_unset(self):
from mem0_cli.app import _resolve_ids
config = Mem0Config()
config.defaults.user_id = ""
ids = _resolve_ids(config, user_id=None)
assert ids["user_id"] is None
+282
View File
@@ -0,0 +1,282 @@
"""Tests for output formatting."""
from __future__ import annotations
from io import StringIO
from rich.console import Console
from mem0_cli.output import (
format_add_result,
format_memories_table,
format_memories_text,
format_single_memory,
sanitize_agent_data,
)
def _make_console() -> tuple[Console, StringIO]:
buf = StringIO()
return Console(file=buf, force_terminal=False, no_color=True, width=120, highlight=False), buf
SAMPLE_MEMORIES = [
{
"id": "abc-123-def-456",
"memory": "User prefers dark mode",
"score": 0.92,
"created_at": "2026-02-15T10:30:00Z",
"categories": ["preferences"],
},
{
"id": "ghi-789-jkl-012",
"memory": "User uses vim keybindings",
"score": 0.78,
"created_at": "2026-03-01T14:00:00Z",
"categories": ["tools"],
},
]
class TestTextFormat:
def test_format_memories_text(self):
console, buf = _make_console()
format_memories_text(console, SAMPLE_MEMORIES)
output = buf.getvalue()
assert "Found 2 memories" in output
assert "dark mode" in output
assert "vim keybindings" in output
assert "0.92" in output
def test_format_memories_text_empty(self):
console, buf = _make_console()
format_memories_text(console, [])
output = buf.getvalue()
assert "Found 0" in output
class TestTableFormat:
def test_format_memories_table(self):
console, buf = _make_console()
format_memories_table(console, SAMPLE_MEMORIES)
output = buf.getvalue()
assert "dark mode" in output
assert "abc-123-" in output
def test_format_memories_table_empty(self):
console, buf = _make_console()
format_memories_table(console, [])
output = buf.getvalue()
# Should still render (empty table)
assert "ID" in output
class TestSingleMemory:
def test_format_single_memory_text(self):
console, buf = _make_console()
mem = SAMPLE_MEMORIES[0]
format_single_memory(console, mem, "text")
output = buf.getvalue()
assert "dark mode" in output
assert "abc-123-def-456" in output
def test_format_single_memory_json(self):
console, buf = _make_console()
mem = SAMPLE_MEMORIES[0]
format_single_memory(console, mem, "json")
output = buf.getvalue()
assert '"memory"' in output
class TestAddResult:
def test_format_add_result_text(self):
console, buf = _make_console()
result = {
"results": [
{"id": "abc-123-def-456", "memory": "User prefers dark mode", "event": "ADD"},
]
}
format_add_result(console, result, "text")
output = buf.getvalue()
assert "dark mode" in output
assert "Added" in output
def test_format_add_result_update_event(self):
console, buf = _make_console()
result = {
"results": [
{"id": "abc-123", "memory": "Updated pref", "event": "UPDATE"},
]
}
format_add_result(console, result, "text")
output = buf.getvalue()
assert "Updated" in output
def test_format_add_result_noop(self):
console, buf = _make_console()
result = {
"results": [
{"id": "abc-123", "memory": "Same thing", "event": "NOOP"},
]
}
format_add_result(console, result, "text")
output = buf.getvalue()
assert "No change" in output
def test_format_add_result_quiet(self):
console, buf = _make_console()
result = {"results": [{"id": "abc-123", "memory": "Quiet", "event": "ADD"}]}
format_add_result(console, result, "quiet")
output = buf.getvalue()
assert output.strip() == ""
def test_format_add_result_deduplicates_pending_by_event_id(self):
console, buf = _make_console()
result = {
"results": [
{"status": "PENDING", "event_id": "evt-dup"},
{"status": "PENDING", "event_id": "evt-dup"},
]
}
format_add_result(console, result, "text")
output = buf.getvalue()
# Should show only one PENDING block despite two entries with same event_id
assert output.count("evt-dup") == 2 # event_id line + status hint line
assert output.count("Queued") == 1
def test_format_add_result_empty(self):
console, buf = _make_console()
format_add_result(console, {"results": []}, "text")
output = buf.getvalue()
assert "No memories extracted" in output
class TestSanitizeAgentData:
def test_add_projects_fields(self):
raw = [
{
"id": "abc",
"memory": "test",
"event": "ADD",
"metadata": {"x": 1},
"categories": ["a"],
}
]
result = sanitize_agent_data("add", raw)
assert result == [{"id": "abc", "memory": "test", "event": "ADD"}]
def test_add_pending_passthrough(self):
raw = [{"status": "PENDING", "event_id": "evt-123", "metadata": "noise"}]
result = sanitize_agent_data("add", raw)
assert result == [{"status": "PENDING", "event_id": "evt-123"}]
def test_search_projects_fields(self):
raw = [
{
"id": "abc",
"memory": "test",
"score": 0.9,
"created_at": "2026-01-01",
"categories": ["a"],
"user_id": "u1",
"agent_id": None,
}
]
result = sanitize_agent_data("search", raw)
assert result == [
{
"id": "abc",
"memory": "test",
"score": 0.9,
"created_at": "2026-01-01",
"categories": ["a"],
}
]
def test_list_projects_fields(self):
raw = [
{
"id": "abc",
"memory": "test",
"created_at": "2026-01-01",
"categories": ["a"],
"user_id": "u1",
}
]
result = sanitize_agent_data("list", raw)
assert result == [
{"id": "abc", "memory": "test", "created_at": "2026-01-01", "categories": ["a"]}
]
def test_get_projects_fields(self):
raw = {
"id": "abc",
"memory": "test",
"created_at": "2026-01-01",
"updated_at": "2026-01-02",
"categories": ["a"],
"metadata": {"k": "v"},
"user_id": "u1",
}
result = sanitize_agent_data("get", raw)
assert "user_id" not in result
assert "id" in result and "memory" in result
def test_update_projects_fields(self):
raw = {"id": "abc", "memory": "updated", "extra": "noise"}
result = sanitize_agent_data("update", raw)
assert result == {"id": "abc", "memory": "updated"}
def test_event_list_projects_fields(self):
raw = [
{
"id": "evt-1",
"event_type": "ADD",
"status": "SUCCEEDED",
"graph_status": None,
"latency": 100.0,
"created_at": "2026-01-01",
"updated_at": "2026-01-02",
}
]
result = sanitize_agent_data("event list", raw)
assert result == [
{
"id": "evt-1",
"event_type": "ADD",
"status": "SUCCEEDED",
"latency": 100.0,
"created_at": "2026-01-01",
}
]
assert "updated_at" not in result[0]
assert "graph_status" not in result[0]
def test_event_status_flattens_results(self):
raw = {
"id": "evt-1",
"event_type": "ADD",
"status": "SUCCEEDED",
"latency": 100.0,
"created_at": "2026-01-01",
"updated_at": "2026-01-02",
"results": [
{"id": "mem-1", "event": "ADD", "user_id": "alice", "data": {"memory": "dark mode"}}
],
}
result = sanitize_agent_data("event status", raw)
assert result["results"][0] == {
"id": "mem-1",
"event": "ADD",
"user_id": "alice",
"memory": "dark mode",
}
assert "data" not in result["results"][0]
def test_passthrough_commands(self):
for cmd in ("status", "import", "config show", "config get", "config set"):
data = {"key": "value", "other": "stuff"}
assert sanitize_agent_data(cmd, data) == data
def test_none_data(self):
assert sanitize_agent_data("add", None) is None
+225
View File
@@ -0,0 +1,225 @@
{
"cells": [
{
"cell_type": "code",
"execution_count": 1,
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from typing import List, Dict\n",
"from mem0 import Memory\n",
"from datetime import datetime\n",
"import anthropic\n",
"\n",
"# Set up environment variables\n",
"os.environ[\"OPENAI_API_KEY\"] = \"your_openai_api_key\" # needed for embedding model\n",
"os.environ[\"ANTHROPIC_API_KEY\"] = \"your_anthropic_api_key\""
]
},
{
"cell_type": "code",
"execution_count": 2,
"metadata": {},
"outputs": [],
"source": [
"class SupportChatbot:\n",
" def __init__(self):\n",
" # Initialize Mem0 with Anthropic's Claude\n",
" self.config = {\n",
" \"llm\": {\n",
" \"provider\": \"anthropic\",\n",
" \"config\": {\n",
" \"model\": \"claude-3-5-sonnet-latest\",\n",
" \"temperature\": 0.1,\n",
" \"max_tokens\": 2000,\n",
" },\n",
" }\n",
" }\n",
" self.client = anthropic.Client(api_key=os.environ[\"ANTHROPIC_API_KEY\"])\n",
" self.memory = Memory.from_config(self.config)\n",
"\n",
" # Define support context\n",
" self.system_context = \"\"\"\n",
" You are a helpful customer support agent. Use the following guidelines:\n",
" - Be polite and professional\n",
" - Show empathy for customer issues\n",
" - Reference past interactions when relevant\n",
" - Maintain consistent information across conversations\n",
" - If you're unsure about something, ask for clarification\n",
" - Keep track of open issues and follow-ups\n",
" \"\"\"\n",
"\n",
" def store_customer_interaction(self, user_id: str, message: str, response: str, metadata: Dict = None):\n",
" \"\"\"Store customer interaction in memory.\"\"\"\n",
" if metadata is None:\n",
" metadata = {}\n",
"\n",
" # Add timestamp to metadata\n",
" metadata[\"timestamp\"] = datetime.now().isoformat()\n",
"\n",
" # Format conversation for storage\n",
" conversation = [{\"role\": \"user\", \"content\": message}, {\"role\": \"assistant\", \"content\": response}]\n",
"\n",
" # Store in Mem0\n",
" self.memory.add(conversation, user_id=user_id, metadata=metadata)\n",
"\n",
" def get_relevant_history(self, user_id: str, query: str) -> List[Dict]:\n",
" \"\"\"Retrieve relevant past interactions.\"\"\"\n",
" return self.memory.search(\n",
" query=query,\n",
" user_id=user_id,\n",
" limit=5, # Adjust based on needs\n",
" )\n",
"\n",
" def handle_customer_query(self, user_id: str, query: str) -> str:\n",
" \"\"\"Process customer query with context from past interactions.\"\"\"\n",
"\n",
" # Get relevant past interactions\n",
" relevant_history = self.get_relevant_history(user_id, query)\n",
"\n",
" # Build context from relevant history\n",
" context = \"Previous relevant interactions:\\n\"\n",
" for memory in relevant_history:\n",
" context += f\"Customer: {memory['memory']}\\n\"\n",
" context += f\"Support: {memory['memory']}\\n\"\n",
" context += \"---\\n\"\n",
"\n",
" # Prepare prompt with context and current query\n",
" prompt = f\"\"\"\n",
" {self.system_context}\n",
"\n",
" {context}\n",
"\n",
" Current customer query: {query}\n",
"\n",
" Provide a helpful response that takes into account any relevant past interactions.\n",
" \"\"\"\n",
"\n",
" # Generate response using Claude\n",
" response = self.client.messages.create(\n",
" model=\"claude-3-5-sonnet-latest\",\n",
" messages=[{\"role\": \"user\", \"content\": prompt}],\n",
" max_tokens=2000,\n",
" temperature=0.1,\n",
" )\n",
"\n",
" # Store interaction\n",
" self.store_customer_interaction(\n",
" user_id=user_id, message=query, response=response, metadata={\"type\": \"support_query\"}\n",
" )\n",
"\n",
" return response.content[0].text"
]
},
{
"cell_type": "code",
"execution_count": 3,
"metadata": {},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"Welcome to Customer Support! Type 'exit' to end the conversation.\n",
"Customer: Hi, I'm having trouble connecting my new smartwatch to the mobile app. It keeps showing a connection error.\n"
]
},
{
"name": "stderr",
"output_type": "stream",
"text": [
"/var/folders/5x/9kmqjfm947g5yh44m7fjk75r0000gn/T/ipykernel_99777/1076713094.py:55: DeprecationWarning: The current get_all API output format is deprecated. To use the latest format, set `api_version='v1.1'`. The current format will be removed in mem0ai 1.1.0 and later versions.\n",
" return self.memory.search(\n",
"/var/folders/5x/9kmqjfm947g5yh44m7fjk75r0000gn/T/ipykernel_99777/1076713094.py:47: DeprecationWarning: The current add API output format is deprecated. To use the latest format, set `api_version='v1.1'`. The current format will be removed in mem0ai 1.1.0 and later versions.\n",
" self.memory.add(\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"Support: Hello! Thank you for reaching out about the connection issue with your smartwatch. I understand how frustrating it can be when a new device won't connect properly. I'll be happy to help you resolve this.\n",
"\n",
"To better assist you, could you please provide me with:\n",
"1. The model of your smartwatch\n",
"2. The type of phone you're using (iOS or Android)\n",
"3. Whether you've already installed the companion app on your phone\n",
"4. If you've tried pairing the devices before\n",
"\n",
"These details will help me provide you with the most accurate troubleshooting steps. In the meantime, here are some general tips that might help:\n",
"- Make sure Bluetooth is enabled on your phone\n",
"- Keep your smartwatch and phone within close range (within 3 feet) during pairing\n",
"- Ensure both devices have sufficient battery power\n",
"- Check if your phone's operating system meets the minimum requirements for the smartwatch\n",
"\n",
"Please provide the requested information, and I'll guide you through the specific steps to resolve the connection error.\n",
"\n",
"Is there anything else you'd like to share about the issue? \n",
"\n",
"\n",
"Customer: The connection issue is still happening even after trying the steps you suggested.\n",
"Support: I apologize that you're still experiencing connection issues with your smartwatch. I understand how frustrating it must be to have this problem persist even after trying the initial troubleshooting steps. Let's try some additional solutions to resolve this.\n",
"\n",
"Before we proceed, could you please confirm:\n",
"1. Which specific steps you've already attempted?\n",
"2. Are you seeing any particular error message?\n",
"3. What model of smartwatch and phone are you using?\n",
"\n",
"This information will help me provide more targeted solutions and avoid suggesting steps you've already tried. In the meantime, here are a few advanced troubleshooting steps we can consider:\n",
"\n",
"1. Completely resetting the Bluetooth connection\n",
"2. Checking for any software updates for both the watch and phone\n",
"3. Testing the connection with a different mobile device to isolate the issue\n",
"\n",
"Would you be able to provide those details so I can better assist you? I'll make sure to document this ongoing issue to help track its resolution. \n",
"\n",
"\n",
"Customer: exit\n",
"Thank you for using our support service. Goodbye!\n"
]
}
],
"source": [
"chatbot = SupportChatbot()\n",
"user_id = \"customer_bot\"\n",
"print(\"Welcome to Customer Support! Type 'exit' to end the conversation.\")\n",
"\n",
"while True:\n",
" # Get user input\n",
" query = input()\n",
" print(\"Customer:\", query)\n",
"\n",
" # Check if user wants to exit\n",
" if query.lower() == \"exit\":\n",
" print(\"Thank you for using our support service. Goodbye!\")\n",
" break\n",
"\n",
" # Handle the query and print the response\n",
" response = chatbot.handle_customer_query(user_id, query)\n",
" print(\"Support:\", response, \"\\n\\n\")"
]
}
],
"metadata": {
"kernelspec": {
"display_name": ".venv",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.12.4"
}
},
"nbformat": 4,
"nbformat_minor": 2
}
+172
View File
@@ -0,0 +1,172 @@
# Copyright (c) 2023 - 2024, Owners of https://github.com/autogen-ai
#
# SPDX-License-Identifier: Apache-2.0
#
# Portions derived from https://github.com/microsoft/autogen are under the MIT License.
# SPDX-License-Identifier: MIT
# forked from autogen.agentchat.contrib.capabilities.teachability.Teachability
from typing import Dict, Optional, Union
from autogen.agentchat.assistant_agent import ConversableAgent
from autogen.agentchat.contrib.capabilities.agent_capability import AgentCapability
from autogen.agentchat.contrib.text_analyzer_agent import TextAnalyzerAgent
from termcolor import colored
from mem0 import Memory
class Mem0Teachability(AgentCapability):
def __init__(
self,
verbosity: Optional[int] = 0,
reset_db: Optional[bool] = False,
recall_threshold: Optional[float] = 1.5,
max_num_retrievals: Optional[int] = 10,
llm_config: Optional[Union[Dict, bool]] = None,
agent_id: Optional[str] = None,
memory_client: Optional[Memory] = None,
):
self.verbosity = verbosity
self.recall_threshold = recall_threshold
self.max_num_retrievals = max_num_retrievals
self.llm_config = llm_config
self.analyzer = None
self.teachable_agent = None
self.agent_id = agent_id
self.memory = memory_client if memory_client else Memory()
if reset_db:
self.memory.reset()
def add_to_agent(self, agent: ConversableAgent):
self.teachable_agent = agent
agent.register_hook(hookable_method="process_last_received_message", hook=self.process_last_received_message)
if self.llm_config is None:
self.llm_config = agent.llm_config
assert self.llm_config, "Teachability requires a valid llm_config."
self.analyzer = TextAnalyzerAgent(llm_config=self.llm_config)
agent.update_system_message(
agent.system_message
+ "\nYou've been given the special ability to remember user teachings from prior conversations."
)
def process_last_received_message(self, text: Union[Dict, str]):
expanded_text = text
if self.memory.get_all(agent_id=self.agent_id):
expanded_text = self._consider_memo_retrieval(text)
self._consider_memo_storage(text)
return expanded_text
def _consider_memo_storage(self, comment: Union[Dict, str]):
response = self._analyze(
comment,
"Does any part of the TEXT ask the agent to perform a task or solve a problem? Answer with just one word, yes or no.",
)
if "yes" in response.lower():
advice = self._analyze(
comment,
"Briefly copy any advice from the TEXT that may be useful for a similar but different task in the future. But if no advice is present, just respond with 'none'.",
)
if "none" not in advice.lower():
task = self._analyze(
comment,
"Briefly copy just the task from the TEXT, then stop. Don't solve it, and don't include any advice.",
)
general_task = self._analyze(
task,
"Summarize very briefly, in general terms, the type of task described in the TEXT. Leave out details that might not appear in a similar problem.",
)
if self.verbosity >= 1:
print(colored("\nREMEMBER THIS TASK-ADVICE PAIR", "light_yellow"))
self.memory.add(
[{"role": "user", "content": f"Task: {general_task}\nAdvice: {advice}"}], agent_id=self.agent_id
)
response = self._analyze(
comment,
"Does the TEXT contain information that could be committed to memory? Answer with just one word, yes or no.",
)
if "yes" in response.lower():
question = self._analyze(
comment,
"Imagine that the user forgot this information in the TEXT. How would they ask you for this information? Include no other text in your response.",
)
answer = self._analyze(
comment, "Copy the information from the TEXT that should be committed to memory. Add no explanation."
)
if self.verbosity >= 1:
print(colored("\nREMEMBER THIS QUESTION-ANSWER PAIR", "light_yellow"))
self.memory.add(
[{"role": "user", "content": f"Question: {question}\nAnswer: {answer}"}], agent_id=self.agent_id
)
def _consider_memo_retrieval(self, comment: Union[Dict, str]):
if self.verbosity >= 1:
print(colored("\nLOOK FOR RELEVANT MEMOS, AS QUESTION-ANSWER PAIRS", "light_yellow"))
memo_list = self._retrieve_relevant_memos(comment)
response = self._analyze(
comment,
"Does any part of the TEXT ask the agent to perform a task or solve a problem? Answer with just one word, yes or no.",
)
if "yes" in response.lower():
if self.verbosity >= 1:
print(colored("\nLOOK FOR RELEVANT MEMOS, AS TASK-ADVICE PAIRS", "light_yellow"))
task = self._analyze(
comment, "Copy just the task from the TEXT, then stop. Don't solve it, and don't include any advice."
)
general_task = self._analyze(
task,
"Summarize very briefly, in general terms, the type of task described in the TEXT. Leave out details that might not appear in a similar problem.",
)
memo_list.extend(self._retrieve_relevant_memos(general_task))
memo_list = list(set(memo_list))
return comment + self._concatenate_memo_texts(memo_list)
def _retrieve_relevant_memos(self, input_text: str) -> list:
search_results = self.memory.search(input_text, agent_id=self.agent_id, limit=self.max_num_retrievals)
memo_list = [result["memory"] for result in search_results if result["score"] <= self.recall_threshold]
if self.verbosity >= 1 and not memo_list:
print(colored("\nTHE CLOSEST MEMO IS BEYOND THE THRESHOLD:", "light_yellow"))
if search_results["results"]:
print(search_results["results"][0])
print()
return memo_list
def _concatenate_memo_texts(self, memo_list: list) -> str:
memo_texts = ""
if memo_list:
info = "\n# Memories that might help\n"
for memo in memo_list:
info += f"- {memo}\n"
if self.verbosity >= 1:
print(colored(f"\nMEMOS APPENDED TO LAST MESSAGE...\n{info}\n", "light_yellow"))
memo_texts += "\n" + info
return memo_texts
def _analyze(self, text_to_analyze: Union[Dict, str], analysis_instructions: Union[Dict, str]):
self.analyzer.reset()
self.teachable_agent.send(
recipient=self.analyzer, message=text_to_analyze, request_reply=False, silent=(self.verbosity < 2)
)
self.teachable_agent.send(
recipient=self.analyzer, message=analysis_instructions, request_reply=True, silent=(self.verbosity < 2)
)
return self.teachable_agent.last_message(self.analyzer)["content"]
File diff suppressed because it is too large Load Diff
+11 -4
View File
@@ -1,7 +1,14 @@
# Contributing to embedchain docs
# Mintlify Starter Kit
Click on `Use this template` to copy the Mintlify starter kit. The starter kit contains examples including
### 👩‍💻 Development
- Guide pages
- Navigation
- Customizations
- API Reference pages
- Use of popular components
### Development
Install the [Mintlify CLI](https://www.npmjs.com/package/mintlify) to preview the documentation changes locally. To install, use the following command
@@ -15,9 +22,9 @@ Run the following command at the root of your documentation (where mint.json is)
mintlify dev
```
### 😎 Publishing Changes
### Publishing Changes
Changes will be deployed to production automatically after your PR is merged to the main branch.
Install our Github App to auto propagate changes from your repo to your deployment. Changes will be deployed to production automatically after pushing to the default branch. Find the link to install on your dashboard.
#### Troubleshooting
+5
View File
@@ -0,0 +1,5 @@
<Note type="info">
📢 Heads up!
We're moving to async memory add for a faster experience.
If you signed up after July 1st, 2025, your add requests will work in the background and return right away.
</Note>
+6 -6
View File
@@ -1,11 +1,11 @@
<CardGroup cols={3}>
<Card title="Talk to founders" icon="calendar" href="https://cal.com/taranjeetio/ec">
Schedule a call
<Card title="Discord" icon="discord" href="https://mem0.dev/DiD" color="#7289DA">
Join our community
</Card>
<Card title="Slack" icon="slack" href="https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw" color="#4A154B">
Join our slack community
<Card title="GitHub" icon="github" href="https://github.com/mem0ai/mem0/discussions/new?category=q-a">
Ask questions on GitHub
</Card>
<Card title="Discord" icon="discord" href="https://discord.gg/6PzXDgEjG5" color="#7289DA">
Join our discord community
<Card title="Support" icon="calendar" href="https://cal.com/taranjeetio/meet">
Talk to founders
</Card>
</CardGroup>
+3
View File
@@ -0,0 +1,3 @@
<Note type="info">
<strong>🎉 Mem0 1.0.0 is here!</strong> Enhanced filtering, reranking, and smarter memory management.
</Note>
+108
View File
@@ -0,0 +1,108 @@
---
title: "Overview"
icon: "terminal"
iconType: "solid"
description: "REST APIs for memory management, search, and entity operations"
---
## Mem0 REST API
Mem0 provides a comprehensive REST API for integrating advanced memory capabilities into your applications. Create, search, update, and manage memories across users, agents, and custom entities with simple HTTP requests.
<Info>
**Quick start:** Get your API key from the [Mem0 Dashboard](https://app.mem0.ai/dashboard/api-keys) and make your first memory operation in minutes.
</Info>
---
## Quick Start Guide
Get started with Mem0 API in three simple steps:
1. **[Add Memories](/api-reference/memory/add-memories)** - Store information and context from user conversations
2. **[Search Memories](/api-reference/memory/search-memories)** - Retrieve relevant memories using semantic search
3. **[Get Memories](/api-reference/memory/get-memories)** - Fetch all memories for a specific entity
---
## Core Operations
<CardGroup cols={2}>
<Card title="Add Memories" icon="plus" href="/api-reference/memory/add-memories">
Store new memories from conversations and interactions
</Card>
<Card title="Search Memories" icon="magnifying-glass" href="/api-reference/memory/search-memories">
Find relevant memories using semantic search with filters
</Card>
<Card title="Update Memory" icon="pen" href="/api-reference/memory/update-memory">
Modify existing memory content and metadata
</Card>
<Card title="Delete Memory" icon="trash" href="/api-reference/memory/delete-memory">
Remove specific memories or batch delete operations
</Card>
</CardGroup>
---
## API Categories
Explore the full API organized by functionality:
<CardGroup cols={2}>
<Card title="Memory APIs" icon="microchip" href="/api-reference/memory/add-memories">
Core and advanced operations: CRUD, search, batch updates, history, and exports
</Card>
<Card title="Events APIs" icon="clock" href="/api-reference/events/get-events">
Track and monitor the status of asynchronous memory operations
</Card>
<Card title="Entities APIs" icon="users" href="/api-reference/entities/get-users">
Manage users, agents, and their associated memory data
</Card>
<Card title="Organizations & Projects" icon="building" href="/api-reference/organizations-projects">
Multi-tenant support, access control, and team collaboration
</Card>
<Card title="Webhooks" icon="webhook" href="/api-reference/webhook/create-webhook">
Real-time notifications for memory events and updates
</Card>
</CardGroup>
<Note>
**Building multi-tenant apps?** Learn about [Organizations & Projects](/api-reference/organizations-projects) for team isolation and access control.
</Note>
---
## Authentication
All API requests require authentication using Token-based authentication. Include your API key in the Authorization header:
```bash
Authorization: Token <your-api-key>
```
Get your API key from the [Mem0 Dashboard](https://app.mem0.ai/dashboard/api-keys).
<Warning>
**Keep your API key secure.** Never expose it in client-side code or public repositories. Use environment variables and server-side requests only.
</Warning>
---
## Next Steps
<CardGroup cols={2}>
<Card title="Add Your First Memory" icon="rocket" href="/api-reference/memory/add-memories">
Start storing memories via the REST API
</Card>
<Card title="Search with Filters" icon="filter" href="/api-reference/memory/search-memories">
Learn advanced search and filtering techniques
</Card>
</CardGroup>
@@ -0,0 +1,5 @@
---
title: 'Delete User'
description: "Remove a user entity from the Mem0 platform by entity type and ID using the DELETE endpoint."
openapi: delete /v2/entities/{entity_type}/{entity_id}/
---

Some files were not shown because too many files have changed in this diff Show More