Compare commits

...

1769 Commits

Author SHA1 Message Date
Mgeeeek 6158ac1065 chore: trigger CI build_mem0 / build_embedchain on cli-only PR 2026-05-14 19:56:56 +05:30
Mgeeeek 3d3303bf36 chore(cli): bump to v0.2.5 + changelog + ruff format fix
- Python: 0.2.4 → 0.2.5 in cli/python/pyproject.toml
- Node:   0.2.4 → 0.2.5 in cli/node/package.json
- docs/changelog/sdk.mdx: new CLI v0.2.5 entry (Agent Mode bootstrap,
  --agent-caller, mem0 identify, plugin sync, claim flow, pingKey fix,
  rate-limit clarity, JSON envelope command field, envelope validation)
- CI fix: ruff format applied to config.py + test_init_internals.py

Release flow: merge → tag cli-v0.2.5 (Python CD) and cli-node-v0.2.5
(Node CD) on GitHub Releases. OIDC trusted publishing — no tokens needed.
2026-05-14 19:51:27 +05:30
Mgeeeek bd6d170ebb fix(cli): pingKey distinguishes network failures from invalid keys + tests
PR review surfaced a data-loss path: pingKey returned False on ANY exception,
so a VPN flap or DNS hiccup made Rules 1/2 (reuse existing valid key) fall
through to Rule 3 (mint new shadow), silently rotating the user's API key
and rewriting plugin-sync targets (~/.claude/settings.json, .zshrc).

Now pingKey returns False ONLY on a definitive auth failure (HTTP 401/403).
Network errors, timeouts, and 5xx responses return True so the existing key
is preferred over re-minting. Mirror change in both Python and Node.

Additional fixes from the same review pass:

  - --agent-caller is now PATCHed to the backend when supplied on a Rule 1
    or Rule 2 reuse path (previously silently dropped). Best-effort —
    failures don't break reuse.

  - bootstrap_via_backend / bootstrapViaBackend renames the `body` local on
    the error path to `err_body` (no longer shadows the request payload).
    Same rename in the claim flow.

  - Bootstrap envelope is now validated for non-empty api_key +
    default_user_id before mutation — defends against partial backend
    responses silently persisting null/undefined into typed string fields.

  - Stale docstrings in agent_detect.{py,ts} and bootstrap_via_backend no
    longer claim env-var sniffing fills `agent_caller`; the field is
    self-declared via --agent-caller only.

Tests (new file mirrored across runtimes):

  - test_init_internals.py / init-internals.test.ts
  - pingKey: 200/401/403/5xx/connect-error/timeout matrix
  - plugin_sync.updateShellRc: trailing-newline preservation, no-create,
    surrounding-content preservation, idempotency, missing-file no-op
  - plugin_sync.updateClaudeSettings: no env-block creation, no
    MEM0_API_KEY insertion into existing env, idempotency, malformed JSON
    no-op
  - Python only: bootstrap 403 "permission" → daily-limit translation

Python: 161 tests pass. Node: 112 tests pass. ruff + biome clean.
2026-05-14 19:39:05 +05:30
Mgeeeek a5cdd45651 docs: teach agents the --agent-caller flag + mem0 identify fallback
Update all Agent Mode quickstart references so agents know how to
attribute their signup:

  1. Canonical: mem0 init --agent --agent-caller <your-name> --json
  2. Fallback if forgotten: mem0 identify <your-name>

Files updated:
- README.md: Agent Mode promo line
- docs/llms.txt: LLM-facing quickstart
- skills/mem0-cli/SKILL.md: Autonomous setup section
- skills/mem0-cli/references/command-reference.md: --agent / --agent-caller / --source columns added to init; new `mem0 identify` section
- skills/mem0/SKILL.md: setup-without-key fallback
- skills/mem0-integrate/SKILL.md: Platform-track key-missing branch

Backend changes that this docs change pairs with: mem0ai/platform#2784
(open sanitize-only normalize + PATCH /agent_mode/caller/).
2026-05-14 18:00:08 +05:30
Mgeeeek a9455313cc feat(cli): mem0 identify <name> — post-init agent self-identify
Adds a one-shot subcommand the agent runs when it bootstrapped without
--agent-caller: `mem0 identify claude-code` PATCHes the active key's
agent_caller via /api/v1/auth/agent_mode/caller/, then mirrors the
canonical value into ~/.mem0/config.json.

Bootstrap success message now prompts unidentified agents to run it.
If --agent-caller was passed on init, no prompt (already attributed).

Both runtimes (Python + Node) get the command + the prompt update.
2026-05-14 17:57:35 +05:30
Mgeeeek 8aa07c2f62 refactor(cli): self-declared agent_caller via --agent-caller flag
Switch agent identity from env-var sniffing to explicit self-declaration
(Proof Editor-style). The agent now passes its own name when running
mem0 init --agent --agent-caller <name>.

Why: env-var detection was speculative for everything but Claude Code —
CLAUDECODE=1 is documented and verified, but CURSOR_AGENT, CODEX_CLI,
CLINE_AGENT etc. were plausible-sounding picks without upstream
confirmation. Self-declaration is honest, future-proof (no whitelist
treadmill as new agents emerge), and matches how Proof handles agent
join: explicit identity from the agent itself.

CLI changes:
- New --agent-caller <name> option on `mem0 init` (Python + Node)
- Removed detect_agent_caller() as the source for agent_caller field;
  still used as a context trigger ("does this look like an agent?")
  for Rule 3 auto-bootstrap and for telemetry-only event property —
  identity goes to NULL unless --agent-caller is passed
- runInit / run_init accept agent_caller kwarg, forward to backend
- PostHog cli.init event uses the self-declared value (not env-sniffed)

Companion backend change (mem0ai/platform#2784) drops the whitelist
normalizer for an open sanitize so any sensible name is accepted.
2026-05-14 17:40:52 +05:30
Mgeeeek d54dad265d feat(cli): pass detected agent_caller to backend on Agent Mode bootstrap
The CLI's detect_agent_caller() already canonicalizes the caller (env-var
sniff returning "claude-code", "cursor", etc.) and reports it to PostHog —
but the value never reached the backend, so APIKey.agent_caller stayed
NULL. Join: send it in the bootstrap request body so the platform can
persist it (see mem0ai/platform#2784).

Wire-up:
- bootstrap_via_backend / bootstrapViaBackend gain an agent_caller kwarg,
  pass it as request body field "agent_caller"
- init_cmd / init.ts call detect_agent_caller() once and forward
- platform.agent_caller persisted to ~/.mem0/config.json so the local
  view matches what the backend stored

No change to created_via — that stays the channel enum ("agent_mode" /
"email" / "api_key"). agent_caller is the orthogonal "who started this".
2026-05-14 17:24:12 +05:30
Mgeeeek bac961bc8d style(cli): biome format multi-line import in node index.ts 2026-05-14 17:01:14 +05:30
Mgeeeek f21e9fe2b3 fix(cli): JSON error envelope shows command name + clearer rate-limit message
Two papercuts surfaced when prod's bootstrap returned 403:

  {"status": "error", "command": "", "error": "Bootstrap failed: {\"detail\":\"You do not have permission to perform this action.\"}", "data": null}

1. `"command": ""` — the JSON error envelope (printError under agent
   mode) reads from current_command state, but nothing ever called
   setCurrentCommand on the init subcommand. Hook into Node's
   preAction and Python's main_callback to stash the active
   subcommand name so error envelopes report which command failed.

2. Opaque rate-limit message. The backend's @ratelimit decorator
   raises PermissionDenied → DRF translates to generic 403
   "You do not have permission to perform this action." Users don't
   know it's a rate limit. Detect status_code==403 with /permission/i
   in the detail and surface the actual reason:
   "Daily Agent Mode signup limit reached for this network (5/day).
   Try again from a different IP or after midnight UTC."

Both fixes mirrored in Python (agent_mode_cmd.py, app.py) and Node
(agent-mode.ts, index.ts).
2026-05-14 16:43:17 +05:30
Mgeeeek 844d633960 feat(cli): emit JSON envelope on init --agent --json reuse paths
PRD's documented form is `mem0 init --agent --json`, but rules 1/2
(env/config reuse) called printSuccess which is silenced under
agent mode — so the command exited 0 with no output.

Two related fixes:

1. Add `--json` as a subcommand-level option on init (Node only;
   Python's argv preprocessor already handles this). Lets the
   PRD-style invocation `mem0 init --agent --json` parse without
   "unknown option" error.

2. When agent mode is set AND a rule 1/2 reuse fires, emit the
   Dev Spec C4 envelope:
     {
       "status": "success", "command": "init",
       "data": {
         "api_key_saved": false,
         "api_key_source": "env" | "config",
         "agent_mode": false,
         "message": "Existing Mem0 API key found and reused..."
       }
     }
   Identical shape between Python and Node (parity).

Verified live: `init --agent --json` against prod with a valid
MEM0_API_KEY env returns the envelope above (no bootstrap call).
2026-05-14 16:30:52 +05:30
Mgeeeek 4f40437d65 feat(cli): auto-sync active api_key to plugin env touchpoints
When saveConfig writes a fresh api_key (e.g. agent-mode bootstrap, OTP
signup), propagate the value into other ecosystem locations that hold
the same key:

  - ~/.claude/settings.json::env::MEM0_API_KEY (Claude Code env injection)
  - ~/.zshrc / ~/.bashrc / ~/.bash_profile `export MEM0_API_KEY="..."`

Without this, agent-mode bootstrap mints a new shadow into config.json
but the Claude plugin's MCP server keeps using the OLD env-var key —
silent surprise.

Hard guarantees:

  1. **Update-only**, never create. If a target file doesn't already
     contain a MEM0_API_KEY entry, we leave it alone. The user's
     existing setup decides which surfaces are managed; we don't
     unilaterally start writing to new files.
  2. **Preserve surrounding content.** JSON files keep all other keys.
     Shell rc files keep all other lines, comments, and the trailing
     newline (regex uses [ \t]* not \s*, which would eat the final \n
     when MEM0_API_KEY is the last line of .zshrc).
  3. **Atomic writes.** tmpfile + rename, so a crash mid-write leaves
     the original intact.
  4. **Idempotent.** If the target already has this value, no-op.
  5. **Best-effort.** Any IOError in the sync is swallowed; the
     canonical config.json write is never blocked by plugin-state.

Implemented identically in Python (plugin_sync.py) and Node
(plugin-sync.ts). Hooked into save_config() / saveConfig() so every
api_key change propagates without any caller plumbing.

Verified on a sandbox copy of real ~/.claude/settings.json and
~/.zshrc: only the MEM0_API_KEY values changed; all 36 other lines
in settings.json and 50+ lines in .zshrc preserved byte-for-byte
including the trailing newline.

Out of scope (deliberate non-changes):
  - ~/.codex/config.toml — no mem0 server entry to update
  - ~/.cursor/mcp.json — no mem0 server entry to update
  - <plugin-install-dir>/.api_key — plugin-managed, different schema
2026-05-14 16:19:21 +05:30
Mgeeeek 477279daeb feat(cli): implement Dev Spec C4 rules 1-2 — reuse valid env/config key
Before this change, `mem0 init --agent` always minted a fresh shadow,
even when a valid MEM0_API_KEY env var (set by the Claude plugin or
shell rc) was already in place. The env var would then silently shadow
the freshly-minted shadow key on the next CLI call — leading to the
surprising "Agent Mode active but my personal account does the work"
behaviour.

Dev Spec §C4 already specifies the right precedence:
  Rule 1: env MEM0_API_KEY valid → reuse, emit existing_key, no new key
  Rule 2: config api_key valid → reuse, emit existing_key
  Rule 3: mint a fresh shadow

This commit implements rules 1-3 in both runtimes (Python + Node), with
a 5-second ping to validate candidate keys against /v1/ping/.

Also reorders so the Agent Mode branch runs BEFORE the existing-config
overwrite guard. The guard's intent is "warn before overwriting a valid
key" — but rules 1/2 REUSE (not overwrite) a valid key, so the guard
must not fire on the agent path.

Net effect: the plugin's MEM0_API_KEY env var and the CLI's
config.json::platform.api_key now naturally co-exist:

  - User installs plugin → MEM0_API_KEY set, persisted via shell rc
  - User runs `mem0 init --agent` → rule 1 fires → existing key kept,
    no shadow created, no confusion
  - Plugin's MCP server (which reads ${MEM0_API_KEY}) and the CLI use
    the same key

No new shadow accounts on prod from CI / repeat invocations.
2026-05-14 16:10:31 +05:30
Mgeeeek 37d5658171 fix(cli): mem0 init --agent now triggers bootstrap (Node) + created_via parity
External sandbox audit (Sandbox E2E Test Report 2026-05-13) surfaced
two CLI issues.

1. Node — `mem0 init --agent` silently falls through to the non-TTY
   error when no agent env var is set. Commander resolves the
   program-level `--agent` alias (for --json) before init's own
   `--agent`; init's opts.agent stays false. Fix: enable Commander's
   positional options so flags AFTER a subcommand belong to that
   subcommand. Now `mem0 --agent <cmd>` is the JSON-output alias and
   `mem0 init --agent` is the Agent Mode bootstrap flag.

   The Python side already had an argv preprocessor doing the
   equivalent; this brings Node to parity using Commander's built-in
   mechanism (cleaner than mirroring the preprocessor).

   Verified: `mem0 init --agent` with bogus MEM0_BASE_URL now hits the
   bootstrap fetch and surfaces a network error (proving it reached
   bootstrap_via_backend), where before it printed "Non-interactive
   terminal detected" and never made an HTTP call.

2. Both — `config.platform.created_via` was inconsistent: set to
   "agent_mode" / "email" on the agent/claim paths but left empty on
   normal email signup and api-key paths. Now all four paths set it:
   "agent_mode" (bootstrap), "email" (OTP signup or claim), "api_key"
   (--api-key flag or interactive prompt). Downstream consumers can
   reliably filter on created_via==value.
2026-05-14 14:48:01 +05:30
Mgeeeek 0414aedce5 fix(cli): escape user-supplied message in print_error
Backend error details can contain `[/...]` or other rich-markup-like text
(e.g. regex patterns in validation errors). print_error interpolated the
raw string into a markup template, so rich's parser would crash with
MarkupError instead of showing the actual error to the user.

Escape message + hint via rich.markup.escape so the colored prefix
still renders but the user-supplied content prints literally.
2026-05-14 04:03:35 +05:30
Mgeeeek a2516269cc Surface mem0_notice for unclaimed Agent Mode keys + docs
CLI now consumes the unified mem0_notice surface that the platform side
emits for unclaimed Agent Mode keys. The notice is a directive to the
LLM agent reading the output, with a verbatim sentence to relay to the
human owner. Two presentation paths:

- Human/text output: yellow stderr banner after the primary output,
  once per command. Skipped in agent mode (the JSON envelope carries
  it instead, so no duplication).
- JSON/agent output (--json/--agent): folded into the envelope as
  "mem0_notice" so an agent parsing the output sees it without
  inspecting HTTP headers.

CLI changes (Python + Node, kept in lockstep):
- state.{ts,py}: captureNotice / takeNotice helpers — last-write-wins
  stash so multi-request commands fire the notice exactly once.
- backend/platform.{ts,py}: _request extracts notice from response
  bodies (top-level dict or list[0]) with header fallback, strips
  from downstream payload, captures for end-of-command surfacing.
- output.{ts,py}: JSON envelope formatters fold in any pending notice.
- index.ts / app.py: entrypoint surfaces notice on exit when not in
  agent mode.
- commands/agent-mode.{ts,py}: init success path prints the platform's
  notice verbatim (fallback to dim claim-command line if a stale
  backend doesn't return it).

Init-flag handling fix: the Python argv preprocessor was stripping
--agent from sys.argv unconditionally as the global JSON-output alias.
That swallowed `mem0 init --agent` (where --agent is a subcommand flag
for unattended bootstrap). Now preserved when "init" is in argv.

Parity tests: cli/python/tests/test_agent_mode.py and
cli/node/tests/agent-mode.test.ts — 7 tests each, kept in sync.

cli-spec.json updated: init now lists --agent and --source.

Docs:
- README.md: Agent Mode promo at top of Quickstart.
- docs/llms.txt: fast-path block for AI agents reading the docs.
- skills/mem0/SKILL.md, skills/mem0-cli/SKILL.md,
  skills/mem0-integrate/SKILL.md, mem0-plugin/skills/mem0/SKILL.md:
  autonomous-setup section + fallback hints.
- mem0-plugin/README.md, openclaw/README.md: "Quick path for agents"
  blocks above the human Quick Start.
2026-05-14 02:11:39 +05:30
Mgeeeek 1c92c466c4 feat(cli): rework claim to OTP-only — drop device flow + browser + polling
Replace claim_via_device_flow / claimViaDeviceFlow with claim_via_otp /
claimViaOtp. The new flow:
  1. POST /api/v1/auth/email_code/ with the user's email
  2. Prompt for the verification code (or accept via --code for non-TTY)
  3. POST /.../verify/ with {email, code, agent_mode_api_key: <local key>}
  4. Backend's verify_email_code runs upgrade-in-place inline and returns
     {claimed: true, claimed_at, ...}

No browser open, no localhost:3000 frontend dependency, no 10-minute poll
loop. Just two HTTP calls + an OTP prompt. Same upgrade-in-place
semantics on the backend; same key-value-unchanged guarantee for the
caller.

--code flag still supported on `mem0 init --email` for non-interactive
use (CI, agent-driven claim scripts).
2026-05-13 23:08:20 +05:30
Mgeeeek bcba0560c4 feat(cli): Agent Mode bootstrap + claim flow (Node)
Mirrors the Python implementation in TypeScript:
  - New PlatformConfig fields: agentMode, createdVia, claimedAt, defaultUserId.
  - agent-detect.ts: detectAgentCaller() — env-var detection covering
    CLAUDECODE / CURSOR_AGENT / CODEX_CLI / CLINE / CONTINUE / AIDER /
    GOOSE / WINDSURF.
  - commands/agent-mode.ts: bootstrapViaBackend() + claimViaDeviceFlow().
  - commands/init.ts: decision tree dispatches to bootstrap (positive agent
    signal + no email/api-key) or claim (--email with existing agent-mode
    config). Raw API key never leaves the device through the claim.
  - index.ts: --agent and --source flags added to `mem0 init`. Skip the
    preAction auto-fire for init so it can fire its own M1-M6 cli.init.
  - telemetry.ts: all cli.* events now carry agent_mode based on
    config.platform.agentMode (per growth-doc M4).

End-to-end verified against the sandbox:
  bootstrap (CLAUDECODE=1) → config.agent_mode=true → claim via --email →
  config.agent_mode=false, claimed_at set, api_key unchanged.
2026-05-13 23:08:20 +05:30
Mgeeeek 84ffb36190 feat(cli): Agent Mode bootstrap + claim flow (Python)
New behavior on `mem0 init`:
  - With no `--email`/`--api-key` AND a positive agent signal (`--agent`,
    global `--json`/`--agent`, or one of the recognized agent env vars
    CLAUDECODE / CURSOR_AGENT / CODEX_CLI / CLINE / CONTINUE / AIDER /
    GOOSE / WINDSURF), bootstrap an unattended Agent Mode account via
    POST /api/v1/auth/agent_mode/. No email, no OTP, no dashboard.
  - With `--email <addr>` AND an existing config that has agent_mode=true,
    run the claim device-flow against the existing key instead of minting
    a fresh one. The raw API key never leaves the device; backend confirms
    claim via the existing CLILoginRequest poll path. Config flips
    agent_mode=false and stamps claimed_at on success.
  - Bare `mem0 init` with no signal + no TTY still errors out — auto-bootstrap
    requires a positive agent signal to avoid surprising pipe-using humans.

New flags:
  --agent   Force unattended Agent Mode bootstrap.
  --source  Channel attribution string for signup_source PostHog property.

Config schema extensions on PlatformConfig:
  agent_mode, created_via, claimed_at, default_user_id.

Telemetry (M1-M6 from the growth doc):
  - cli.init: mode (agent|email|api_key|existing_key), agent_caller,
    signup_source, claimed_agent_mode (bool when --email claims an
    existing agent-mode config).
  - All cli.* events: agent_mode reflects config.platform.agent_mode (the
    bootstrap flag), not the output-format flag — per the growth-doc spec.
2026-05-13 23:08:20 +05:30
Agam Pandey 70bc9e51d5 docs(readme): update LongMemEval benchmark to 94.8 and add Temporal Reasoning (#5131) 2026-05-13 14:31:15 +05:30
Agam Pandey 0107fd53b8 feat: add temporal reasoning cookbook and docs (#5061) 2026-05-13 01:59:38 +05:30
Mragank Shekhar 54a03cc721 chore(plugin): bump mem0 plugin to v0.1.2 (#5094) 2026-05-09 20:56:34 +05:30
Mragank Shekhar e95de4ca50 fix(plugin): hook cleanup + identity + compact-summary flow (#5076) 2026-05-09 19:19:30 +05:30
youneshima a623cfaf76 Oss qdrant hosted memories to platform migration (#5080) 2026-05-08 08:04:09 +05:30
Chaithanya Kumar 92491c00c2 docs(memory-decay): use SDK calls in code samples (#5079)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 01:53:55 +05:30
Mragank Shekhar 9043fbf61e chore(release): bump mem0ai to 2.0.2 (py) and 3.0.3 (ts) (#5078) 2026-05-08 01:27:23 +05:30
Chaithanya Kumar c90cbc75a2 docs: memory decay v0.5 — platform feature page + API reference (#5056)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-08 01:21:33 +05:30
Gabriel Stein 58304fc939 refactor(plugin): hand mem0 search decisions to the agent (#4992)
Co-authored-by: Mgeeeek <ms8939@bennett.edu.in>
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-07 20:41:39 +05:30
Chaithanya Kumar 397f3414ee feat(sdk): expose decay on project.update (Python + TypeScript) (#5062)
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-06 15:33:32 +05:30
Gabriel Stein a734e057cf fix (telemetry): stitch oss and platform telemetry identities for python and typescript sdk
Co-authored-by: Younes Slaoui <younes.slaoui@mem0.ai>
2026-05-05 13:48:21 -07:00
Saket Aryan 0fdaa29b4a feat(skills): add mem0-integrate + mem0-test-integration pipeline skills (#4961) 2026-05-05 18:52:22 +05:30
Kartik 6d3486ca56 docs: update changelog for v1.0.11 with new features, improvements, fixes, and dependency updates (#5022) 2026-04-29 22:45:26 +05:30
Kartik ebb9bb2b15 fix: adding skills config and updating the plugin the config (#4958) 2026-04-29 22:19:40 +05:30
Kabir Kohli 594b4e65d6 fix(openclaw): bump protobufjs to >=7.5.5 (GHSA-xq3m-2v4x-88gg) (#5012) 2026-04-29 10:43:19 +05:30
Harsh Vardhan Gupta 1b95c99db4 fix: sql injection, prompt injection (#4997)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-29 00:51:16 +05:30
Gabriel Stein b66cf0f272 docs(mcp): document list_events and get_event_status tools (#4989) 2026-04-29 00:32:48 +05:30
Gabriel Stein 72dca1cdf5 docs(codex): fix broken install instructions, lead with direct MCP (#4951) 2026-04-29 00:32:27 +05:30
Zeger Hoogeboom ece7ff6b84 (TS) Fix PGVector implementation, where vector distance was inverted. (#4944) 2026-04-28 00:39:48 +05:30
Gabriel Stein 30ce028a71 feat(mem0-plugin): add Codex lifecycle hooks via opt-in installer (#4917) 2026-04-27 22:59:35 +05:30
Kartik bd9d27ff50 docs: changelog updates, version bump in mem0-ts and pyproject (#4976) 2026-04-25 23:06:57 +05:30
Prathamesh 08b746c9be chore(readme): update cover banner image (#4966) 2026-04-25 19:17:02 +05:30
Pratik Rai 693e709389 fix(api): map entity params to filters in GET /memories (#4955) (#4960) 2026-04-24 23:52:14 +05:30
Kartik 553e275112 fix(docs): updating endpoints to v3 in the api reference (#4953) 2026-04-24 17:34:11 +05:30
Varun Chawla 43dde3b186 fix: add ca_certs config option for Elasticsearch vector store (#3993) 2026-04-24 02:46:32 +05:30
Andrew Halpern cca7551192 fix(memory): honor prompt param in vector store extraction (#4914) 2026-04-23 22:36:54 +05:30
cid 5be2630f5b fix: add missing text_lemmatized in AsyncMemory._create_memory (#4886) 2026-04-23 20:04:43 +05:30
Kartik 2549a84e5c fix: update command on docs and logic (#4946) 2026-04-23 19:42:28 +05:30
Jean Ibarz 34ed122ef3 fix(ts): forward timeout config to OpenAI client in JS OSS LLM providers (#4770)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Kartik <kartik.labhshetwar@mem0.ai>
2026-04-23 19:29:27 +05:30
Gabriel Stein db8ac61713 Self-hosted dashboard and admin auth (#4837)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-23 19:06:36 +05:30
Rudrasinh Nimeshkumar Ravalji 15feaa8ac4 fix(llms): narrow _is_reasoning_model to not match gpt-5.x variants (#4746)
Co-authored-by: Claude <noreply@anthropic.com>
2026-04-23 18:58:12 +05:30
Kartik 282feaebf2 fix: remove the process env from the tests and fix the plugin manifest (#4927) 2026-04-22 22:57:44 +05:30
Kartik f5dc825d47 refactor: update memory skill loader, plugin config, and add privacy docs (#4905) 2026-04-22 17:15:19 +05:30
Saket Aryan 32b74e18b7 feat(cli): migrate Python and Node CLIs to v3 API routes (#4916) 2026-04-22 15:20:38 +05:30
Gabriel Stein daa4495583 docs(claude-code): split marketplace install into two separate steps (#4915) 2026-04-22 03:32:31 +05:30
Kabir Kohli cfb5f1776e chore(security): bump vulnerable dependencies to patched versions (#4835) 2026-04-21 01:27:13 +05:30
jessai2099 573e5212a4 fix(vector-stores): add agent_id and run_id to Elasticsearch/OpenSearch default mappings (#4906)
Co-authored-by: Claude Sonnet 4.6 <noreply@anthropic.com>
2026-04-20 23:10:59 +05:30
Yarizakura 8ba225cec8 fix: merge same-key operator dicts in AND metadata filters (#4853) 2026-04-20 21:54:02 +05:30
mintlify[bot] 4b09943092 Fix broken link in delete memory docs (#4894)
Co-authored-by: mintlify[bot] <109931778+mintlify[bot]@users.noreply.github.com>
2026-04-20 21:19:30 +05:30
Kartik 4e611e8dba docs: update memory tool list, CLI usage, and config file reading logic (#4861)
Co-authored-by: Livia Ellen <liviaellen@msn.com>
2026-04-20 20:09:45 +05:30
Kartik 5520226b5b fix: updating docs with v3 integrations updates (#4898) 2026-04-20 18:54:21 +05:30
Saket Aryan 00695e3113 ci(sdk): require changelog entry on version bump + harden TS telemetry (#4900) 2026-04-20 18:09:03 +05:30
Saket Aryan 7b6790bafb fix(ts-sdk): inject SDK version into telemetry at build time (#4897) 2026-04-20 17:29:29 +05:30
Kartik 93da5ef8f7 fix: update skills and docs (#4868) 2026-04-18 11:42:37 +05:30
Saket Aryan c1c5bd62f6 docs(llms-txt): platform-first override with scope tags + CI check (#4880) 2026-04-17 22:31:50 +05:30
Prithvi Monangi 2ec3c4ab20 fix(embeddings): set FastEmbed embedding_dims from model metadata at init (#4711) 2026-04-17 18:17:21 +05:30
Kartik 3fbc1c9aef fix(docs): updating the changelog, and removing cookbook page referencing graph memory (#4867) 2026-04-16 21:23:29 +05:30
Kartik 0b14f75c05 fix(docs): update the cookbooks and remove and update teh depcreataed param (#4814)
Co-authored-by: Claude Opus 4.5 <noreply@anthropic.com>
2026-04-16 17:39:55 +05:30
Saket Aryan fb224083e4 chore(release): promote Python SDK to 2.0.0 and TS SDK to 3.0.0 (#4860) 2026-04-16 17:13:50 +05:30
Chaithanya Kumar 30469aec17 docs: new algorithm migration guides + memory evaluation (#4811)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2026-04-16 17:13:13 +05:30
Saket Aryan 50db9e428d chore(release): bump SDK versions to next beta (#4859) 2026-04-16 16:23:50 +05:30
soumil-rathi fb87349664 fix(oss): v3 entity cleanup, filter fixes, and QA hardening (TS + Python) (#4858)
Co-authored-by: Soumil Rathi <soumilrathi@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-16 12:17:13 +05:30
Saket Aryan 8827553576 fix: adopt new v3 memory endpoints in Python + TS clients (#4856) 2026-04-16 05:28:49 +05:30
Kabir Kohli c8e20a9bb5 fix(docs): resolve duplicate operationIds and expiration_date type in openapi spec (#4854) 2026-04-16 04:09:51 +05:30
Kartik 93a51f4763 test: update integration tests for v1.1 output_format (#4847) 2026-04-16 01:37:51 +05:30
Saket Aryan 86fe275f53 fix(ts): entity store isolation, backward compat, pgvector + redis init fixes (#4841) 2026-04-15 21:01:01 +05:30
Kartik e6d6276bb9 refactor: add entity ID and search param validation, rename textLemmatized field, update tests (#4843) 2026-04-15 20:57:09 +05:30
Chaithanya Kumar 9692726db4 fix(ts-oss): isolate entity store from memory store by default (#4829)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-15 14:47:43 +05:30
soumil-rathi d8d776636f fix(v3): migration crashes + entity linking on OSS (#4836)
Co-authored-by: Soumil Rathi <soumilrathi@gmail.com>
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-15 14:46:28 +05:30
Kartik a5a688295e fix: prevent arbitrary code execution via pickle in FAISS vector store (#4833) 2026-04-15 00:02:07 +05:30
Saket Aryan 5d40592e42 chore: version bump to beta1 (#4827) 2026-04-14 18:05:22 +05:30
soumil-rathi a488e19044 feat(oss): port v3 pipeline with hybrid search, entity extraction, and additive scoring (#4805)
Co-authored-by: Soumil Rathi <soumilrathi@gmail.com>
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
Co-authored-by: chaithanyak42 <chaithanya.kumar42a@gmail.com>
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-14 18:00:58 +05:30
Parteeksachdeva 57f944e18a fix: allow anonymousTelemetryId in openclaw.json config (#4826)
Co-authored-by: parteeksachdeva-123 <parteek.sachdeva@aerchain.io>
2026-04-14 17:31:28 +05:30
Gabriel Stein fe3f7ae618 fix(plugin): remove invalid keys from Claude plugin config (#4821) 2026-04-14 01:13:03 +05:30
shafdev 4a7e166f9a fix(tests): use top_k instead of limit in test_server_params (#4820) 2026-04-14 01:11:31 +05:30
Kartik 85768e78e7 fix(docs): remove chrome extension cookbooks (#4813) 2026-04-13 22:14:00 +05:30
Yunsu 7b395f3bf7 fix(openai): make store opt-in so it stops leaking to non-OpenAI backends (#4757) 2026-04-13 21:53:22 +05:30
HUANG XIAO 4180409b09 fix(s3vectors): handle vector=None in update() to prevent boto3 validation error (#4594) 2026-04-13 21:12:49 +05:30
Joe Wu 649e719ce6 fix: LLM config manager falls back to userConf.url for baseURL (#4715) (#4761) 2026-04-13 20:46:18 +05:30
Kartik 1a53852d93 test: update valkey cluster search test to use top_k parameter (#4815) 2026-04-13 20:44:18 +05:30
Chinnu Abey ac9cdd4840 Fix incorrect use of SentenceTransformer for cross-encoder reranker models (#4806) 2026-04-13 20:14:22 +05:30
Swarnaprakash Udayakumar cf530c4bec feat(valkey): add cluster mode enabled (CME) support (#4759) 2026-04-13 20:07:11 +05:30
Saket Aryan 92b958c1cc chore: bump Python SDK to v2.0.0b0 and Node SDK to v3.0.0-beta.0 (#4810) 2026-04-13 15:51:33 +05:30
Asish Kumar c239d8a483 fix(client): prevent feedback telemetry TypeError (#4795) 2026-04-12 03:06:05 +05:30
Kartik 9d6b79a14e fix(sdk): removing the enable graph flag and switching from snake case to camel case for client ts sdk (#4776)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-12 02:40:54 +05:30
Kartik e44b46ef2e fix(sdk): removing deprecating param from our sdk and docs changes with it (#4740) 2026-04-12 00:34:58 +05:30
Saket Aryan 3882af7450 fix(cli): persistent anonymous telemetry ID + pass source=CLI in all API calls (#4789) 2026-04-11 21:00:05 +05:30
Saket Aryan d39ebad09f fix(openclaw): persistent anonymous telemetry ID, flush fix, and email resolution (#4790)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-11 20:57:14 +05:30
Gabriel Stein 9d82e2329d refactor(telemetry): sample OSS hot-path events at 10% to reduce PostHog volume (#4771) 2026-04-11 16:25:38 +05:30
szinvas 789cc9d607 docs - replace session_id with run_id (#4742) 2026-04-10 20:06:59 +05:30
Jared Diaz e59e3d5f0c Add deepseek.ts to src/llms with corresponding unit tests. Updates fa… (#4613)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-10 20:06:22 +05:30
Kartik d926f3697c fix(docs): eliminate ~222 SEO redirect chains on docs.mem0.ai (#4768) 2026-04-10 19:33:45 +05:30
Kartik c996b0e7fa docs: removing the changelog.mdx file adn replacing it with new changelog system (#4750) 2026-04-10 19:07:43 +05:30
Kartik 78ca85a260 refactor: update OpenClaw plugin config, hook logic, and documentation (#4764) 2026-04-09 17:36:05 +05:30
Kartik 88f696a60a refactor: drop orgId, projectId, enableGraph config options, update CLI prompts, and clean up related code (#4734) 2026-04-09 14:57:33 +05:30
Ignazio De Santis 081eca6d8f fix: guard temp_uuid_mapping lookups against LLM-hallucinated IDs (fixes #3931) (#4674)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-08 21:55:52 +05:30
Kartik 2434b9d550 docs: add ChatDev integration guide and update integrations list (#4751) 2026-04-08 20:16:04 +05:30
Rakhee Singh 3ffea554bc fix(azure_openai): forward response_format to Azure OpenAI API (#4689) 2026-04-08 19:22:02 +05:30
Rakhee Singh 1ad8a59b0c fix(deepseek): forward response_format to OpenAI-compatible API (#4688) 2026-04-08 19:21:11 +05:30
Saket Aryan a670333d67 feat: add AGENTS.md for AI coding agent instructions (#4726) 2026-04-06 21:32:43 +05:30
Saket Aryan 4c2db3e68b feat(skills): introduce Mem0 skill graph with dedicated CLI and Vercel AI SDK skills (#4725) 2026-04-06 20:41:29 +05:30
Saket Aryan 07f0d4f1e0 fix: use npx npm@latest for OIDC trusted publishing (#4724)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-06 17:22:00 +05:30
Saket Aryan 3565404eef fix: remove npm self-upgrade from CD workflows (#4723) 2026-04-06 17:11:28 +05:30
Kartik 144627c4ce fix(docs): change the position of the openclaw to agnet plugin and fix the integrations overview and sidebar list view (#4722) 2026-04-06 16:57:24 +05:30
Kartik 6984958138 chore: sat release (#4702) 2026-04-06 16:55:08 +05:30
Kartik b13748c446 feat: add import and event commands, refactor CLI, remove baseUrl config, update docs (#4704) 2026-04-06 16:54:27 +05:30
Saket Aryan 4642a1d6e3 feat(cli): validate API key upfront via ping and unify telemetry identity resolution (#4701) 2026-04-04 23:03:27 +05:30
Kartik 686d5e987d fix: openclaw plugin and fix the login section there (#4696)
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2026-04-04 22:21:46 +05:30
DEVAN CHAUHAN c55447c1e4 [fix] groq model (#4700) 2026-04-04 21:36:17 +05:30
Saket Aryan ee67602c58 feat(cli): add PostHog telemetry and source tracking to Python & Node CLIs (#4699) 2026-04-04 20:47:38 +05:30
Saket Aryan 0daa5d7d03 fix(ci): handle npm prerelease publish across Node.js CD workflows (#4690)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-03 21:17:11 +05:30
Kartik cfb3f58e4a fix: adding login and fixing the plugin to follow the openclaw plugin standards (#4686) 2026-04-03 20:59:23 +05:30
Kartik 66230b3f1f docs: update integration docs with new SVG icons and links (#4684) 2026-04-03 20:54:46 +05:30
BillionToken 1941cae031 fix(server): add missing psycopg-pool dependency (#4374)
Co-authored-by: BillionClaw <267901332+BillionClaw@users.noreply.github.com>
2026-04-03 20:04:05 +05:30
Utkarsh fcbb70ab3b fix: prevent thread and memory leaks from PostHog telemetry (#4535)
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-04-03 20:01:04 +05:30
Chaithanya Kumar 33d2bc495d fix(openclaw): clear security scanner exfiltration warning (#4678)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
Co-authored-by: Saket Aryan <saketaryan2002@gmail.com>
2026-04-03 00:34:49 +05:30
Gabriel Stein c0cae68646 feat(plugin): add Codex plugin support and integration docs (#4665)
Co-authored-by: Gabriel Stein <gabrielstein416@gmail.com>
2026-04-03 00:18:02 +05:30
Saket Aryan 3b2f01796e feat(cli): comprehensive docs, version bump, and purple branding (#4680)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 22:52:00 +05:30
Chaithanya Kumar 9cd3d2cca8 fix(openclaw): remove process.env access to clear security scanner warning (#4676)
Co-authored-by: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
2026-04-02 21:19:12 +05:30
Saket Aryan c53f1f126d docs(openclaw): add v1.0.1 changelog and release notes (#4675) 2026-04-02 20:25:49 +05:30
Patel Tirth 0b7615fa87 docs: add api_key parameter to Google AI LLM provider config examples (#4626) 2026-04-02 19:37:23 +05:30
Shaik Faizan Roshan Ali 66d34fab3c fix: update_memory endpoint passing dict instead of str (#3933) (#4595) 2026-04-02 19:35:09 +05:30
Krishna Chaitanya 868b63af63 fix: use DatetimeRange for datetime string values in Qdrant range filters (#4659)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-02 18:43:58 +05:30
soumil-rathi 6cc1c15320 feat(sdk): add multilingual param to project update (#4314)
Co-authored-by: Soumil Rathi <soumilrathi@gmail.com>
Co-authored-by: Claude Opus 4.6 <noreply@anthropic.com>
2026-04-02 18:38:26 +05:30
Prithvi Monangi 7a20da59ee fix(configs): add missing ConfigDict to vector store configs (#4656)
Co-authored-by: kartik-mem0 <kartik.labhshetwar@mem0.ai>
2026-04-02 18:05:54 +05:30
Saket Aryan f89f7c7c81 ci: add CD workflow for @mem0/openclaw-mem0 with OIDC trusted publishing (#4672) 2026-04-02 16:32:31 +05:30
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
Deven Patel 1b661bb2fd [Improvement] fix slack loader max_count (#1127)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-07 13:03:42 +05:30
Deven Patel 73e726f6e3 [Bug fix] quickfix custom configuration doc (#1125)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-07 11:00:35 +05:30
Deven Patel f58bbeffce [Bugfix] fix sadhguru example sources return logic (#1123)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-06 13:57:32 +05:30
Deven Patel 99261e5fb5 [Example] add example for nextjs app and discord/slack bots (#1111)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-05 22:36:58 +05:30
Deshraj Yadav b4a59d1bd5 [Feature] Update commands to run full stack app (#1118) 2024-01-05 15:58:59 +05:30
Deshraj Yadav 5c1f78879f [Misc] Minor fixes and refactor utils code (#1117) 2024-01-05 14:01:56 +05:30
Sandra Serrano 94ba82f2a2 #710 | Remove unused and deprecated app.count method (#1116) 2024-01-05 13:51:49 +05:30
Joe Sleiman b4ec14382b [Feature] Google Drive Folder support as a data source (#1106) 2024-01-05 11:46:01 +05:30
Ikko Eltociear Ashimine 38ad57a22c Update full-stack.mdx (#1115) 2024-01-05 11:27:48 +05:30
Deshraj Yadav 60bbc180ba Add support for ec runserver command (#1112) 2024-01-04 15:59:43 +05:30
Taranjeet Singh a67d902b85 Fix top_p value bug in quickstart example in docs (#1110) 2024-01-04 00:41:14 +05:30
Deven Patel ae2e9cb890 [Bugfix] fix cache session id in chat method (#1107)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-03 13:51:11 +05:30
Deven Patel 1976d38b25 [Bugfix] fix chat pdf streamlit example (#1108)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-03 13:46:44 +05:30
Deshraj Yadav f5e3410e9a Update docs for session_id (#1105) 2024-01-03 01:14:47 +05:30
Taranjeet Singh 2f6ba642c7 feat: Add private ai example (#1101) 2024-01-02 19:18:25 +05:30
Taranjeet Singh dd9b72dc62 Bump version to 0.1.51 (#1100) 2024-01-02 18:08:18 +05:30
Taranjeet Singh dd258c14b5 feat: Make gpt4all work offline. (#1099) 2024-01-02 17:46:08 +05:30
Deven Patel 295cd3fac6 [Updates] Update GPTCache configuration/docs (#1098)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-02 17:32:48 +05:30
Sidharth Mohanty c62663f2e4 Add GPT4Vision Image loader (#1089)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2024-01-02 03:57:23 +05:30
Taranjeet Singh 367d6b70e2 Update docs and side navbar. (#1097) 2024-01-02 00:18:57 +05:30
Taranjeet Singh 27236bd1b2 Improve docs. (#1096) 2024-01-01 23:22:43 +05:30
Deven Patel 6a82eb4287 [Docs] update search docs (#1093)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2024-01-01 22:32:27 +05:30
Logie bd88fe3980 fix: typo when import helpers.json_serializable on rss_feed files (#1095) 2024-01-01 21:38:41 +05:30
Deven Patel 4f70fea6df [BugFix] search method in app (#1092)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-31 23:58:17 +05:30
Sidharth Mohanty aee5bbb44b [Refactor] Update dependencies and loaders (#1062) 2023-12-30 20:52:20 +05:30
Deven Patel a304ded500 [Bugfix] fix config validation for google llm config (#1088)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-30 16:25:41 +05:30
Deshraj Yadav a54dde0509 [Bug Fix] Handle chat sessions properly during app.chat() calls (#1084) 2023-12-30 15:36:24 +05:30
Taranjeet Singh 52b4577d3b Update links, code and description (#1087) 2023-12-30 15:32:16 +05:30
Taranjeet Singh e199f57279 Fix light mode logo (#1083) 2023-12-30 15:16:48 +05:30
Taranjeet Singh dec12b33a6 Update dark mode logo (#1082) 2023-12-30 15:14:14 +05:30
Deven Patel 04daa1b206 [Feature] Add support for GPTCache (#1065) 2023-12-30 14:51:48 +05:30
Deven Patel a7e1520d08 [Improvements] update docs (#1079)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-30 13:39:38 +05:30
Deshraj Yadav 9e2b232c13 Update version to 0.1.47 (#1077) 2023-12-30 00:16:31 +05:30
Sidharth Mohanty 404e73af77 [Feature] Add Dropbox loader (#1073)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2023-12-29 23:22:25 +05:30
Sidharth Mohanty a544b4d3ff Load local text files of any kind - code, txts, json etc (#1076) 2023-12-29 22:26:24 +05:30
Sidharth Mohanty 6df63d9ca7 Update notebooks to use dict instead of yaml and remove dataloaders (#1075) 2023-12-29 21:57:46 +05:30
Deshraj Yadav 904baac153 Update version to 0.1.46 (#1074) 2023-12-29 17:01:11 +05:30
Deven Patel a926bcc640 [Refactor] Converge Pipeline and App classes (#1021)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-29 16:52:41 +05:30
Deven Patel c0aafd38c9 [Feature] Return score when doing search in vectorDB (#1060)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-29 15:56:12 +05:30
Deven Patel 19d80914df [Improvement] return all the metadata when citations flag is True (#1059)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-29 14:48:41 +05:30
Sidharth Mohanty d9d529987e Migrate from template to prompt arg while keeping backward compatibility (#1066) 2023-12-28 23:36:33 +05:30
Deven Patel 12e6eaf802 [Docs] add docs for youtube channel (#1072)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-28 21:08:48 +05:30
Taranjeet Singh 7a026ea282 Fix link in github issue bug template (#1068) 2023-12-28 20:06:53 +05:30
Taranjeet Singh 97dc90169b docs: Update framework desc & link (#1067) 2023-12-28 19:39:22 +05:30
Sidharth Mohanty 64c02f374a Add cards for gradio and hf (#1061) 2023-12-28 09:03:31 +05:30
Sukkrit Sharma 6c1ea7799e [Feature] Add support for Together AI API (#1058) 2023-12-25 18:36:20 +05:30
Deshraj Yadav 6be29f5bed [Bug Fix] Fix FileExistsError when using Embedchain in multithreading setting (#1056) 2023-12-24 16:33:53 +05:30
Deven Patel 68737da7a2 [Update] update readme (#1054)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-23 16:38:32 +05:30
Deven Patel da388b679f [Chat PDF] update chat pdf logic (#1053)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-23 16:13:48 +05:30
Sidharth Mohanty 11f0d719f5 Provide openai-key support from config (#1052) 2023-12-23 14:42:18 +05:30
Deshraj Yadav e90673ae5b Added Dev Container Folder 2023-12-23 12:55:39 +05:30
Deven Patel f055028c6b [Bugfix] fix chat PDF dependencies (#1051)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-23 12:21:03 +05:30
Prikshit 106a338371 Add local file path support to SitemapLoader (#954) 2023-12-23 10:56:33 +05:30
Deven Patel 9fe80c5cca [App Deployment] create chat with PDF app (#1049)
Co-authored-by: Deven Patel <deven298@yahoo.com>
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2023-12-22 19:53:10 +05:30
Deshraj Yadav 050706e95e Update poetry.lock file (#1048) 2023-12-22 19:48:27 +05:30
Sidharth Mohanty 6d2389de1c [fix] streamlit app init by caching it (#1047) 2023-12-22 12:03:45 +05:30
Dhravya Shah dd97fad5a4 Fix: indents in yaml file (#1046) 2023-12-22 10:56:40 +05:30
Sukkrit Sharma 0f73ba9677 Added Support for Ollama for local model inference. (#1045)
Co-authored-by: Deshraj Yadav <deshraj@gatech.edu>
2023-12-22 05:10:00 +05:30
Sidharth Mohanty 210fe9bb80 [new] add gradio and hf spaces deployments (#1042) 2023-12-22 00:00:55 +05:30
Sidharth Mohanty ec8549d0e1 [fix] dot file and docs (#1044) 2023-12-21 18:50:12 +05:30
Deven Patel b77d9d750f [Bugfix] Improve/modal deployment (#1041)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-21 14:30:54 +05:30
Deshraj Yadav a10823d309 Add Unacademy AI demo (#1043) 2023-12-21 14:23:03 +05:30
Sidharth Mohanty 3a09c2bd62 [docs] add a faq on how to persist data (#1040) 2023-12-21 10:32:47 +05:30
Sidharth Mohanty a1394ce32e [chore] pypdf and bs4 by default in package (#1038) 2023-12-20 23:14:30 +05:30
Sidharth Mohanty 1020a4121f [new] streamlit deployment (#1034) 2023-12-20 23:02:51 +05:30
Sidharth Mohanty 737837ae0b [Feature] Add render.com deployment template (#1033)
Co-authored-by: Deven Patel <iamdevenpatel@gmail.com>
2023-12-20 22:38:23 +05:30
Deshraj Yadav 7ee2d0653b Update Sadhguru AI code (#1037) 2023-12-20 17:11:59 +05:30
Deshraj Yadav 43926fb527 Update introduction in README and docs (#1036) 2023-12-20 14:54:02 +05:30
Deshraj Yadav 7bcc9e35dd Update README (#1035) 2023-12-20 14:44:41 +05:30
Deshraj Yadav 6437661837 [Example] Add example for creating Sadhguru AI using Embedchain (#1032) 2023-12-19 20:21:53 +05:30
Sidharth Mohanty b5f84f27ff [chore] fix deployment link for modal.com card (#1031) 2023-12-19 20:09:58 +05:30
Sidharth Mohanty 48c38b5dc3 Telemetry support for ec cli commands (#1030) 2023-12-19 19:29:04 +05:30
Sidharth Mohanty b4f3bbbbc9 Add docs for modal.com deployment (#1029) 2023-12-19 16:37:58 +05:30
Deshraj Yadav 3cd50c4cd9 [Deployment] Setup fly.io deployment method and update docs (#1028)
Co-authored-by: sidmohanty11 <sidmohanty11@gmail.com>
2023-12-19 14:36:44 +05:30
Sidharth Mohanty cd2c40a9c4 OpenAI function calling support (#1011) 2023-12-18 19:34:15 +05:30
Sidharth Mohanty 33dcfe42b5 Improve Streamlit docs (#1025) 2023-12-18 18:25:17 +05:30
Deven Patel db37b2ac15 [Bugfix] fix chunker config bug (#1024)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-18 17:32:26 +05:30
Sidharth Mohanty bee4e834b1 Add docs for slack and add missing datatype (#1023) 2023-12-18 16:52:28 +05:30
Sidharth Mohanty 0272459435 Embedchain + Mistral Streamlit chatbot (#1017) 2023-12-18 14:42:19 +05:30
Deven Patel c0b5e93967 [Feature] add google ai embedder (#1019)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-18 13:58:01 +05:30
Sidharth Mohanty 6983ebba49 Chainlit + Embedchain Integration (Example) (#1020) 2023-12-18 13:20:56 +05:30
Sidharth Mohanty 9943d1e015 [chore] Remove deployment_name for openai embedder and update docs (#1022) 2023-12-18 08:50:49 +05:30
Sidharth Mohanty b348251484 [Docs] Don't need open ai key for gpt4all model (#1018) 2023-12-16 08:44:47 +05:30
xuxiang e719b5bac3 [Bug-fix] Fix error caused by executing Repo.clone_from twice (#1015)
Co-authored-by: xuxiang <xuxiang@aliyun.com>
2023-12-16 08:33:07 +05:30
Sidharth Mohanty 54f43215cd Docs for directory data loader usage (#1016) 2023-12-16 07:59:05 +05:30
Deven Patel b246d9823e [Docs] Documentation updates (#1014) 2023-12-15 17:02:50 +05:30
Deven Patel 65c8dd445b Update README to show package downloads (#1013) 2023-12-15 11:05:13 +05:30
Deven Patel 0efbc80ac9 [Doc update] update mistral example (#1012) 2023-12-15 06:12:53 +05:30
Deven Patel 151746beec [Feature] Add support for Google Gemini (#1009) 2023-12-15 06:10:55 +05:30
Deven Patel c0ee680546 [Improvement] Add support for min chunk size (#1007) 2023-12-15 05:59:15 +05:30
Sidharth Mohanty 9303a1bf81 [Feature] Add support for directory loader as data source (#1008) 2023-12-15 05:24:34 +05:30
Deshraj Yadav d54cdc5b00 [Docs] Revamp documentation (#1010) 2023-12-15 05:14:17 +05:30
Sidharth Mohanty b7a44ef472 Fix multiple top level packages (#1006) 2023-12-13 12:36:25 +05:30
Deven Patel ae6f866901 [improvement] update web page default chunk size to 2000 (#1005)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-13 12:36:13 +05:30
Sidharth Mohanty 7910cee259 [chore] fix wrong import of LLMs from langchain (#1002) 2023-12-11 05:41:51 +05:30
Deshraj Yadav d66e647f99 Update package version to v0.1.32 (#1003) 2023-12-11 05:41:17 +05:30
Deven Patel ff4a333be7 [Bugfix] fix sitemap loader (#1000)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-07 16:32:41 -08:00
Deshraj Yadav 111749a95d [Docs] Add documentation about getting source along with the answer (#999) 2023-12-07 14:54:16 -08:00
Deshraj Yadav adde398b65 Update package version to 0.1.30 (#998) 2023-12-07 14:23:21 -08:00
Sidharth Mohanty d8897ce356 [Feature] RSS Feed loader (#942) 2023-12-07 14:18:35 -08:00
Deven Patel 0ea8ab228c [Improvements] allow setting up the elasticsearch cloud instance (#997)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-07 14:17:59 -08:00
Sidharth Mohanty d62a23edf6 Substack loader improvements (#952)
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2023-12-07 14:12:50 -08:00
Sidharth Mohanty 51ebf3439b [New] Beehiiv loader (#963) 2023-12-07 14:11:56 -08:00
Deven Patel 4a5ed1dd8d Update ec query and chat function (#996)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-06 15:23:28 -08:00
Deven Patel e84b5034ea [Bugfix] fix return type of ec chat (#995)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-06 10:00:19 -08:00
Deven Patel a4831d6ed9 Bump package version to 0.1.27 (#994)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-06 00:03:13 -08:00
Deven Patel 51b4966801 [Improvements] Package improvements (#993)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-05 23:42:45 -08:00
atkinsh 1d4e00ccef local file path support for sitemap loader (#992) 2023-12-05 19:04:20 -08:00
Deven Patel c9fbc2e7d6 fix sitemap loader (#986)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-05 17:56:24 -08:00
Deshraj Yadav fa34788df6 Bump package version to 0.1.26 (#991) 2023-12-05 16:52:48 -08:00
Deven Patel 0f4f220119 Package improvements (#989)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-12-05 16:48:00 -08:00
Deven Patel 512cfc9466 [Improvement] customize add method (#988) 2023-12-05 00:55:33 -08:00
Deshraj Yadav 541b1cb7c7 Update version to 0.1.25 (#985) 2023-12-03 15:15:52 -08:00
Deven Patel 36af1a7615 [Improvement] improve github loader (#984) 2023-12-01 11:24:13 -08:00
Deshraj Yadav b02e8feeda [BugFix] Fix issue of chunks not getting embedded in opensearch index (#983) 2023-11-29 21:56:05 -08:00
Deven Patel 406c46e7f4 [Improvements] Add support for creating app from YAML string config (#980) 2023-11-29 12:25:30 -08:00
Sidharth Mohanty e35eaf1bfc Improve deps installation by converting them to one liner (#967) 2023-11-29 10:08:34 -08:00
Sidharth Mohanty 38426a7af1 Discord loader (#976) 2023-11-29 10:07:05 -08:00
Deshraj Yadav 141a23fb1e [BugFix] Skip checking thread when making sqlite connection (#978) 2023-11-26 15:44:06 -08:00
Sidharth Mohanty bb28569abf Update workflow to run when required (#941) 2023-11-24 09:29:31 -08:00
Deshraj Yadav 1df46b2bb3 [Bug fix] Fix issue of missing user directory (#975) 2023-11-24 09:26:59 -08:00
Deshraj Yadav 58f72e1ffe Update Azure OpenAI embedding model docs (#974) 2023-11-23 01:45:46 -08:00
Deshraj Yadav 33409140b4 [Bug fix] Fix Azure OpenAI related issue (#973) 2023-11-23 01:40:54 -08:00
Deshraj Yadav f6b80e01a1 [Feature] Add support for custom streaming callback (#971) 2023-11-22 01:06:33 -08:00
Sidharth Mohanty 798d3fcc5a Update version to 0.1.18 (#970) 2023-11-21 10:01:09 -08:00
Sidharth Mohanty 85f3ac428b Update embedding_fn signature to newest chroma db's (#969) 2023-11-21 09:42:11 -08:00
Deshraj Yadav 9fcf2130b5 [Feature] Improve github and youtube channel loader (#966)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-17 18:25:14 -08:00
Taranjeet Singh 51df00729e Import beautifulsoup pacakge lazily. (#964) 2023-11-17 18:19:08 -08:00
Deven Patel 023a61446f [Feature] Improve GitHub loader (#962) 2023-11-16 22:06:36 -08:00
Deshraj Yadav e0b73e6a5a [Loaders] Improve web page and sitemap loader usability (#961) 2023-11-16 16:01:43 -08:00
Deven Patel 28460f725c [Bugfix] fix poetry lock (#960) 2023-11-16 13:30:38 -08:00
Deshraj Yadav c93e49d2b8 [Bug fix] Update sleep time for substack loader and version bump (#958) 2023-11-15 19:35:30 -08:00
Deven Patel 07fb6bee54 [Features] Add Github and Youtube Channel loaders (#957)
Co-authored-by: Deven Patel <deven298@yahoo.com>
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2023-11-15 19:17:42 -08:00
Deshraj Yadav 3fa7db8420 Bump version to 0.1.13 (#956) 2023-11-15 18:42:48 -08:00
Deven Patel c14bd7b73b [Improvement] fix discourse loader to avoid rate limit (#953)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-15 15:33:16 -08:00
Sidharth Mohanty 5201beaab0 Bump version to 0.1.12 (#951) 2023-11-15 09:33:26 -08:00
Sidharth Mohanty 122313d8a5 [New] Substack loader (#949) 2023-11-14 21:52:15 -08:00
Deven Patel 82fd595306 [Improvements] improve package ux (#950)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-14 17:53:43 -08:00
Deven Patel 95c0d47236 [Feature] Discourse Loader (#948)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-13 16:39:11 -08:00
Deven Patel 919cc74e94 [Feature] Add MySQL Loader (#920)
Co-authored-by: Deven Patel <deven298@yahoo.com>
Co-authored-by: Deshraj Yadav <deshrajdry@gmail.com>
2023-11-13 13:21:36 -08:00
Deshraj Yadav d839991acb [Docs] Add back sitemap loader docstring (#947) 2023-11-13 13:08:09 -08:00
Deven Patel 539286aafd [Feature] Add Slack Loader (#932)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-13 13:06:01 -08:00
Sidharth Mohanty 23522b7b55 Add slack_bot docker image (#933) 2023-11-13 13:04:04 -08:00
UnMonsieur bf3fac56e4 Refactor: Make it clear what methods are private (#946) 2023-11-13 13:00:13 -08:00
Deshraj Yadav a5bf8e9075 [Improvement] Parallelize loading of sitemap urls 2023-11-13 12:53:34 -08:00
Deshraj Yadav 1d31b8f7e4 [Bugfix] Fix issue of "unable to open database file" (#945) 2023-11-13 12:37:00 -08:00
Deshraj Yadav b144c7dccc [Bugfix] Fix hugging face command (#944) 2023-11-13 12:17:27 -08:00
Deshraj Yadav 1364975396 [Feature] Add support for AIAssistant (#938) 2023-11-10 16:47:34 -08:00
Deven Patel deaa7f50f8 [Bug Fix] fix chromadb where clause for query and delete (#937)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-10 16:04:25 -08:00
Sidharth Mohanty 744ab5156f [Bug fix] missing dir on first init for App (#934) 2023-11-10 10:15:04 -08:00
Sidharth Mohanty c45413969a [refactor] Use pipeline for bots instead of App (#936) 2023-11-10 10:13:21 -08:00
Deshraj Yadav b314e5e080 [bug] Fix issue of missing user directory on first init (#931) 2023-11-09 22:29:41 -08:00
Deshraj Yadav 17129e2eaa [Improvement] Add support for reloading history for an existing app (#930) 2023-11-09 15:17:51 -08:00
Deven Patel 654fd8d74c [Improvement] Use SQLite for chat memory (#910)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-09 13:56:28 -08:00
Sidharth Mohanty 9d3568ef75 Update package version to 0.1.3 (#928) 2023-11-09 11:33:18 -08:00
Sidharth Mohanty 14712cac88 Deploy remaining bots and fix schema validation (#927) 2023-11-09 10:44:47 -08:00
Deshraj Yadav 0d568c758b [Feat] Add anonymous telemetry to assistant (#924) 2023-11-09 02:07:47 -08:00
Deshraj Yadav 7c6b88c7c5 [Docs] Update docs and improve assistant api (#923) 2023-11-09 01:16:19 -08:00
Deshraj Yadav 32c93be46e [chore] update poetry.lock file (#922) 2023-11-09 00:59:29 -08:00
Deven Patel 7de8d85199 [Feature] Add Postgres data loader (#918)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-08 23:50:46 -08:00
Deshraj Yadav f7dd65a3de [Feature] Add support for OpenAI assistants and support openai version >=1.0.0 (#921) 2023-11-08 22:49:03 -08:00
Sidharth Mohanty d8cdbe0041 Set check_same_thread false so that one App can be used in parallel (#911) 2023-11-08 10:26:31 -08:00
Sidharth Mohanty 2b8b6d3ea9 Chunker config docs (#913) 2023-11-08 10:25:45 -08:00
Sidharth Mohanty 936c7e389f Deploy Full stack docker image (#914) 2023-11-08 10:25:12 -08:00
Sidharth Mohanty 6864b4207b Dockerize discord bot and update docs to run the bot correctly (#919) 2023-11-08 10:24:57 -08:00
Deshraj Yadav 98eb5b54be [Docs] Update developer documentation (#916) 2023-11-07 19:07:47 -08:00
Deshraj Yadav 3332e6e236 [Docs] Update README (#915) 2023-11-07 18:12:27 -08:00
Deven Patel 0533da72d7 [Improvement] add delete functionality to zilliz DB (#912)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-07 02:46:39 -08:00
Sidharth Mohanty a1de238716 Introduce chunker config in yaml config (#907) 2023-11-06 09:43:15 -08:00
Deshraj Yadav f0d112254b [version] bump package version and minor cleanup (#909) 2023-11-05 16:34:36 -08:00
Sidharth Mohanty 830a7397ef Add yaml config validation (#890) 2023-11-04 22:23:55 -07:00
Deven Patel 5428765329 Beautify JSON docs (#906)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-03 13:52:33 -07:00
Sidharth Mohanty 23c912f2b7 Improve getting started page by adding steps (#904) 2023-11-03 13:40:40 -07:00
Sidharth Mohanty 9c4b023297 Use either embedder or embedding_model as YAML key (#905) 2023-11-03 13:40:16 -07:00
Deven Patel 53037b5ed8 [Feature Improvement] Update JSON Loader to support loading data from more sources (#898)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-03 10:00:27 -07:00
Sidharth Mohanty e2546a653d [chore] fix rest api docs and other minor fixes (#902) 2023-11-03 09:40:48 -07:00
Deshraj Yadav 4b8cada873 [REST API] Change docker image name and update docs (#901) 2023-11-03 01:08:27 -07:00
Deshraj Yadav fa3ca1d08a [REST API] Incorporate changes related to REST API docs (#900) 2023-11-03 00:42:55 -07:00
Sidharth Mohanty 8dd5cb9602 Add rest-api example (#889) 2023-11-03 00:32:51 -07:00
Deven Patel a054f7be9c [Data loader] Make json data loader work for other languages (#897)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-01 22:27:40 -07:00
Deven Patel df314dc6d1 Clean json data before loading (#895)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-11-01 21:52:34 -07:00
Deven Patel 930280f4ce [Feature] Add citations flag in query and chat functions of App to return context along with the answer (#859) 2023-11-01 13:06:28 -07:00
Deshraj Yadav 5022c1ae29 [misc] update poetry.lock file (#891) 2023-11-01 11:35:44 -07:00
Deshraj Yadav 6ced756a6b [misc] add json extra in pyproject.toml file (#886) 2023-10-31 23:55:25 -07:00
Deshraj Yadav b17268db50 [misc] remove jq as a dependency (#885) 2023-10-31 21:02:19 -07:00
Deshraj Yadav 476da37009 [version] bump package version to v0.0.87 (#883) 2023-10-31 12:40:02 -07:00
Deshraj Yadav 455f059c6f [Telemetry] Update anonymous telemetry API key (#882) 2023-10-31 12:34:54 -07:00
Deven Patel 5255a37c93 Embedchain json url support (#878)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-10-30 16:19:11 -07:00
Deven Patel 68dc274f72 Embedchain json loader update (#876)
Co-authored-by: Deven Patel <deven298@yahoo.com>
2023-10-30 15:30:49 -07:00
Deshraj Yadav 30228f7f8e [version] Update langchain to v0.0.303 (#875) 2023-10-30 15:02:38 -07:00
Sidharth Mohanty e15ef79ca9 Lazy load loaders and chunkers (#872) 2023-10-30 11:20:38 -07:00
Deshraj Yadav bc012a7518 [Docs] Update embedchain docs and analytics (#871) 2023-10-30 00:38:35 -07:00
Sidharth Mohanty 3b4409cfad Update notebooks to work with the latest version (#870) 2023-10-29 23:06:43 -07:00
Deshraj Yadav d3726134b2 [Docs] Update docs and minor improvements in search API (#869) 2023-10-29 16:50:14 -07:00
anujshandillya 5acb7f1c55 [fix]: updated twitter logo to new X (#868) 2023-10-29 14:37:32 -07:00
Deshraj Yadav 81336668b3 [Feature]: Add posthog anonymous telemetry and update docs (#867) 2023-10-29 01:20:21 -07:00
Deshraj Yadav 35c2b83015 [version] Update openai version to 0.28.0 (#866) 2023-10-28 19:18:57 -07:00
Deshraj Yadav cc1ee1deaa Fix dependencies in base package (#863) 2023-10-27 21:35:48 -07:00
Deshraj Yadav 29bd038579 [Bug fix] Fix missing dependency issue with gmail (#862) 2023-10-27 20:02:03 -07:00
Deshraj Yadav f6c4f86986 [Pipelines] Improvements in pipelines feature (#861) 2023-10-27 18:42:46 -07:00
Deven Patel 68183e9dce [Feature] Gmail Loader (#841) 2023-10-27 18:05:08 -07:00
Deshraj Yadav 78ec91a3a9 [Bug fix] Fix sqlite related issue with api server example (#857) 2023-10-26 21:54:45 -07:00
Deven Patel c95d458e52 Add tests for gpt4all llm (#852) 2023-10-26 20:41:28 -07:00
Deshraj Yadav 191ae3ec1e Update README (#855) 2023-10-26 11:11:05 -07:00
Deshraj Yadav ab9598d00a Update version to v0.0.77 (#851) 2023-10-26 09:51:51 -07:00
Deven Patel 0f8a2e624a [Improvement] Add support for gpt4all through langchain (#838) 2023-10-25 22:25:00 -07:00
Deven Patel d77e8da3f3 [Feature] Update db.query to return source of context (#831) 2023-10-25 22:20:32 -07:00
Sidharth Mohanty a27eeb3255 [Bug fix] import App shouldn't throw other llm deps errors (#837) 2023-10-25 20:48:53 -07:00
Deshraj Yadav 413ccb83e6 [Bug fix] Fix issues related to creating pipelines (#850) 2023-10-25 20:26:58 -07:00
Deven Patel 797bb567c6 [feat]: Add openapi spec data loader (#818) 2023-10-25 14:19:13 -07:00
Deshraj Yadav f2a5dc40ee Update package version to v0.0.76 (#849) 2023-10-25 13:40:00 -07:00
Deshraj Yadav 3979480532 [Feature] Add support for deploying local pipelines to Embedchain platform (#847) 2023-10-25 13:36:24 -07:00
Abhinesh 76f1993e7a Update CONTRIBUTING.md (#845) 2023-10-25 13:31:39 -07:00
Deshraj Yadav d783fa2b89 [chore]: Add poetry lock file (#848) 2023-10-25 12:53:06 -07:00
Sidharth Mohanty bbce18caac [Bug fix] reset() erases everything from db (#844) 2023-10-25 10:22:20 -07:00
Deshraj Yadav 3ce2d8a656 Bump version to v0.0.75 (#836) 2023-10-19 21:06:49 -07:00
Deshraj Yadav a5c86a2f5c [Bugfix]: Fix issue of context overspilling into other apps (#835) 2023-10-19 17:46:33 -07:00
Deshraj Yadav d18e533adf [Feature] Setup base for creating pipelines in embedchain (#834) 2023-10-19 17:46:15 -07:00
Sidharth Mohanty 2b881aaad0 Cache dependencies in CI (#832) 2023-10-19 13:59:02 -07:00
Sidharth Mohanty 39cc07608f [chore] Update repl links to launch main.py directly (#830) 2023-10-19 13:53:07 -07:00
Sidharth Mohanty 9894cfcced [docs] add links to example repositories (#833) 2023-10-19 13:51:08 -07:00
Sidharth Mohanty b5d80be037 Remove person_app, open_source app, llama2_app with their configs (#829) 2023-10-19 01:13:52 -07:00
Sidharth Mohanty b7870fbd9b [Bug fix] Table content alignment in example docs table (#828) 2023-10-18 21:38:31 -07:00
Sidharth Mohanty 36e6d486fc [chore] Remove image.jpg produced by tests and add cleanup in test (#827) 2023-10-18 21:38:00 -07:00
Sidharth Mohanty 2d5dc84f1a [chore] Update LLM YAML config docs (#826) 2023-10-18 21:37:45 -07:00
Deshraj Yadav b47405e1bd Update version to v0.0.74 (#825) 2023-10-18 17:12:51 -07:00
Muhammad Muzammil 8b64deab40 [Feature]: Unstructured File Loader Support - USF (#815) 2023-10-18 16:43:41 -07:00
Rupesh Bansal c8846e0e93 [Feature] Add Qdrant support (#822) 2023-10-18 14:27:57 -07:00
Deven Patel 7641cba01d [Feature] JSON data loader support (#816) 2023-10-18 13:53:15 -07:00
Sidharth Mohanty 4dc1785ef1 [docs] Examples table with notebooks and replit links (#823) 2023-10-18 10:29:41 -07:00
Sidharth Mohanty b2286f3e34 Google Colab Notebooks for LLMs, Embedders and VectorDBs (#821) 2023-10-18 09:04:06 -07:00
Sidharth Mohanty 65a20aa457 [Bug fix] Anthropic, Llama2 and VertexAI LLMs dependencies (#820) 2023-10-18 01:10:46 -07:00
Rupesh Bansal d8a7d71344 [Feature] Batch uploading in chromadb (#814) 2023-10-17 22:22:29 -07:00
Deshraj Yadav bb490df9a6 Bump version to v0.0.73 (#819) 2023-10-17 22:19:22 -07:00
Rupesh Bansal cdfd6519c8 [Feature] Add support for weaviate vector db (#782) 2023-10-17 22:18:53 -07:00
Sidharth Mohanty e8a2846449 Improve and add more tests (#807) 2023-10-17 14:06:47 -07:00
Sidharth Mohanty d065cbf934 [Bug fix] Qna pair not loading properly (#817) 2023-10-17 11:34:03 -07:00
Sidharth Mohanty 413b107b9a Update dependencies for fast installation (#811) 2023-10-17 08:21:30 -07:00
Deshraj Yadav c336292346 Update version to v0.0.72 (#809) 2023-10-16 13:33:36 -07:00
Deshraj Yadav adf50f1e81 [Docs] Add docs for Azure OpenAI provider (#804) 2023-10-16 13:31:56 -07:00
2243 changed files with 304284 additions and 39798 deletions
+20
View File
@@ -0,0 +1,20 @@
{
"name": "mem0-plugins",
"interface": {
"displayName": "Mem0 Plugins"
},
"plugins": [
{
"name": "mem0",
"source": {
"source": "local",
"path": "./mem0-plugin"
},
"policy": {
"installation": "AVAILABLE",
"authentication": "ON_INSTALL"
},
"category": "Productivity"
}
]
}
+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.2"
}
]
}
+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.1"
}
]
}
-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/gventuri/pandas-ai/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/
+130 -11
View File
@@ -3,35 +3,154 @@ name: ci
on:
push:
branches: [main]
paths:
- 'mem0/**'
- 'tests/**'
- 'embedchain/**'
- '.github/workflows/**'
- 'pyproject.toml'
pull_request:
paths:
- 'mem0/**'
- 'tests/**'
- 'embedchain/**'
- 'pyproject.toml'
jobs:
build:
changelog_check:
if: github.event_name == 'pull_request'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Require CHANGELOG entry when Python SDK version changes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
extract_version() {
python3 -c "import sys, re; m = re.search(r'^\s*version\s*=\s*\"([^\"]+)\"', sys.stdin.read(), re.M); print(m.group(1) if m else '')"
}
base_version=$(git show "$BASE_SHA:pyproject.toml" 2>/dev/null | extract_version || echo "")
head_version=$(extract_version < pyproject.toml)
echo "Base version: ${base_version:-<unknown>}"
echo "Head version: $head_version"
if [ -z "$base_version" ] || [ "$base_version" = "$head_version" ]; then
echo "pyproject.toml version unchanged — no CHANGELOG entry required."
exit 0
fi
echo "Detected version bump ${base_version} -> ${head_version}. Checking docs/changelog/sdk.mdx…"
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- docs/changelog/sdk.mdx | grep -q .; then
echo "Changelog update present in docs/changelog/sdk.mdx ✅"
else
echo "::error file=pyproject.toml::pyproject.toml version changed from ${base_version} to ${head_version} but docs/changelog/sdk.mdx was not updated in this PR. Add a new <Update> entry under the Python tab for v${head_version}."
exit 1
fi
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
run: pip install poetry==1.4.2
- 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-hatch-dependencies
uses: actions/cache@v3
with:
path: .venv
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
- 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: Test with pytest
- name: Run tests and generate coverage report
run: make test
- name: Generate coverage report
run: make coverage
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:
file: coverage.xml
env:
CODECOV_TOKEN: ${{ secrets.CODECOV_TOKEN }}
+46
View File
@@ -0,0 +1,46 @@
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: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm run build
- name: Publish to npm
run: |
if [ "${{ github.event.release.prerelease }}" = "true" ]; then
PREID=$(node -p "require('./package.json').version.split('-')[1].split('.')[0]")
npx npm@latest publish --provenance --access public --tag "$PREID"
else
npx npm@latest publish --provenance --access public
fi
+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)
+45
View File
@@ -0,0 +1,45 @@
name: docs - llms.txt check
# Blocks PRs that introduce new .mdx pages without a matching entry in
# docs/llms.txt, or that link to pages that no longer exist. Contributors
# must update docs/llms.txt in the same PR. Run locally with:
# python scripts/check-llms-txt-coverage.py # read-only
# python scripts/check-llms-txt-coverage.py --write # scaffold placeholders
on:
pull_request:
paths:
- 'docs/**/*.mdx'
- 'docs/llms.txt'
- 'scripts/check-llms-txt-coverage.py'
- 'scripts/llms-txt-ignore.txt'
workflow_dispatch: {}
permissions:
contents: read
jobs:
check-llms-txt:
runs-on: ubuntu-24.04-arm
timeout-minutes: 2
steps:
- uses: actions/checkout@v4
- name: Verify docs/llms.txt coverage
run: |
if ! python3 scripts/check-llms-txt-coverage.py; then
echo ""
echo "::error title=llms.txt out of sync::docs/llms.txt does not match docs/**/*.mdx."
echo ""
echo "To fix:"
echo " 1. Run locally: python scripts/check-llms-txt-coverage.py --write"
echo " This appends placeholder entries under '## Unclassified - needs triage'."
echo " 2. For each placeholder:"
echo " - replace [TODO: Platform|OSS|Both] with the correct scope tag"
echo " - rewrite the description as 'Use when ...'"
echo " - move the entry into the appropriate section"
echo " - delete the '## Unclassified - needs triage' heading once empty"
echo " 3. Resolve any stale URLs listed above by updating or removing the link."
echo " 4. Commit the updated docs/llms.txt to this PR."
exit 1
fi
+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
+46
View File
@@ -0,0 +1,46 @@
name: Publish @mem0/openclaw-mem0 📦 to npm
on:
release:
types: [published]
jobs:
build-n-publish:
name: Build and publish @mem0/openclaw-mem0 📦 to npm
if: startsWith(github.event.release.tag_name, 'openclaw-v')
runs-on: ubuntu-latest
permissions:
id-token: write
defaults:
run:
working-directory: openclaw
steps:
- uses: actions/checkout@v4
- name: Install pnpm
uses: pnpm/action-setup@v4
with:
version: 9
- 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: openclaw/pnpm-lock.yaml
- name: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm build
- name: Publish to npm
run: |
if [ "${{ github.event.release.prerelease }}" = "true" ]; then
PREID=$(node -p "require('./package.json').version.split('-')[1].split('.')[0]")
npx npm@latest publish --provenance --access public --tag "$PREID"
else
npx npm@latest publish --provenance --access public
fi
+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
+46
View File
@@ -0,0 +1,46 @@
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: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm run build
- name: Publish to npm
run: |
if [ "${{ github.event.release.prerelease }}" = "true" ]; then
PREID=$(node -p "require('./package.json').version.split('-')[1].split('.')[0]")
npx npm@latest publish --provenance --access public --tag "$PREID"
else
npx npm@latest publish --provenance --access public
fi
+146
View File
@@ -0,0 +1,146 @@
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/**'
changelog_check:
needs: check_changes
if: github.event_name == 'pull_request' && needs.check_changes.outputs.ts_sdk_changed == 'true'
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
with:
fetch-depth: 0
- name: Require CHANGELOG entry when SDK version changes
env:
BASE_SHA: ${{ github.event.pull_request.base.sha }}
HEAD_SHA: ${{ github.event.pull_request.head.sha }}
run: |
set -euo pipefail
base_version=$(git show "$BASE_SHA:mem0-ts/package.json" 2>/dev/null | jq -r .version || echo "")
head_version=$(jq -r .version mem0-ts/package.json)
echo "Base version: ${base_version:-<unknown>}"
echo "Head version: $head_version"
if [ -z "$base_version" ] || [ "$base_version" = "$head_version" ]; then
echo "mem0-ts/package.json version unchanged — no CHANGELOG entry required."
exit 0
fi
echo "Detected version bump ${base_version} -> ${head_version}. Checking docs/changelog/sdk.mdx…"
if git diff --name-only "$BASE_SHA" "$HEAD_SHA" -- docs/changelog/sdk.mdx | grep -q .; then
echo "Changelog update present in docs/changelog/sdk.mdx ✅"
else
echo "::error file=mem0-ts/package.json::mem0-ts/package.json version changed from ${base_version} to ${head_version} but docs/changelog/sdk.mdx was not updated in this PR. Add a new <Update> entry under the TypeScript tab for v${head_version}."
exit 1
fi
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
+46
View File
@@ -0,0 +1,46 @@
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: Install dependencies
run: pnpm install --frozen-lockfile
- name: Build
run: pnpm run build
- name: Publish to npm
run: |
if [ "${{ github.event.release.prerelease }}" = "true" ]; then
PREID=$(node -p "require('./package.json').version.split('-')[1].split('.')[0]")
npx npm@latest publish --provenance --access public --tag "$PREID"
else
npx npm@latest publish --provenance --access public
fi
+24 -5
View File
@@ -2,6 +2,11 @@
__pycache__/
*.py[cod]
*$py.class
**/node_modules/
# Self-hosted server local runtime state
server/history/
server/.env
# C extensions
*.so
@@ -14,8 +19,8 @@ dist/
downloads/
eggs/
.eggs/
lib/
lib64/
/lib/
/lib64/
parts/
sdist/
var/
@@ -76,7 +81,6 @@ docs/_build/
target/
# Jupyter Notebook
.ipynb_checkpoints
# IPython
profile_default/
@@ -104,7 +108,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,9 +169,24 @@ cython_debug/
# Database
db
test-db
!embedchain/embedchain/core/db/
.vscode
/poetry.lock
.idea/
.DS_Store
notebooks/*.yaml
.ipynb_checkpoints/
!configs/*.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"]
+585
View File
@@ -0,0 +1,585 @@
# AGENTS.md
This file provides context for AI coding assistants (Claude Code, Cursor, GitHub Copilot, Codex, etc.) working with the Mem0 repository.
## Project Overview
**Mem0** ("mem-zero") is an intelligent memory layer for AI agents and assistants. It provides persistent, personalized memory via both a hosted platform API and self-hosted open-source SDKs.
- **Repository**: https://github.com/mem0ai/mem0
- **Documentation**: https://docs.mem0.ai
- **License**: Apache-2.0
## Repository Structure
This is a **polyglot monorepo** containing Python and TypeScript packages, CLIs, servers, plugins, documentation, and evaluation tooling.
### Key Directories
| Directory | Description |
|-----------|-------------|
| `mem0/` | Core Python SDK (`mem0ai` on PyPI) — memory, LLMs, embeddings, vector stores, graphs, rerankers |
| `mem0-ts/` | TypeScript SDK (`mem0ai` on npm) — client + OSS memory |
| `cli/python/` | Python CLI (`mem0-cli` on PyPI) — Typer-based, entry point `mem0` |
| `cli/node/` | Node CLI (`@mem0/cli` on npm) — Commander-based, entry point `mem0` |
| `vercel-ai-sdk/` | `@mem0/vercel-ai-provider` — Vercel AI SDK memory provider |
| `openclaw/` | `@mem0/openclaw-mem0` — OpenClaw plugin for Claude Code / AI editors |
| `server/` | FastAPI REST server for self-hosted Mem0 (Docker: FastAPI + PostgreSQL/pgvector + Neo4j) |
| `openmemory/` | Self-hosted memory platform — `api/` (FastAPI + Alembic + MCP server) and `ui/` (Next.js 15 + React 19) |
| `mem0-plugin/` | AI editor plugins (Claude Code, Cursor, Codex) — MCP server connection, lifecycle hooks, skills |
| `skills/` | Claude Code skill definitions. Reference skills (SDK knowledge, always-on): `mem0/`, `mem0-cli/`, `mem0-vercel-ai-sdk/`. Pipeline skills (run on demand): `mem0-integrate/`, `mem0-test-integration/` |
| `docs/` | Documentation site (Mintlify) |
| `tests/` | Python SDK tests (pytest) |
| `evaluation/` | Benchmarking framework — LOCOMO evals, experiment runner, score generation |
| `examples/` | Sample projects — demo apps, Chrome extension, multi-agent patterns |
| `cookbooks/` | Jupyter notebooks — customer support chatbot, AutoGen integration |
| `embedchain/` | Legacy Embedchain RAG framework (maintained separately, Poetry-based) |
| `pr-reviews/` | Pull request review materials |
| `scripts/` | Repo-wide utility scripts (e.g., `check-llms-txt-coverage.py` for docs/llms.txt sync) |
### Core Package Dependencies
```
mem0 (Python SDK) mem0-ts (TypeScript SDK)
├── mem0/memory/ ├── src/client/ (MemoryClient — hosted)
├── mem0/llms/ └── src/oss/ (Memory — self-hosted)
├── mem0/embeddings/ ├── src/llms/
├── mem0/vector_stores/ ├── src/embeddings/
├── mem0/graphs/ ├── src/vector_stores/
└── mem0/reranker/ └── src/graphs/
cli/python/ ──▶ mem0ai (optional, for OSS mode)
cli/node/ ──▶ mem0ai (npm, for API calls)
vercel-ai-sdk/ ──▶ ai, @ai-sdk/* providers
openclaw/ ──▶ mem0ai (npm)
```
## Development Setup
### Requirements
- **Python**: 3.9+ (3.10+ for CLI)
- **Node.js**: v18+ (v20 or v22 recommended)
- **pnpm**: v10+ (`npm install -g pnpm@10`) — used for all TypeScript packages
- **Hatch**: Python build/environment tool (`pip install hatch`)
- **Docker**: Required for `server/` and `openmemory/` development
### Initial Setup
```bash
# Python SDK
hatch shell dev_py_3_11 # creates environment with all deps
pre-commit install # install git hooks
# TypeScript packages
cd mem0-ts && pnpm install # TS SDK
cd cli/node && pnpm install # Node CLI
cd vercel-ai-sdk && pnpm install # Vercel AI provider
cd openclaw && pnpm install # OpenClaw plugin
```
## Build, Lint, and Test Commands
### Python SDK (`mem0/`)
```bash
# Environment setup (uses Hatch)
hatch shell dev_py_3_11 # or dev_py_3_9, dev_py_3_10, dev_py_3_12
# Linting and formatting
make lint # ruff check
make format # ruff format
make sort # isort mem0/
# Tests
make test # pytest tests/
make test-py-3.9 # test specific Python version (3.9–3.12)
# Build and publish
make build # hatch build
make publish # hatch publish
```
- **Python:** 3.9, 3.10, 3.11, 3.12
- **Linter/formatter:** Ruff (line length **120**)
- **Import sorting:** isort (`profile = "black"`)
- **Test framework:** pytest (with pytest-mock, pytest-asyncio)
- **Pre-commit hooks:** ruff + isort — run `pre-commit install` before committing
### TypeScript SDK (`mem0-ts/`)
```bash
cd mem0-ts
pnpm install
pnpm run build # tsup
pnpm run test # jest (all tests)
pnpm run test:unit # jest --coverage (unit tests only)
pnpm run test:integration # jest (integration tests, needs MEM0_API_KEY)
pnpm run test:ci # jest --coverage --ci (CI mode)
pnpm run test:watch # jest watch mode
```
- **Node:** 20, 22 (CI-tested)
- **Build:** tsup (CJS + ESM)
- **Test:** jest
- **Formatter:** prettier
### Python CLI (`cli/python/`)
```bash
cd cli/python
pip install -e ".[dev]" # dev install with ruff + pytest
ruff check . # lint
ruff format . # format
pytest # test
hatch build # build
```
- **Python:** 3.10+ (not 3.9)
- **Linter/formatter:** Ruff (line length **100** — different from root SDK)
- **Ruff rules:** E, F, I, W, UP, B, SIM, RUF (ignores E501, B008 for Typer patterns, SIM108)
- **Framework:** Typer + Rich + httpx
- **Entry point:** `mem0 = "mem0_cli.app:main"`
- **Source layout:** `src/mem0_cli/`
- **Optional dependency:** `mem0ai` (for OSS mode, via `[oss]` extra)
### Node CLI (`cli/node/`)
```bash
cd cli/node
pnpm install
pnpm run build # tsup
pnpm run lint # biome check src/
pnpm run lint:fix # biome check --write src/
pnpm run typecheck # tsc --noEmit
pnpm run test # vitest run
pnpm run test:watch # vitest (watch mode)
pnpm run dev # tsx src/index.ts (development)
```
- **Node:** 18+ required
- **Build:** tsup (ESM)
- **Linter:** Biome (not ESLint, not Ruff)
- **Test:** vitest (not jest)
- **Framework:** Commander + Chalk + ora + cli-table3
### Vercel AI SDK Provider (`vercel-ai-sdk/`)
```bash
cd vercel-ai-sdk
pnpm install
pnpm run build # tsup
pnpm run lint # eslint
pnpm run type-check # tsc --noEmit
pnpm run prettier-check # prettier --check
pnpm run test # jest
pnpm run test:edge # vitest (edge runtime)
pnpm run test:node # vitest (node runtime)
```
- **Build:** tsup (CJS + ESM)
- **Lint:** ESLint + Prettier
- **Test:** jest + vitest (edge/node configs)
### OpenClaw Plugin (`openclaw/`)
```bash
cd openclaw
pnpm install
pnpm run build # tsup
pnpm run test # vitest run
```
- **Build:** tsup (ESM)
- **Test:** vitest (with Codecov in CI)
- **Plugin manifest:** `openclaw.plugin.json`
### Server (`server/`)
```bash
# Docker production build
cd server
make build # docker build -t mem0-api-server .
make run_local # docker run -p 8000:8000 with .env
# Docker Compose development (FastAPI + PostgreSQL/pgvector + Neo4j)
cd server
docker-compose up # starts all 3 services
# mem0 API: localhost:8888
# PostgreSQL: localhost:8432
# Neo4j HTTP: localhost:8474, Bolt: localhost:8687
```
- **Framework:** FastAPI with uvicorn (auto-reload in dev)
- **Services:** PostgreSQL with pgvector, Neo4j 5.x with APOC plugin
- **Hot reload:** Dev Dockerfile mounts `server/` and `mem0/` for live changes
### OpenMemory (`openmemory/`)
```bash
# Full stack via Docker Compose
cd openmemory
docker-compose up
# Qdrant: localhost:6333
# API (MCP): localhost:8765
# UI: localhost:3000
# Individual development
cd openmemory/api && uvicorn main:app --reload # FastAPI backend
cd openmemory/ui && npm run dev # Next.js frontend
# Tests
cd openmemory/api && pytest tests/ # API tests (e.g., test_mcp_server.py)
```
- **API:** FastAPI + Alembic (DB migrations) + MCP server (Model Context Protocol)
- **UI:** Next.js 15, React 19, Radix UI, Redux Toolkit, TailwindCSS, Recharts
- **Vector store:** Qdrant
### Documentation (`docs/`)
```bash
make docs # or: cd docs && mintlify dev
```
- **Framework:** Mintlify
- **API spec:** `docs/openapi.json`
- **Structure:** `api-reference/`, `open-source/`, `platform/`, `integrations/`, `cookbooks/`, `core-concepts/`
### Evaluation (`evaluation/`)
```bash
cd evaluation
make run-mem0-add # Run mem0 add experiments
make run-mem0-search # Run mem0 search experiments
make run-mem0-plus-add # With graph memory
make run-mem0-plus-search # With graph memory
make run-rag # RAG baseline
make run-full-context # Full context baseline
make run-langmem # LangMem comparison
make run-openai # OpenAI comparison
```
## Core APIs
### Python
| Function / Class | Purpose | Import |
|-----------------|---------|--------|
| `Memory` | Self-hosted memory (sync) | `from mem0 import Memory` |
| `AsyncMemory` | Self-hosted memory (async) | `from mem0 import AsyncMemory` |
| `MemoryClient` | Hosted platform client (sync) | `from mem0 import MemoryClient` |
| `AsyncMemoryClient` | Hosted platform client (async) | `from mem0 import AsyncMemoryClient` |
**Key `Memory` / `MemoryClient` methods:**
| Method | Purpose |
|--------|---------|
| `add(messages, *, user_id, agent_id, run_id, metadata)` | Store a new memory |
| `search(query, *, user_id, agent_id, run_id, limit, filters)` | Search memories |
| `get(memory_id)` | Retrieve a single memory by ID |
| `get_all(*, user_id, agent_id, run_id, limit)` | List all memories |
| `update(memory_id, data)` | Update a memory |
| `delete(memory_id)` | Delete a memory |
| `delete_all(*, user_id, agent_id, run_id)` | Delete all memories |
| `history(memory_id)` | Get change history for a memory |
### TypeScript
| Export | Purpose | Import |
|--------|---------|--------|
| `MemoryClient` | Hosted platform client | `import { MemoryClient } from 'mem0ai'` |
| `Memory` | Self-hosted OSS memory | `import { Memory } from 'mem0ai/oss'` |
## Import Patterns
### Python
| What | Import |
|------|--------|
| Core memory classes | `from mem0 import Memory, AsyncMemory` |
| Platform client | `from mem0 import MemoryClient, AsyncMemoryClient` |
| Configuration | `from mem0.configs.base import MemoryConfig` |
| LLM providers | `from mem0.llms.<provider> import <ProviderLLM>` |
| Embedding providers | `from mem0.embeddings.<provider> import <ProviderEmbedding>` |
| Vector store providers | `from mem0.vector_stores.<provider> import <ProviderVectorStore>` |
### TypeScript
| What | Import |
|------|--------|
| Hosted client | `import { MemoryClient } from 'mem0ai'` |
| OSS memory | `import { Memory } from 'mem0ai/oss'` |
| Specific providers (OSS) | `import { OpenAIEmbedding } from 'mem0ai/oss'` |
## Coding Standards
### File Naming Conventions
- **Python source files:** `snake_case.py` (e.g., `azure_openai.py`, `cohere_reranker.py`)
- **Python test files:** `test_<module>.py` (e.g., `test_memory.py`, `test_main.py`)
- **TypeScript source files:** `snake_case.ts` (e.g., `azure_ai_search.ts`)
- **TypeScript test files:** `<module>.test.ts` (e.g., `memory.test.ts`)
- **Config/manifest files:** `kebab-case` (e.g., `openclaw.plugin.json`, `jest.config.js`)
### Python Conventions
- **Provider pattern:** All providers (LLMs, embeddings, vector stores, graphs, rerankers) inherit from a `base.py` abstract class in their directory. Config classes live in `configs.py`.
- **Pydantic v2** for all data models and configuration.
- **Ruff** is the single linting and formatting tool — no black, no flake8.
- Root SDK: line length **120**
- Python CLI: line length **100** with extended rule set (UP, B, SIM, RUF)
- **isort** with `profile = "black"` for import sorting.
- Ruff excludes `embedchain/` and `openmemory/` from root config.
### TypeScript Conventions
- **Build:** tsup across all packages.
- **Package manager:** pnpm everywhere (no npm, no yarn).
- **TypeScript strict mode** across all packages.
- **Linting varies by package:**
| Package | Linter | Formatter | Test Framework |
|---------|--------|-----------|---------------|
| `mem0-ts/` | — | Prettier | jest |
| `cli/node/` | Biome | Biome | vitest |
| `vercel-ai-sdk/` | ESLint | Prettier | jest + vitest |
| `openclaw/` | — | — | vitest |
### Type Checking
Always run type checking after modifying TypeScript code:
```bash
cd <package> && pnpm run typecheck # or: tsc --noEmit
```
## Architecture
### Provider Pattern
The SDK uses a consistent plugin architecture across 5 categories. Each category has a `base.py` abstract class and concrete provider implementations:
| Category | Count | Examples |
|----------|-------|---------|
| **LLMs** | 24 | OpenAI, Anthropic, AWS Bedrock, Azure OpenAI, Gemini, Groq, Ollama, Together, DeepSeek, vLLM, LiteLLM, LM Studio, xAI |
| **Vector Stores** | 30 | Qdrant, Pinecone, Chroma, Weaviate, Milvus, MongoDB, Redis, Elasticsearch, pgvector, Supabase, Faiss, S3 Vectors |
| **Embeddings** | 15 | OpenAI, Azure OpenAI, Gemini, HuggingFace, FastEmbed, Together, AWS Bedrock, Ollama, Vertex AI |
| **Graph Stores** | 4 | Neo4j, Memgraph, Kuzu, Apache AGE |
| **Rerankers** | 5 | Cohere, HuggingFace, LLM-based, Sentence Transformer, Zero Entropy |
### Two Usage Modes
Self-hosted `Memory` / `AsyncMemory` classes and hosted-platform `MemoryClient` — both in Python and TypeScript.
### Graph Memory
Optional layer on top of vector memory for relationship-aware retrieval. Configured via the `graph` section of `MemoryConfig`.
### MCP Integration
Model Context Protocol support in multiple places:
- **Remote:** MCP server at `mcp.mem0.ai`
- **Local:** MCP server in `openmemory/api/` (FastAPI-based)
- **Plugin:** MCP tools in `mem0-plugin/` — 9 tools: `add_memory`, `search_memories`, `get_memories`, `get_memory`, `update_memory`, `delete_memory`, `delete_all_memories`, `delete_entities`, `list_entities`
### Plugin & Skills System
- `mem0-plugin/` provides integrations for Claude Code, Cursor, and Codex via MCP server connections and lifecycle hooks for automatic memory capture.
- `skills/` contains structured skill definitions for AI agents, split into two categories:
- **Reference skills** (always-on SDK knowledge): `mem0` (Python + TS SDKs, framework integrations), `mem0-cli` (terminal workflows), `mem0-vercel-ai-sdk` (Vercel AI provider).
- **Pipeline skills** (run on demand): `mem0-integrate` wires Mem0 into an existing repo via a TDD pipeline; `mem0-test-integration` verifies what the integrator produced on the same branch. The two are loosely coupled via `.mem0-integration/` artifacts.
### Adding a New Provider
To add a new LLM, embedding, vector store, or reranker provider:
1. Create `mem0/<category>/<provider_name>.py`
2. Inherit from the abstract base class in `mem0/<category>/base.py`
3. Add configuration to `mem0/<category>/configs.py` (if the category uses one)
4. Register the provider in `mem0/<category>/__init__.py`
5. Add tests in `tests/<category>/<provider_name>/`
6. Add any new dependencies to the appropriate optional group in `pyproject.toml` (never to core `dependencies`)
7. Follow the exact pattern of existing providers in the same category — match method signatures, error handling, and config structure
## CI/CD
### CI Workflows (automated testing)
| Workflow | File | Triggers | Tests |
|----------|------|----------|-------|
| Python SDK | `ci.yml` | Push to main, PRs on `mem0/`, `tests/`, `pyproject.toml` | Ruff lint + pytest on Python 3.10, 3.11, 3.12 |
| TypeScript SDK | `ts-sdk-ci.yml` | Push to main, PRs on `mem0-ts/` | Prettier + build + jest on Node 20, 22 |
| Python CLI | `cli-python-ci.yml` | Push to `cli/python/`, PRs, manual | Ruff lint + pytest + hatch build on Python 3.10, 3.11, 3.12 |
| Node CLI | `cli-node-ci.yml` | Push to `cli/node/`, PRs, manual | Biome lint + tsc + vitest + tsup build on Node 20, 22 |
| OpenClaw | `openclaw-checks.yml` | Push to `openclaw/`, PRs, manual | tsc + vitest (with Codecov) + tsup build on Node 20, 22 |
| Embedchain | `ci.yml` (shared) | PRs on `embedchain/` | Ruff + pytest + coverage on Python 3.9–3.12 |
### CD Workflows (automated publishing)
| Workflow | File | Tag Prefix | Target |
|----------|------|------------|--------|
| Python SDK | `cd.yml` | `v*` | PyPI (`mem0ai`) |
| TypeScript SDK | `ts-sdk-cd.yml` | `ts-v*` | npm (`mem0ai`) |
| Python CLI | `cli-python-cd.yml` | `cli-v*` | PyPI (`mem0-cli`) |
| Node CLI | `cli-node-cd.yml` | `cli-node-v*` | npm (`@mem0/cli`) |
| Vercel AI SDK | `vercel-ai-cd.yml` | `vercel-ai-v*` | npm (`@mem0/vercel-ai-provider`) |
| OpenClaw | `openclaw-cd.yml` | `openclaw-v*` | npm (`@mem0/openclaw-mem0`) |
- All publishing uses **OIDC trusted publishing** — no tokens or secrets required.
- First publish of a new npm package must be done manually; OIDC works for subsequent versions.
### Utility Workflows
| Workflow | File | Purpose |
|----------|------|---------|
| Issue Labeler | `issue-labeler.yml` | Automatic issue labeling |
| Stale Bot | `stale.yml` | Marks stale issues and PRs |
| llms.txt Check | `docs-llms-txt-check.yml` | Blocks PRs touching `docs/**/*.mdx` when `docs/llms.txt` is out of sync. Fix locally with `python scripts/check-llms-txt-coverage.py --write`. |
## Task Completion Guidelines
These guidelines outline typical artifacts for different task types. Use judgment to adapt based on scope and context.
### Bug Fixes
1. **Unit tests**: Add tests that would fail without the fix (regression tests)
2. **Implementation**: Fix the bug
3. **Manual verification**: Run the relevant test suite to confirm the fix
4. **Lint**: Run the appropriate linter for the package you modified
### New Features
1. **Implementation**: Build the feature following existing patterns
2. **Unit tests**: Comprehensive test coverage for new functionality
3. **Documentation**: Update relevant docs in `docs/` for public APIs
4. **Examples**: Add usage examples if the feature introduces new user-facing behavior
5. **llms.txt**: Any new `.mdx` page under `docs/` must be linked in `docs/llms.txt` with a scope tag (`[Platform]` / `[OSS]` / `[Both]`) and a `Use when ...` description. The `docs-llms-txt-check.yml` workflow runs on every PR that touches docs and **fails the check** if the index is out of sync. To fix: run `python scripts/check-llms-txt-coverage.py --write` locally to scaffold placeholders under `## Unclassified - needs triage`, then replace the `[TODO: ...]` tags, rewrite descriptions as `Use when ...`, move entries into the right section, and delete the triage heading when empty.
### New Provider (LLM / Embedding / Vector Store / Reranker)
1. **Implementation**: Follow the "Adding a New Provider" steps above
2. **Tests**: Add unit tests matching the pattern of existing providers
3. **Configuration**: Add to the appropriate `configs.py` and `__init__.py`
4. **Dependencies**: Add to the correct optional group in `pyproject.toml`
5. **Documentation**: Add an integration guide in `docs/integrations/`
### Refactoring / Internal Changes
- Unit tests for any changed behavior
- No documentation needed for internal-only changes
- Ensure all existing tests still pass
### When to Deviate
These are guidelines, not rigid rules. Adjust based on:
- **Scope**: Trivial fixes (typos, comments) may not need tests
- **Visibility**: Internal changes may not need documentation
- **Context**: Some changes span multiple categories — use judgment
When uncertain about expected artifacts, ask for clarification.
## Contributing Guidelines
### Workflow
1. Fork and clone the repository.
2. Create a feature branch from `main` (e.g., `feature/my-new-feature`).
3. Make your changes — add tests, docs, and examples as appropriate.
4. Run linting and tests for every package you modified (see commands above).
5. Run `pre-commit install` on first setup — hooks run ruff + isort automatically.
6. Commit with a clear message following [Conventional Commits](https://www.conventionalcommits.org/) (e.g., `feat:`, `fix:`, `docs:`, `refactor:`).
7. Push and open a Pull Request against `main`.
### Pull Request Requirements
Every PR must follow the repo's PR template (`.github/PULL_REQUEST_TEMPLATE.md`):
1. **Linked Issue** — Reference the issue with `Closes #<number>`. If no issue exists, create one first or explain why in the description.
2. **Description** — Explain what the PR does and why it's needed.
3. **Type of Change** — Check the appropriate box:
- Bug fix / New feature / Breaking change / Refactor / Documentation update
4. **Breaking Changes** — If applicable, describe what breaks and the migration path.
5. **Test Coverage** — Check what applies:
- Added/updated unit tests
- Added/updated integration tests
- Tested manually (describe how)
- No tests needed (explain why)
6. **Checklist** — All must be checked before merge:
- [ ] Code follows the project's style guidelines
- [ ] Self-review performed
- [ ] Tests added that prove the fix/feature works
- [ ] New and existing tests pass locally
- [ ] Documentation updated if needed
### PR Description Template
```markdown
## Linked Issue
Closes #<!-- issue number -->
## Description
<!-- What does this PR do? Why is it needed? -->
## Type of Change
- [ ] 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
## Breaking Changes
N/A
## Test Coverage
- [ ] I added/updated unit tests
- [ ] I added/updated integration tests
- [ ] I tested manually (describe below)
- [ ] No tests needed (explain why)
## Checklist
- [ ] 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
```
### General Rules
- Follow existing code patterns — don't introduce new frameworks or abstractions without discussion.
- Version bumps go in `pyproject.toml` (Python) or `package.json` (TypeScript).
- For `server/` and `openmemory/` work, use Docker Compose for local development.
- Do NOT use `pip` or `conda` for dependency management — use `hatch` (see `docs/contributing/development.mdx`).
### Contributing Guides
| Task | Guide |
|------|-------|
| Code contributions | `docs/contributing/development.mdx` |
| Documentation contributions | `docs/contributing/documentation.mdx` |
| PR template | `.github/PULL_REQUEST_TEMPLATE.md` |
| Bug reports | `.github/ISSUE_TEMPLATE/bug_report.yml` |
| Feature requests | `.github/ISSUE_TEMPLATE/feature_request.yml` |
| Documentation issues | `.github/ISSUE_TEMPLATE/documentation_issue.yml` |
## Do NOT
- Modify CI/CD workflows without explicit approval.
- Add new Python dependencies to the core `dependencies` list in `pyproject.toml` without discussion — use optional dependency groups instead.
- Commit `.env` files, API keys, or credentials.
- Modify `embedchain/` unless specifically working on that package — it has its own build system (Poetry).
- Skip pre-commit hooks.
- Use npm or yarn in TypeScript packages — this repo uses pnpm exclusively.
- Use `require()` for imports in TypeScript — use ES module `import` syntax.
- Mix up linter configs: root Python SDK uses line-length 120, Python CLI uses 100, Node CLI uses Biome (not ESLint/Ruff).
- Modify `openmemory/` database migrations without understanding the Alembic migration chain.
- Change public APIs without updating documentation in `docs/`.
Symlink
+1
View File
@@ -0,0 +1 @@
AGENTS.md
+57 -40
View File
@@ -1,74 +1,91 @@
# Contributing to embedchain
# Contributing to mem0
Let us make contributing easy, collaborative and fun.
Let us make contribution easy, collaborative and fun.
## Submit your Contribution through PR
To make a contribution, follow the following steps:
To make a contribution, follow these steps:
1. Fork and clone this repository
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
To ensure our standards, make sure to install pre-commit before star to contribute.
To ensure our standards, make sure to install pre-commit before starting to contribute.
```bash
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 Format 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` |
| `@mem0/openclaw-mem0` | npm | `openclaw-v*` | `openclaw-v1.0.1` |
#### 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!")
```
+40 -32
View File
@@ -1,44 +1,52 @@
# 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
lint:
poetry run ruff .
rm -rf dist
test:
poetry run pytest
hatch run test
coverage:
poetry run pytest --cov=$(PROJECT_NAME) --cov-report=xml
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
+210 -64
View File
@@ -1,103 +1,249 @@
# embedchain
<p align="center">
<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>
[![PyPI](https://img.shields.io/pypi/v/embedchain)](https://pypi.org/project/embedchain/)
[![Slack](https://img.shields.io/badge/slack-embedchain-brightgreen.svg?logo=slack)](https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw)
[![Discord](https://dcbadge.vercel.app/api/server/6PzXDgEjG5?style=flat)](https://discord.gg/CUU9FPhRNt)
[![Twitter](https://img.shields.io/twitter/follow/embedchain)](https://twitter.com/embedchain)
[![Substack](https://img.shields.io/badge/Substack-%23006f5c.svg?logo=substack)](https://embedchain.substack.com/)
[![Open in Colab](https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667)](https://colab.research.google.com/drive/138lMWhENGeEu7Q1-6lNbNTHGLZXBBz_B?usp=sharing)
[![codecov](https://codecov.io/gh/embedchain/embedchain/graph/badge.svg?token=EMRRHZXW1Q)](https://codecov.io/gh/embedchain/embedchain)
<p align="center">
<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>
Embedchain is a Data Platform for LLMs - load, index, retrieve, and sync any unstructured data. Using embedchain, you can easily create LLM powered apps over any data. If you want a javascript version, check out [embedchain-js](https://github.com/embedchain/embedchain/tree/main/embedchain-js)
<p align="center">
<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/mem0ai">
<img src="https://img.shields.io/pypi/dm/mem0ai" alt="Mem0 PyPI - Downloads">
</a>
<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://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://www.npmjs.com/package/mem0ai" target="blank">
<img src="https://img.shields.io/npm/v/mem0ai" alt="Npm package">
</a>
<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>
## Community
<p align="center">
<a href="https://mem0.ai/research"><strong>📄 Benchmarking Mem0's token-efficient memory algorithm →</strong></a>
</p>
* Join embedchain community on slack by accepting [this invite](https://join.slack.com/t/embedchain/shared_invite/zt-22uwz3c46-Zg7cIh5rOBteT_xe1jwLDw)
## New Memory Algorithm (April 2026)
## 🤝 Schedule a 1-on-1 Session
| Benchmark | Old | New | Tokens | Latency p50 |
| --- | --- | --- | --- | --- |
| **LoCoMo** | 71.4 | **91.6** | 7.0K | 0.88s |
| **LongMemEval** | 67.8 | **94.8** | 6.8K | 1.09s |
| **BEAM (1M)** | — | **64.1** | 6.7K | 1.00s |
| **BEAM (10M)** | — | **48.6** | 6.9K | 1.05s |
Book a [1-on-1 Session](https://cal.com/taranjeetio/ec) with Taranjeet, the founder, to discuss any issues, provide feedback, or explore how we can improve Embedchain for you.
All benchmarks run on the same production-representative model stack. Single-pass retrieval (one call, no agentic loops).
## 🔧 Quick install
**What changed:**
- **Single-pass ADD-only extraction** -- one LLM call, no UPDATE/DELETE. Memories accumulate; nothing is overwritten.
- **Agent-generated facts are first-class** -- when an agent confirms an action, that information is now stored with equal weight.
- **Entity linking** -- entities are extracted, embedded, and linked across memories for retrieval boosting.
- **Multi-signal retrieval** -- semantic, BM25 keyword, and entity matching scored in parallel and fused.
- **Temporal Reasoning** -- time-aware retrieval that ranks the right dated instance for queries about current state, past events, and upcoming plans.
See the [migration guide](https://docs.mem0.ai/migration/oss-v2-to-v3) for upgrade instructions. The [evaluation framework](https://github.com/mem0ai/memory-benchmarks) is open-sourced so anyone can reproduce the numbers.
## Research Highlights
- **91.6 on LoCoMo** -- +20 points over the previous algorithm
- **94.8 on LongMemEval** -- +27 points, with +53.6 on assistant memory recall
- **64.1 on BEAM (1M)** -- production-scale memory evaluation at 1M tokens
- [Read the full paper](https://mem0.ai/research)
# Introduction
[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.
### Key Features & Use Cases
**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
**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>
> **Agent Mode — for AI agents setting up Mem0 autonomously.** Run `mem0 init --agent --agent-caller <your-name> --json` (after `pip install mem0-cli` or `npm install -g @mem0/cli`), replacing `<your-name>` with your AI agent identity (e.g. `claude-code`, `cursor`, `codex`). This mints an evaluation Mem0 API key in <5 seconds — no email, no dashboard, no OTP. If you forgot to pass `--agent-caller`, run `mem0 identify <your-name>` after init. Use the key immediately against the Platform or the MCP server. The human owner can claim ownership later with `mem0 init --email <their-email>`: memories transfer, the same key keeps working, and the agent isn't disrupted.
| | Library | Self-Hosted Server | Cloud Platform |
|---|---------|-------------------|----------------|
| **Best for** | Testing, prototyping | Teams running on their own infrastructure | Zero-ops production use |
| **Setup** | `pip install mem0ai` | `docker compose up` | Sign up at [app.mem0.ai](https://app.mem0.ai?utm_source=oss&utm_medium=readme) |
| **Dashboard** | -- | [Yes](https://docs.mem0.ai/open-source/setup) | Yes |
| **Auth & API Keys** | -- | Yes | Yes |
| **Advanced Features** | -- | Teasers | All included |
Just testing? Use the library. Building for a team? Self-hosted. Want zero ops? Cloud.
### Library (pip / npm)
```bash
pip install --upgrade embedchain
pip install mem0ai
```
## 🔍 Demo
For enhanced hybrid search with BM25 keyword matching and entity extraction, install with NLP support:
Try out embedchain in your browser:
```bash
pip install mem0ai[nlp]
python -m spacy download en_core_web_sm
```
[![Open in Colab](https://camo.githubusercontent.com/84f0493939e0c4de4e6dbe113251b4bfb5353e57134ffd9fcab6b8714514d4d1/68747470733a2f2f636f6c61622e72657365617263682e676f6f676c652e636f6d2f6173736574732f636f6c61622d62616467652e737667)](https://colab.research.google.com/drive/17ON1LPonnXAtLaZEebnOktstB_1cJJmh?usp=sharing)
Install sdk via npm:
## 📖 Documentation
```bash
npm install mem0ai
```
The documentation for embedchain can be found at [docs.embedchain.ai](https://docs.embedchain.ai).
### Self-Hosted Server
## 💻 Usage
> **Note:** Self-hosted auth is on by default. Upgrading from a pre-auth build? Set `ADMIN_API_KEY`, register an admin through the wizard, or `AUTH_DISABLED=true` for local dev only. See [upgrade notes](https://docs.mem0.ai/open-source/setup#upgrade-notes).
Embedchain empowers you to create ChatGPT like apps, on your own dynamic dataset.
```bash
# Recommended: one command — start the stack, create an admin, issue the first API key.
cd server && make bootstrap
### Data Types Supported
# Manual: start the stack and finish setup via the browser wizard.
cd server && docker compose up -d # http://localhost:3000
```
* Youtube video
* PDF file
* Web page
* Sitemap
* Doc file
* Code documentation website loader
* Notion and many more.
See the [self-hosted docs](https://docs.mem0.ai/open-source/overview) for configuration.
You can find the full list of data types on [our documentation](https://docs.embedchain.ai/data-sources/csv).
### Cloud Platform
### Queries
1. Sign up on [Mem0 Platform](https://app.mem0.ai?utm_source=oss&utm_medium=readme)
2. Embed the memory layer via SDK or API keys
For example, you can use Embedchain to create an Elon Musk bot using the following code:
### CLI
Manage memories from your terminal:
```bash
npm install -g @mem0/cli # or: pip install mem0-cli
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.
### Agent Skills
Teach your AI coding assistant (Claude Code, Codex, Cursor, Windsurf, OpenCode, OpenClaw, and any tool that supports the skills standard) how to build with Mem0. Two categories:
**Reference skills — always on** (SDK knowledge loaded into the assistant's context):
```bash
npx skills add https://github.com/mem0ai/mem0 --skill mem0
npx skills add https://github.com/mem0ai/mem0 --skill mem0-cli
npx skills add https://github.com/mem0ai/mem0 --skill mem0-vercel-ai-sdk
```
**Pipeline skills — run on demand** (execute an end-to-end workflow in an existing repo):
```bash
npx skills add https://github.com/mem0ai/mem0 --skill mem0-integrate
npx skills add https://github.com/mem0ai/mem0 --skill mem0-test-integration
```
Use `/mem0-integrate` to wire Mem0 into an existing repo via a test-first pipeline, then `/mem0-test-integration` to verify. See the [skills catalog](./skills/) or [Vibecoding with Mem0](https://docs.mem0.ai/vibecoding) for the full picture.
### Basic Usage
Mem0 requires an LLM to function, with `gpt-5-mini` 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).
Mem0 uses `text-embedding-3-small` from OpenAI as the default embedding model. For best results with hybrid search (semantic + keyword + entity boosting), we recommend using at least [Qwen 600M](https://huggingface.co/Alibaba-NLP/gte-Qwen2-1.5B-instruct) or a comparable embedding model. See [Supported Embeddings](https://docs.mem0.ai/components/embedders/overview) for configuration details.
First step is to instantiate the memory:
```python
import os
from embedchain import 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")
elon_bot.add("https://www.youtube.com/watch?v=RcYjXbSJBN8")
def chat_with_memories(message: str, user_id: str = "default_user") -> str:
# Retrieve relevant memories
relevant_memories = memory.search(query=message, filters={"user_id": user_id}, top_k=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-5-mini", 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()
```
## 🤝 Contributing
For detailed integration steps, see the [Quickstart](https://docs.mem0.ai/quickstart) and [API Reference](https://docs.mem0.ai/api-reference).
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).
## 🔗 Integrations & Demos
For more reference, please go through [Development Guide](https://docs.embedchain.ai/contribution/dev) and [Documentation Guide](https://docs.embedchain.ai/contribution/docs).
- **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))
<a href="https://github.com/embedchain/embedchain/graphs/contributors">
<img src="https://contrib.rocks/image?repo=embedchain/embedchain" />
</a>
## 📚 Documentation & Support
## 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 `app.config.collect_metrics = False` in the code. 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: Data platform for LLMs - load, index, retrieve, and sync any unstructured data},
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
+551
View File
@@ -0,0 +1,551 @@
{
"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": "#8b5cf6",
"accent": "#a78bfa",
"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 Agent Mode bootstrap (--agent), 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": "agent", "flags": ["--agent"], "type": "boolean", "default": false, "help": "Bootstrap an unattended Agent Mode account (no email required)." },
{ "name": "source", "flags": ["--source"], "type": "string", "default": null, "help": "Channel attribution for signup (e.g. github, hn, ph)." }
]
},
{
"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." }
]
}
]
}
+337
View File
@@ -0,0 +1,337 @@
# mem0 CLI (Node.js)
The official command-line interface for [mem0](https://mem0.ai) — the memory layer for AI agents. TypeScript implementation.
> **Built for AI agents.** Pass `--agent` (or `--json`) as a global flag on any command to get structured JSON output optimized for programmatic consumption — sanitized fields, no colors or spinners, and errors as JSON too.
## Prerequisites
- Node.js **18+**
- pnpm (`npm install -g pnpm`) — for development only
## Installation
```bash
npm install -g @mem0/cli
```
## Quick start
```bash
# Interactive setup wizard
mem0 init
# Or login via email
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
# Get a specific memory
mem0 get <memory-id>
# Update a memory
mem0 update <memory-id> "I switched to light mode"
# Delete a memory
mem0 delete <memory-id>
```
## Commands
### `mem0 init`
Interactive setup wizard. Prompts for your API key and default user ID.
```bash
mem0 init
mem0 init --api-key m0-xxx --user-id alice
mem0 init --email alice@company.com
```
If an existing configuration is detected, the CLI asks for confirmation before overwriting. Use `--force` to skip the prompt (useful in CI/CD).
```bash
mem0 init --api-key m0-xxx --user-id alice --force
```
| Flag | Description |
|------|-------------|
| `--api-key` | API key (skip prompt) |
| `-u, --user-id` | Default user ID (skip prompt) |
| `--email` | Login via email verification code |
| `--code` | Verification code (use with `--email` for non-interactive login) |
| `--force` | Overwrite existing config without confirmation |
### `mem0 add`
Add a memory from text, a JSON messages array, a file, or stdin.
```bash
mem0 add "I prefer dark mode" --user-id alice
mem0 add --file conversation.json --user-id alice
echo "Loves hiking on weekends" | mem0 add --user-id alice
```
| Flag | Description |
|------|-------------|
| `-u, --user-id` | Scope to a user |
| `--agent-id` | Scope to an agent |
| `--messages` | Conversation messages as JSON |
| `-f, --file` | Read messages from a JSON file |
| `-m, --metadata` | Custom metadata as JSON |
| `--categories` | Categories (JSON array or comma-separated) |
| `--graph / --no-graph` | Enable or disable graph memory extraction |
| `-o, --output` | Output format: `text`, `json`, `quiet` |
### `mem0 search`
Search memories using natural language.
```bash
mem0 search "dietary restrictions" --user-id alice
mem0 search "preferred tools" --user-id alice --output json --top-k 5
```
| Flag | Description |
|------|-------------|
| `-u, --user-id` | Filter by user |
| `-k, --top-k` | Number of results (default: 10) |
| `--threshold` | Minimum similarity score (default: 0.3) |
| `--rerank` | Enable reranking |
| `--keyword` | Use keyword search instead of semantic |
| `--filter` | Advanced filter expression (JSON) |
| `--graph / --no-graph` | Enable or disable graph in search |
| `-o, --output` | Output format: `text`, `json`, `table` |
### `mem0 list`
List memories with optional filters and pagination.
```bash
mem0 list --user-id alice
mem0 list --user-id alice --category preferences --output json
mem0 list --user-id alice --after 2024-01-01 --page-size 50
```
| Flag | Description |
|------|-------------|
| `-u, --user-id` | Filter by user |
| `--page` | Page number (default: 1) |
| `--page-size` | Results per page (default: 100) |
| `--category` | Filter by category |
| `--after` | Created after date (YYYY-MM-DD) |
| `--before` | Created before date (YYYY-MM-DD) |
| `-o, --output` | Output format: `text`, `json`, `table` |
### `mem0 get`
Retrieve a specific memory by ID.
```bash
mem0 get 7b3c1a2e-4d5f-6789-abcd-ef0123456789
mem0 get 7b3c1a2e-4d5f-6789-abcd-ef0123456789 --output json
```
### `mem0 update`
Update the text or metadata of an existing memory.
```bash
mem0 update <memory-id> "Updated preference text"
mem0 update <memory-id> --metadata '{"priority": "high"}'
echo "new text" | mem0 update <memory-id>
```
### `mem0 delete`
Delete a single memory, all memories for a scope, or an entire entity.
```bash
# Delete a single memory
mem0 delete <memory-id>
# Delete all memories for a user
mem0 delete --all --user-id alice --force
# Delete all memories project-wide
mem0 delete --all --project --force
# Preview what would be deleted
mem0 delete --all --user-id alice --dry-run
```
| Flag | Description |
|------|-------------|
| `--all` | Delete all memories matching scope filters |
| `--entity` | Delete the entity and all its memories |
| `--project` | With `--all`: delete all memories project-wide |
| `--dry-run` | Preview without deleting |
| `--force` | Skip confirmation prompt |
### `mem0 import`
Bulk import memories from a JSON file.
```bash
mem0 import data.json --user-id alice
```
The file should be a JSON array where each item has a `memory` (or `text` or `content`) field and optional `user_id`, `agent_id`, and `metadata` fields.
### `mem0 config`
View or modify the local CLI configuration.
```bash
mem0 config show # Display current config (secrets redacted)
mem0 config get api_key # Get a specific value
mem0 config set user_id bob # Set a value
```
### `mem0 entity`
List or delete entities (users, agents, apps, runs).
```bash
mem0 entity list users
mem0 entity list agents --output json
mem0 entity delete --user-id alice --force
```
### `mem0 event`
Inspect background processing events created by async operations (e.g. bulk deletes, large add jobs).
```bash
# List recent events
mem0 event list
# Check the status of a specific event
mem0 event status <event-id>
```
| Flag | Description |
|------|-------------|
| `-o, --output` | Output format: `text`, `json` |
### `mem0 status`
Verify your API connection and display the current project.
```bash
mem0 status
```
### `mem0 version`
Print the CLI version.
```bash
mem0 version
```
## 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
mem0 --agent delete --all --user-id alice --force
```
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`) |
## Global flags
These flags are available on all commands:
| Flag | Description |
|------|-------------|
| `--json` | Enable agent mode: structured JSON envelope output, no colors or spinners |
| `--agent` | Alias for `--json` |
| `--api-key` | Override the configured API key for this request |
| `--base-url` | Override the configured API base URL for this request |
| `-o, --output` | Set the output format |
## 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`) |
Environment variables take precedence over values in the config file, which take precedence over defaults.
## Development
```bash
cd cli/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
```
## Documentation
Full documentation is available at [docs.mem0.ai/platform/cli](https://docs.mem0.ai/platform/cli).
## 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.2.5",
"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
+32
View File
@@ -0,0 +1,32 @@
/**
* Detect whether the CLI is being invoked from inside an AI-agent context.
*
* Used by `mem0 init` to auto-enter Agent Mode (Rule 3 bootstrap) when an
* agent runtime env var is present. The return value is a context **trigger
* only** — the canonical agent identity is self-declared by the agent via
* `--agent-caller <name>` (Proof Editor-style) and never sniffed from env
* vars to fill the `agent_caller` field on the APIKey row.
*
* Returns a short name or null. Honest reporting depends on `--agent-caller`;
* this list is just enough to enable the zero-friction auto-bootstrap UX.
*/
const AGENT_CALLER_ENV: ReadonlyArray<readonly [string, readonly string[]]> = [
["claude-code", ["CLAUDECODE", "CLAUDE_CODE"]],
["cursor", ["CURSOR_AGENT", "CURSOR_SESSION_ID"]],
["codex", ["CODEX_CLI", "OPENAI_CODEX"]],
["cline", ["CLINE_AGENT", "CLINE"]],
["continue", ["CONTINUE_AGENT", "CONTINUE_SESSION"]],
["aider", ["AIDER_SESSION"]],
["goose", ["GOOSE_AGENT"]],
["windsurf", ["WINDSURF_AGENT"]],
] as const;
export function detectAgentCaller(): string | null {
for (const [name, envVars] of AGENT_CALLER_ENV) {
if (envVars.some((v) => process.env[v])) {
return name;
}
}
return null;
}
+127
View File
@@ -0,0 +1,127 @@
/**
* 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[];
}
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[];
}
export interface ListOptions {
userId?: string;
agentId?: string;
appId?: string;
runId?: string;
page?: number;
pageSize?: number;
category?: string;
after?: string;
before?: string;
}
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>>;
ping(): 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";
+392
View File
@@ -0,0 +1,392 @@
/**
* Platform (SaaS) backend — communicates with api.mem0.ai.
*/
import type { PlatformConfig } from "../config.js";
import { captureNotice, isAgentMode } from "../state.js";
import { CLI_VERSION } from "../version.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",
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "node",
"X-Mem0-Client-Version": CLI_VERSION,
};
}
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 headers = {
...this.headers,
"X-Mem0-Caller-Type": isAgentMode() ? "agent" : "user",
};
const fetchOpts: RequestInit = {
method,
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 {};
}
const data = await resp.json();
// Pull the unclaimed-Agent-Mode notice out of the body (or the header
// fallback for endpoints returning non-dict / non-dict-leading payloads)
// and stash for end-of-command surfacing.
let notice: string | null = null;
if (
data &&
typeof data === "object" &&
!Array.isArray(data) &&
"mem0_notice" in data
) {
notice = (data as Record<string, unknown>).mem0_notice as string;
// biome-ignore lint/performance/noDelete: intentional strip so downstream consumers don't see duplicate notice
delete (data as Record<string, unknown>).mem0_notice;
} else if (
Array.isArray(data) &&
data.length > 0 &&
typeof data[0] === "object" &&
data[0] !== null &&
"mem0_notice" in data[0]
) {
notice = (data[0] as Record<string, unknown>).mem0_notice as string;
// biome-ignore lint/performance/noDelete: see above.
delete (data[0] as Record<string, unknown>).mem0_notice;
}
if (!notice) {
notice = resp.headers.get("X-Mem0-Notice-Message") ?? null;
}
captureNotice(notice);
return data;
}
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;
payload.source = "CLI";
return (await this._request("POST", "/v3/memories/add/", {
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;
payload.source = "CLI";
const result = (await this._request("POST", "/v3/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}/`, {
params: { source: "CLI" },
})) 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;
payload.source = "CLI";
const result = (await this._request("POST", "/v3/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;
payload.source = "CLI";
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> = { source: "CLI" };
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}/`, {
params: { source: "CLI" },
})) 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}/`,
{ params: { source: "CLI" } },
)) as Record<string, unknown>;
}
return result;
}
async ping(): Promise<Record<string, unknown>> {
return (await this._request("GET", "/v1/ping/")) as Record<string, unknown>;
}
async status(
opts: { userId?: string; agentId?: string } = {},
): Promise<Record<string, unknown>> {
try {
await this.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 = "#8b5cf6";
export const ACCENT_COLOR = "#a78bfa";
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?utm_source=oss&utm_medium=cli-node`
: 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 };
+285
View File
@@ -0,0 +1,285 @@
/**
* Agent Mode commands — bootstrap (unattended signup) and OTP-based claim.
*/
import readline from "node:readline";
import { colors, printError, printInfo, printSuccess } from "../branding.js";
import { type Mem0Config, saveConfig } from "../config.js";
const { brand, dim } = colors;
const SOURCE_HEADERS = {
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "node",
} as const;
export interface BootstrapEnvelope {
api_key: string;
default_user_id: string;
org_id: string;
project_id: string;
mcp_url?: string;
smoke_test_url?: string;
claim_command?: string;
mem0_notice?: string;
}
function isValidEnvelope(v: unknown): v is BootstrapEnvelope {
return (
!!v &&
typeof v === "object" &&
typeof (v as BootstrapEnvelope).api_key === "string" &&
(v as BootstrapEnvelope).api_key.length > 0 &&
typeof (v as BootstrapEnvelope).default_user_id === "string" &&
(v as BootstrapEnvelope).default_user_id.length > 0
);
}
/**
* POST /api/v1/auth/agent_mode/ and mutate config in place.
*
* @param config - Mem0Config mutated in place with the new platform values.
* @param source - `--source` flag passthrough (analytics tag, free-form).
* @param agentCaller - Self-declared agent identity passed via `--agent-caller`
* (e.g. `claude-code`, `cursor`). May be null when the caller omitted the
* flag; the agent can backfill later via `mem0 identify <name>`. Sent to the
* backend in the request body and saved into `platform.agentCaller` for
* local introspection.
*/
export async function bootstrapViaBackend(
config: Mem0Config,
{
source,
agentCaller,
}: { source?: string | null; agentCaller?: string | null } = {},
): Promise<void> {
const baseUrl = (config.platform.baseUrl || "https://api.mem0.ai").replace(
/\/+$/,
"",
);
const body: Record<string, unknown> = {};
if (source) body.source = source;
if (agentCaller) body.agent_caller = agentCaller;
let resp: Response;
try {
resp = await fetch(`${baseUrl}/api/v1/auth/agent_mode/`, {
method: "POST",
headers: {
...SOURCE_HEADERS,
"Content-Type": "application/json",
},
body: JSON.stringify(body),
signal: AbortSignal.timeout(30_000),
});
} catch (err) {
printError(
`Network error contacting Mem0: ${err instanceof Error ? err.message : String(err)}`,
);
process.exit(1);
}
if (resp.status === 429) {
printError("Rate-limited. Try again in a few minutes.");
process.exit(1);
}
if (resp.status === 503) {
printError("Agent Mode is temporarily disabled. Try again later.");
process.exit(1);
}
if (!resp.ok) {
let detail: string = resp.statusText;
try {
const errBody = (await resp.json()) as {
error?: string;
detail?: string;
};
detail = errBody.error ?? errBody.detail ?? resp.statusText;
} catch {
/* leave detail as statusText */
}
// Backend's @ratelimit decorator raises PermissionDenied, which DRF
// translates to a generic 403 "You do not have permission to perform
// this action." That's opaque — surface it as the rate-limit message
// it actually is.
if (resp.status === 403 && /permission/i.test(detail)) {
printError(
"Daily Agent Mode signup limit reached for this network (5/day). Try again from a different IP or after midnight UTC.",
);
process.exit(1);
}
printError(`Bootstrap failed: ${detail}`);
process.exit(1);
}
const envelope = (await resp.json()) as BootstrapEnvelope;
if (!isValidEnvelope(envelope)) {
// Defend against partial/malformed backend responses (e.g. {api_key: null}).
// Without this guard, the typed `string` field is silently set to
// undefined/null and persisted, producing confusing downstream errors.
printError(
"Bootstrap response missing required fields — please update the CLI.",
);
process.exit(1);
}
config.platform.apiKey = envelope.api_key;
config.platform.baseUrl = baseUrl;
config.platform.agentMode = true;
config.platform.createdVia = "agent_mode";
config.platform.agentCaller = agentCaller ?? "";
config.platform.claimedAt = "";
config.platform.defaultUserId = envelope.default_user_id;
// Adopt the slug-derived user_id as the default scope for memory ops.
config.defaults.userId = envelope.default_user_id;
saveConfig(config);
printSuccess(
`Agent Mode active. Default user_id: ${envelope.default_user_id}`,
);
if (envelope.mem0_notice) {
console.log(`\n\x1b[33m🔔 ${envelope.mem0_notice}\x1b[0m\n`);
} else {
// Fallback for older backends without the unified notice field.
const claimCmd = envelope.claim_command ?? "mem0 init --email <your-email>";
console.log(` ${dim(`To claim this account later: ${claimCmd}`)}`);
}
if (!agentCaller) {
console.log(
` ${dim("If you are an AI agent, identify yourself: `mem0 identify <your-name>` (e.g. claude-code, cursor).")}`,
);
}
}
/**
* Claim an existing Agent Mode account via OTP — no browser, no polling.
*
* Hits /api/v1/auth/email_code/ to send a verification code, prompts for it
* interactively (or accepts via `code`), then sends it to /verify/ alongside
* `agent_mode_api_key`. Backend's verify_email_code runs upgrade-in-place
* inline and returns the claim result.
*/
export async function claimViaOtp(
config: Mem0Config,
{ email, code }: { email: string; code?: string },
): Promise<void> {
const baseUrl = (config.platform.baseUrl || "https://api.mem0.ai").replace(
/\/+$/,
"",
);
if (!config.platform.apiKey || !config.platform.agentMode) {
printError(
"This command requires an active Agent Mode config. Run `mem0 init` first.",
);
process.exit(1);
}
const rawKey = config.platform.apiKey;
// Step 1: request OTP (unless --code was supplied)
if (!code) {
const sendResp = await fetch(`${baseUrl}/api/v1/auth/email_code/`, {
method: "POST",
headers: { ...SOURCE_HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({ email }),
signal: AbortSignal.timeout(30_000),
});
if (sendResp.status === 429) {
printError("Too many attempts. Try again in a few minutes.");
process.exit(1);
}
if (!sendResp.ok) {
let detail: string = sendResp.statusText;
try {
const errBody = (await sendResp.json()) as { error?: string };
if (errBody.error) detail = errBody.error;
} catch {
/* leave as statusText */
}
printError(`Failed to send code: ${detail}`);
process.exit(1);
}
printSuccess(`Verification code sent to ${email}. Check your inbox.`);
if (!process.stdin.isTTY) {
printError(
"No --code provided and terminal is non-interactive.",
`Re-run: mem0 init --email ${email} --code <code>`,
);
process.exit(1);
}
console.log();
code = await promptLine(` ${brand("Verification Code")}`);
if (!code) {
printError("Code is required.");
process.exit(1);
}
}
// Step 2: verify + claim atomically
const verifyResp = await fetch(`${baseUrl}/api/v1/auth/email_code/verify/`, {
method: "POST",
headers: { ...SOURCE_HEADERS, "Content-Type": "application/json" },
body: JSON.stringify({
email,
code: code.trim(),
agent_mode_api_key: rawKey,
}),
signal: AbortSignal.timeout(30_000),
});
if (!verifyResp.ok) {
let detail: string = verifyResp.statusText;
let errCode = "";
try {
const errBody = (await verifyResp.json()) as {
error?: string;
code?: string;
};
if (errBody.error) detail = errBody.error;
if (errBody.code) errCode = errBody.code;
} catch {
/* leave as statusText */
}
printError(`Claim failed: ${detail}`);
if (errCode === "email_already_claimed") {
console.log(
` ${dim("Tip: this email already has a Mem0 account. Sign in there and run `mem0 link <key>` to attach this agent.")}`,
);
}
process.exit(1);
}
const claimBody = (await verifyResp.json()) as {
claimed?: boolean;
claimed_at?: string;
};
if (!claimBody.claimed) {
printError(`Unexpected verify response: ${JSON.stringify(claimBody)}`);
process.exit(1);
}
config.platform.agentMode = false;
config.platform.claimedAt = claimBody.claimed_at ?? new Date().toISOString();
config.platform.userEmail = email;
config.platform.createdVia = "email";
saveConfig(config);
printSuccess(`Agent claimed to ${email}. Your API key is unchanged.`);
}
function promptLine(label: string): Promise<string> {
const rl = readline.createInterface({
input: process.stdin,
output: process.stdout,
});
return new Promise((resolve) => {
rl.question(`${label}: `, (answer) => {
rl.close();
resolve(answer.trim());
});
});
}
+109
View File
@@ -0,0 +1,109 @@
/**
* 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,
},
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(["", ""]);
// 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();
}
+75
View File
@@ -0,0 +1,75 @@
/**
* mem0 identify — declare which agent owns the current agent-mode key.
*
* Used when `mem0 init --agent` ran without --agent-caller, so the backend
* saved agent_caller=NULL. The agent re-runs `mem0 identify <name>` to PATCH
* its own row with its real identity. Idempotent.
*/
import { printError, printSuccess } from "../branding.js";
import { loadConfig, saveConfig } from "../config.js";
const SOURCE_HEADERS = {
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "node",
} as const;
export async function runIdentify(name: string): Promise<void> {
const config = loadConfig();
if (!config.platform.apiKey) {
printError("No API key configured. Run `mem0 init --agent` first.");
process.exit(1);
}
if (!config.platform.agentMode) {
printError("This command only works on unclaimed agent-mode keys.");
process.exit(1);
}
const clean = (name ?? "").trim();
if (!clean) {
printError("Agent name is required.");
process.exit(1);
}
const baseUrl = (config.platform.baseUrl || "https://api.mem0.ai").replace(
/\/+$/,
"",
);
let resp: Response;
try {
resp = await fetch(`${baseUrl}/api/v1/auth/agent_mode/caller/`, {
method: "PATCH",
headers: {
...SOURCE_HEADERS,
Authorization: `Token ${config.platform.apiKey}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ agent_caller: clean }),
signal: AbortSignal.timeout(30_000),
});
} catch (err) {
printError(
`Network error: ${err instanceof Error ? err.message : String(err)}`,
);
process.exit(1);
}
if (!resp.ok) {
let detail: string = resp.statusText;
try {
const body = (await resp.json()) as { error?: string };
if (body.error) detail = body.error;
} catch {
/* leave as statusText */
}
printError(`Identify failed: ${detail}`);
process.exit(1);
}
const body = (await resp.json()) as { agent_caller?: string };
const canonical = body.agent_caller ?? clean;
config.platform.agentCaller = canonical;
saveConfig(config);
printSuccess(`Identified as ${canonical}.`);
}
+612
View File
@@ -0,0 +1,612 @@
/**
* 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";
import { formatJsonEnvelope } from "../output.js";
import { isAgentMode } from "../state.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);
}
}
/** @internal — exported for unit tests. */
export async function pingKey(
apiKey: string,
baseUrl: string,
timeoutMs = 5000,
): Promise<boolean> {
// Returns false ONLY on a definitive "invalid key" signal (HTTP 401/403).
// Network errors, timeouts, and 5xx responses return true so we prefer
// reusing an existing key over silently minting a new shadow on a transient
// blip (which would also clobber config + plugin-sync targets).
try {
const resp = await fetch(`${baseUrl.replace(/\/+$/, "")}/v1/ping/`, {
headers: { Authorization: `Token ${apiKey}` },
signal: AbortSignal.timeout(timeoutMs),
});
return resp.status !== 401 && resp.status !== 403;
} catch {
return true; // unknown — prefer reuse
}
}
async function maybeIdentify(
key: string,
baseUrl: string,
agentCaller: string | undefined,
): Promise<void> {
// Best-effort PATCH agent_caller when --agent-caller is supplied on a
// reused key. Silent no-op on any failure — reuse must not break.
if (!agentCaller) return;
try {
const resp = await fetch(
`${baseUrl.replace(/\/+$/, "")}/api/v1/auth/agent_mode/caller/`,
{
method: "PATCH",
headers: {
Authorization: `Token ${key}`,
"Content-Type": "application/json",
},
body: JSON.stringify({ agent_caller: agentCaller }),
signal: AbortSignal.timeout(10_000),
},
);
if (resp.ok) {
try {
const body = (await resp.json()) as { agent_caller?: string };
if (fs.existsSync(CONFIG_FILE)) {
const cfg = loadConfig();
cfg.platform.agentCaller = body.agent_caller ?? agentCaller;
saveConfig(cfg);
}
} catch {
/* swallow — best effort */
}
}
} catch {
/* swallow — best effort */
}
}
async function emailLogin(
email: string,
code: string | undefined,
baseUrl: string,
): Promise<Record<string, unknown>> {
const url = baseUrl.replace(/\/+$/, "");
let codeValue = code;
const sourceHeaders = {
"Content-Type": "application/json",
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "node",
};
if (!codeValue) {
const resp = await fetch(`${url}/api/v1/auth/email_code/`, {
method: "POST",
headers: sourceHeaders,
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: sourceHeaders,
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?utm_source=oss&utm_medium=cli-node")}`,
);
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;
config.platform.createdVia = "api_key";
}
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!");
// Cache user_email from ping response for telemetry distinct_id
try {
const pingData = (await backend.ping()) as Record<string, unknown>;
const userEmail = pingData?.user_email as string | undefined;
if (userEmail) {
config.platform.userEmail = userEmail;
}
} catch {
/* ignore — telemetry ID will fall back to API key hash */
}
} else {
printError(
`Could not connect: ${status.error ?? "Unknown error"}`,
"Visit https://app.mem0.ai/dashboard/api-keys?utm_source=oss&utm_medium=cli-node 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;
agent?: boolean;
source?: string;
agentCaller?: string;
} = {},
): Promise<void> {
const { detectAgentCaller } = await import("../agent-detect.js");
const { bootstrapViaBackend, claimViaOtp } = await import("./agent-mode.js");
const { isAgentMode } = await import("../state.js");
const { captureEvent } = await import("../telemetry.js");
const fireInit = (
mode: "agent" | "email" | "api_key" | "existing_key",
claimed = false,
) => {
const props: Record<string, unknown> = { command: "init", mode };
// Self-declared via --agent-caller; not sniffed from env vars.
if (opts.agentCaller) props.agent_caller = opts.agentCaller;
if (opts.source) props.signup_source = opts.source;
if (claimed) props.claimed_agent_mode = true;
captureEvent("cli.init", props);
};
const config = createDefaultConfig();
const savedConfig = loadConfig();
const baseUrl =
process.env.MEM0_BASE_URL ||
savedConfig.platform.baseUrl ||
DEFAULT_BASE_URL;
config.platform.baseUrl = baseUrl;
// 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);
}
// ── Claim flow: --email against an existing agent-mode config ───────────
if (
opts.email &&
fs.existsSync(CONFIG_FILE) &&
savedConfig.platform.agentMode &&
savedConfig.platform.apiKey
) {
const email = opts.email.trim().toLowerCase();
validateEmail(email);
printInfo(`Claiming Agent Mode account to ${email}...`);
await claimViaOtp(savedConfig, { email, code: opts.code });
fireInit("email", true);
return;
}
// ── Agent Mode path runs BEFORE the existing-config guard ──────────────
// Rule 1/2 will REUSE a valid existing key (not overwrite), so we must
// short-circuit before the guard prompts the user about overwriting.
// Rule 3 only mints when there's no valid key to reuse — in that case
// overwriting is what the user wants.
const agentCtx =
opts.agent === true || isAgentMode() || detectAgentCaller() !== null;
if (!opts.apiKey && !opts.email && agentCtx) {
const emitReuseEnvelope = (source: "env" | "config") => {
if (isAgentMode()) {
formatJsonEnvelope({
command: "init",
data: {
api_key_saved: false,
api_key_source: source,
agent_mode: false,
message:
"Existing Mem0 API key found and reused. No Agent Mode key was created.",
},
});
} else {
printSuccess(
source === "env"
? "Existing MEM0_API_KEY is valid; reusing it. No new Agent Mode key was minted."
: "Existing API key in config is valid; reusing it. No new Agent Mode key was minted.",
);
}
};
// Rule 1: env MEM0_API_KEY valid → reuse, no new key.
const envKey = (process.env.MEM0_API_KEY || "").trim();
if (envKey && (await pingKey(envKey, baseUrl))) {
await maybeIdentify(envKey, baseUrl, opts.agentCaller);
emitReuseEnvelope("env");
fireInit("existing_key");
return;
}
// Rule 2: existing config api_key valid → reuse.
if (
savedConfig.platform.apiKey &&
(await pingKey(savedConfig.platform.apiKey, baseUrl))
) {
await maybeIdentify(
savedConfig.platform.apiKey,
baseUrl,
opts.agentCaller,
);
emitReuseEnvelope("config");
fireInit("existing_key");
return;
}
// Rule 3: mint a fresh shadow (no valid key to reuse).
// agent_caller is self-declared via --agent-caller (Proof Editor-style),
// not derived from env-var sniffing. detectAgentCaller() above is still
// used as a context trigger (does this look like an agent?) but never
// to fill identity.
await bootstrapViaBackend(config, {
source: opts.source ?? null,
agentCaller: opts.agentCaller ?? null,
});
fireInit("agent");
return;
}
// 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.platform.userEmail = email;
config.platform.createdVia = "email";
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 ──────────────────────────────────────────────────────────
// (Agent Mode branch runs earlier — see above, before the existing-config
// guard, so Rules 1/2 can REUSE a valid key without prompting overwrite.)
// 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>, --email <addr>, or --agent for unattended Agent Mode bootstrap.",
);
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.platform.createdVia = "api_key";
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.platform.userEmail = email;
config.platform.createdVia = "email";
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();
}
+703
View File
@@ -0,0 +1,703 @@
/**
* 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;
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,
});
});
} 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;
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,
});
});
} 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;
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,
});
});
} 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?utm_source=oss&utm_medium=cli-node")}`,
);
}
}
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.`);
}
}
+217
View File
@@ -0,0 +1,217 @@
/**
* 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;
userEmail: string;
// Agent Mode (unclaimed-shadow signup)
agentMode: boolean; // true while the key is an unclaimed agent-mode key
createdVia: string; // "agent_mode" | "email" | "api_key" | "existing_key"
agentCaller: string; // canonical agent name when createdVia === "agent_mode" (e.g. "claude-code")
claimedAt: string; // ISO timestamp once the agent has been claimed
defaultUserId: string; // `user_<slug>` returned by bootstrap; auto-default scope
}
export interface DefaultsConfig {
userId: string;
agentId: string;
appId: string;
runId: string;
}
export interface TelemetryConfig {
anonymousId: string;
}
export interface Mem0Config {
version: number;
defaults: DefaultsConfig;
platform: PlatformConfig;
telemetry: TelemetryConfig;
}
export function createDefaultConfig(): Mem0Config {
return {
version: CONFIG_VERSION,
defaults: {
userId: "",
agentId: "",
appId: "",
runId: "",
},
platform: {
apiKey: "",
baseUrl: DEFAULT_BASE_URL,
userEmail: "",
agentMode: false,
createdVia: "",
agentCaller: "",
claimedAt: "",
defaultUserId: "",
},
telemetry: {
anonymousId: "",
},
};
}
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;
config.platform.userEmail = plat.user_email ?? "";
config.platform.agentMode = Boolean(plat.agent_mode ?? false);
config.platform.createdVia = plat.created_via ?? "";
config.platform.agentCaller = plat.agent_caller ?? "";
config.platform.claimedAt = plat.claimed_at ?? "";
config.platform.defaultUserId = plat.default_user_id ?? "";
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 ?? "";
const telemetry = data.telemetry ?? {};
config.telemetry.anonymousId = telemetry.anonymous_id ?? "";
}
// 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;
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,
},
platform: {
api_key: config.platform.apiKey,
base_url: config.platform.baseUrl,
user_email: config.platform.userEmail,
agent_mode: config.platform.agentMode,
created_via: config.platform.createdVia,
agent_caller: config.platform.agentCaller,
claimed_at: config.platform.claimedAt,
default_user_id: config.platform.defaultUserId,
},
telemetry: {
anonymous_id: config.telemetry.anonymousId,
},
};
fs.writeFileSync(CONFIG_FILE, JSON.stringify(data, null, 2));
fs.chmodSync(CONFIG_FILE, 0o600);
// Propagate api_key to ecosystem touchpoints (Claude plugin env injection,
// shell rc exports). Idempotent — updates only EXISTING entries; never
// creates new ones. Best-effort: errors swallowed so config.json is
// always authoritative, never blocked by plugin-state issues.
if (config.platform.apiKey) {
try {
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { syncApiKey } = require("./plugin-sync.js");
syncApiKey(config.platform.apiKey);
} catch {
/* swallow */
}
}
}
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"],
"platform.user_email": ["platform", "userEmail"],
"defaults.user_id": ["defaults", "userId"],
"defaults.agent_id": ["defaults", "agentId"],
"defaults.app_id": ["defaults", "appId"],
"defaults.run_id": ["defaults", "runId"],
// Short-form aliases
api_key: ["platform", "apiKey"],
base_url: ["platform", "baseUrl"],
user_email: ["platform", "userEmail"],
user_id: ["defaults", "userId"],
agent_id: ["defaults", "agentId"],
app_id: ["defaults", "appId"],
run_id: ["defaults", "runId"],
};
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;
});
}
+838
View File
@@ -0,0 +1,838 @@
#!/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 { AuthError, type Backend, getBackend } from "./backend/index.js";
import { colors, printError, printWarning } from "./branding.js";
import type { Mem0Config } from "./config.js";
import { loadConfig, saveConfig } from "./config.js";
import { richFormatHelp } from "./help.js";
import {
isAgentMode,
setAgentMode,
setCurrentCommand,
takeNotice,
} from "./state.js";
import { captureEvent } from "./telemetry.js";
import { CLI_VERSION } from "./version.js";
const program = new Command();
// ── Validated user identity (set by getBackendAndConfig) ─────────────────
let _validatedUserEmail: string | undefined;
// ── Helpers ──────────────────────────────────────────────────────────────
async function getBackendAndConfig(
apiKey?: string,
baseUrl?: string,
): Promise<{ 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);
}
const backend = getBackend(config);
// Validate the API key upfront with a fast timeout
try {
const pingData = (await Promise.race([
backend.ping(),
new Promise<never>((_, reject) =>
setTimeout(() => reject(new Error("timeout")), 5000),
),
])) as Record<string, unknown>;
const email = pingData?.user_email as string | undefined;
if (email) {
_validatedUserEmail = email;
if (config.platform.userEmail !== email) {
config.platform.userEmail = email;
try {
saveConfig(config);
} catch {
/* ignore */
}
}
}
} catch (e) {
if (e instanceof AuthError) {
printError(
"Invalid or expired API key.",
"Run 'mem0 init' or set MEM0_API_KEY environment variable.",
);
process.exit(1);
}
// Network error / timeout — warn but proceed
printWarning(
"Could not validate API key (network issue). Proceeding anyway.",
);
}
return { backend, config };
}
async function getBackendOnly(
apiKey?: string,
baseUrl?: string,
): Promise<Backend> {
return (await 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,
};
}
// ── Main program ──────────────────────────────────────────────────────────
program
.name("mem0")
.description(
`◆ Mem0 CLI v${CLI_VERSION} · Node.js SDK\n\nThe Memory Layer for AI Agents`,
)
// Positional options: flags AFTER a subcommand name belong to that
// subcommand, not the global program. Without this, `mem0 init --agent`
// routes `--agent` to the program-level alias (for --json) and init's own
// `--agent` (Agent Mode bootstrap) silently never fires.
.enablePositionalOptions()
.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) Place BEFORE the subcommand: `mem0 --agent <cmd>`. On `init`, `mem0 init --agent` is the Agent Mode bootstrap flag instead.",
)
.usage("<command> [options]")
.helpOption("--help", "Show this message and exit.")
.addHelpCommand(false)
.configureHelp({ formatHelp: richFormatHelp });
// ── Telemetry hook ───────────────────────────────────────────────────────
program.hook("preAction", (_thisCommand, actionCommand) => {
try {
const commandName = actionCommand.name();
const parentName = actionCommand.parent?.name();
const fullCommand =
parentName && parentName !== "mem0"
? `${parentName}.${commandName}`
: commandName;
// Stash the active command name in shared state so the JSON
// error envelope (printError) can report which command failed
// instead of an empty `"command": ""` field.
setCurrentCommand(fullCommand);
// init fires its own telemetry from runInit with full M1-M6 props
// (mode/agent_caller/signup_source/claimed_agent_mode); skip the
// auto-fire here so we don't double-count.
if (fullCommand === "init") return;
const isAgent = !!(program.opts().json || program.opts().agent);
captureEvent(
`cli.${fullCommand}`,
{
command: fullCommand,
is_agent: isAgent,
},
_validatedUserEmail,
);
} catch {
/* silently swallow */
}
});
// ── 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)
.option(
"--agent",
"Bootstrap an unattended Agent Mode account (no email required).",
false,
)
.option(
"--source <channel>",
"Channel attribution for signup (e.g. github, hn, ph).",
)
.option(
"--agent-caller <name>",
"Self-declared agent identity (e.g. claude-code, cursor). Used with --agent to attribute Agent Mode signups.",
)
// Accept `--json` at the init level too so the PRD-documented form
// `mem0 init --agent --json` works without requiring users to move it
// before the subcommand. Effect is identical to the global `--json`:
// flip agent-mode output state.
.option("--json", "Output as JSON (alias for global `--json`).", 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\n $ mem0 init --agent # Bootstrap an Agent Mode account (unattended)\n $ mem0 init --email you@example.com # Claims an existing Agent Mode key when one is present",
)
.action(async (opts) => {
// `--json` at init level mirrors the global flag — flip agent_mode
// state so downstream formatters use JSON envelopes.
if (opts.json) setAgentMode(true);
const { runInit } = await import("./commands/init.js");
await runInit({
apiKey: opts.apiKey,
userId: opts.userId,
email: opts.email,
code: opts.code,
force: opts.force,
agent: opts.agent,
source: opts.source,
agentCaller: opts.agentCaller,
});
});
// ── Setup: identify (post-bootstrap agent self-tag) ──────────────────────
program
.command("identify <name>")
.description(
"Tag your active Agent Mode key with the AI agent that's using it (e.g. claude-code, cursor).",
)
.action(async (name: string) => {
const { runIdentify } = await import("./commands/identify.js");
await runIdentify(name);
});
// ── 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("-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 } = await getBackendAndConfig(
opts.apiKey,
opts.baseUrl,
);
const ids = resolveIds(config, opts);
const output = isAgent ? "agent" : opts.output;
await cmdAdd(backend, text, { ...ids, ...opts, 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("-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 } = await getBackendAndConfig(
opts.apiKey,
opts.baseUrl,
);
const ids = resolveIds(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,
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 = await 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("-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 } = await getBackendAndConfig(
opts.apiKey,
opts.baseUrl,
);
const ids = resolveIds(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,
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 = await 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 = await 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 } = await 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 = await 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 = await 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 = await 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 = await 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 = await 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 } = await 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 } = await 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 ────────────────────────────────────────────────────────────
// Surface any unclaimed Agent Mode notice once per command, after the primary
// output. In JSON/agent mode the notice is folded into the envelope by
// formatJsonEnvelope, so skip the stderr banner there to avoid duplication.
function surfaceNotice(): void {
const notice = takeNotice();
if (notice && !isAgentMode()) {
process.stderr.write(`\n\x1b[33m🔔 ${notice}\x1b[0m\n\n`);
}
}
program.parseAsync().finally(() => {
surfaceNotice();
});
+397
View File
@@ -0,0 +1,397 @@
/**
* 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";
import { takeNotice } from "./state.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;
// If the platform flagged this as an unclaimed Agent Mode account, surface
// the notice inside the JSON envelope so an agent consuming the output
// sees it without needing to inspect HTTP headers.
// eslint-disable-next-line @typescript-eslint/no-require-imports
const { takeNotice } = require("./state.js");
const notice = takeNotice();
if (notice) envelope.mem0_notice = notice;
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);
// Surface the unclaimed-Agent-Mode notice (if any) in the envelope so an
// agent reading the JSON output sees it without inspecting HTTP headers.
const notice = takeNotice();
if (notice) envelope.mem0_notice = notice;
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();
}
+120
View File
@@ -0,0 +1,120 @@
/**
* Sync the active Mem0 API key into other ecosystem touchpoints.
*
* Why: the CLI canonical state is ~/.mem0/config.json. MCP servers
* (Claude Code plugin, Codex plugin) read MEM0_API_KEY from env or
* their own config files. Without a sync, agent-mode bootstrap mints a
* new key into config.json but the plugin's MCP keeps using the old
* key from env — silent surprise.
*
* Design:
* - Update ONLY entries that already exist; never create new ones
* - Preserve surrounding content, formatting, other keys
* - Atomic writes (tmp + rename) so a crash mid-write doesn't corrupt
* - Idempotent — re-running with the same key is a no-op
*
* Targets:
* - ~/.claude/settings.json::env::MEM0_API_KEY (Claude Code env injection)
* - ~/.zshrc / ~/.bashrc `export MEM0_API_KEY="..."` lines
*
* Out of scope: Codex / Cursor MCP configs and the plugin's own
* <plugin-dir>/.api_key file (plugin-managed, different schema).
*/
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
const CLAUDE_SETTINGS = path.join(os.homedir(), ".claude", "settings.json");
const SHELL_RCS = [
path.join(os.homedir(), ".zshrc"),
path.join(os.homedir(), ".bashrc"),
path.join(os.homedir(), ".bash_profile"),
];
// Use [ \t]* (not \s*) so a trailing newline at end-of-file is preserved
// when the MEM0_API_KEY export is the last line of the rc file.
const RC_LINE_RE =
/^([ \t]*export[ \t]+MEM0_API_KEY[ \t]*=[ \t]*)(["']?)([^"'\n]*)(["']?)[ \t]*$/m;
export function syncApiKey(apiKey: string): string[] {
if (!apiKey) return [];
const updated: string[] = [];
if (updateClaudeSettings(CLAUDE_SETTINGS, apiKey)) {
updated.push(CLAUDE_SETTINGS);
}
for (const rc of SHELL_RCS) {
if (updateShellRc(rc, apiKey)) updated.push(rc);
}
return updated;
}
/** @internal — exported for unit tests; consumers should use {@link syncApiKey}. */
export function updateClaudeSettings(
filePath: string,
apiKey: string,
): boolean {
if (!fs.existsSync(filePath)) return false;
let raw: string;
let data: Record<string, unknown>;
try {
raw = fs.readFileSync(filePath, "utf-8");
data = JSON.parse(raw);
} catch {
return false;
}
const env = data.env;
if (!env || typeof env !== "object" || !("MEM0_API_KEY" in env)) {
return false; // no existing entry — don't create one
}
const envObj = env as Record<string, string>;
if (envObj.MEM0_API_KEY === apiKey) return false; // already in sync
envObj.MEM0_API_KEY = apiKey;
atomicWriteText(filePath, `${JSON.stringify(data, null, 2)}\n`);
return true;
}
/** @internal — exported for unit tests; consumers should use {@link syncApiKey}. */
export function updateShellRc(filePath: string, apiKey: string): boolean {
if (!fs.existsSync(filePath)) return false;
let text: string;
try {
text = fs.readFileSync(filePath, "utf-8");
} catch {
return false;
}
const match = text.match(RC_LINE_RE);
if (!match) return false; // no existing line
if (match[3] === apiKey) return false;
const newText = text.replace(
RC_LINE_RE,
(_full, prefix) => `${prefix}"${apiKey}"`,
);
atomicWriteText(filePath, newText);
return true;
}
function atomicWriteText(filePath: string, content: string): void {
const dir = path.dirname(filePath);
const tmp = path.join(dir, `.${path.basename(filePath)}.${process.pid}.tmp`);
try {
fs.writeFileSync(tmp, content, "utf-8");
// Preserve permissions if original existed.
if (fs.existsSync(filePath)) {
try {
const mode = fs.statSync(filePath).mode & 0o777;
fs.chmodSync(tmp, mode);
} catch {
/* best-effort */
}
}
fs.renameSync(tmp, filePath);
} catch (err) {
try {
fs.unlinkSync(tmp);
} catch {
/* ignore */
}
throw err;
}
}
+40
View File
@@ -0,0 +1,40 @@
/**
* Agent mode state — set by the root program option handler,
* read by commands and branding functions.
*/
let _agentMode = false;
let _currentCommand = "";
let _pendingNotice = "";
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;
}
/**
* Stash a Mem0 backend notice (Agent Mode unclaimed reminder) for end-of-
* command surfacing. Called from the platform backend after each response so
* the notice prints once per command regardless of how many sub-requests
* fired. Last-write-wins is fine — the message text is identical.
*/
export function captureNotice(notice: string | null | undefined): void {
if (notice) _pendingNotice = notice;
}
export function takeNotice(): string {
const msg = _pendingNotice;
_pendingNotice = "";
return msg;
}
+157
View File
@@ -0,0 +1,157 @@
/**
* CLI telemetry — anonymous usage tracking via PostHog.
*
* Sends fire-and-forget events by spawning a detached child process
* (telemetry-sender.cjs). The parent CLI process exits immediately;
* the child handles email resolution, caching, and the HTTP POST.
*
* Disable with: MEM0_TELEMETRY=false
*/
import { spawn } from "node:child_process";
import { createHash, randomUUID } from "node:crypto";
import path from "node:path";
import { fileURLToPath } from "node:url";
import { CONFIG_FILE, loadConfig, saveConfig } from "./config.js";
import { CLI_VERSION } from "./version.js";
const POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX";
const POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/";
const __dirname = path.dirname(fileURLToPath(import.meta.url));
const SENDER_SCRIPT = path.join(__dirname, "..", "telemetry-sender.cjs");
function isTelemetryEnabled(): boolean {
try {
return process.env.MEM0_TELEMETRY !== "false";
} catch {
return true;
}
}
/**
* Return a persistent per-machine anonymous ID, generating one if needed.
*
* Stored in ~/.mem0/config.json under `telemetry.anonymous_id` so that
* repeat runs on the same machine share one PostHog identity instead of
* collapsing into a single shared fallback string.
*/
function getOrCreateAnonymousId(): string {
const config = loadConfig();
if (config.telemetry.anonymousId) {
return config.telemetry.anonymousId;
}
const newId = `cli-anon-${randomUUID().replace(/-/g, "")}`;
config.telemetry.anonymousId = newId;
try {
saveConfig(config);
} catch {
/* ignore persistence failure — still return the generated ID */
}
return newId;
}
/**
* Return a stable anonymous identifier for the current user.
*
* Priority: cached user_email (from /v1/ping/) > MD5(api_key) >
* persistent per-machine anonymous ID.
*/
function getDistinctId(): string {
try {
const config = loadConfig();
if (config.platform.userEmail) {
return config.platform.userEmail;
}
if (config.platform.apiKey) {
return createHash("md5").update(config.platform.apiKey).digest("hex");
}
} catch {
/* ignore */
}
try {
return getOrCreateAnonymousId();
} catch {
return `cli-anon-${randomUUID().replace(/-/g, "")}`;
}
}
/**
* Fire a PostHog event (non-blocking, returns void, never throws).
* Spawns telemetry-sender.cjs as a detached subprocess.
*
* When `preResolvedEmail` is provided (e.g. from an upfront ping
* validation), it is used directly as the PostHog distinct ID and the
* subprocess skips its own `/v1/ping/` call.
*/
export function captureEvent(
eventName: string,
properties: Record<string, unknown> = {},
preResolvedEmail?: string,
): void {
if (!isTelemetryEnabled()) return;
try {
const config = loadConfig();
const distinctId = preResolvedEmail || getDistinctId();
// Detect anonymous → identified transition. If a stored anonymous_id
// exists and we just resolved to a real identity, fire a one-shot
// $identify event so PostHog stitches the pre-signup history onto
// the authenticated profile. Clear the stored id so we don't re-alias.
let anonIdToAlias: string | null = null;
if (
distinctId &&
!distinctId.startsWith("cli-anon-") &&
config.telemetry.anonymousId
) {
anonIdToAlias = config.telemetry.anonymousId;
config.telemetry.anonymousId = "";
try {
saveConfig(config);
} catch {
/* ignore — alias may double-fire next run, harmless */
}
}
// M4: every cli.* event carries agent_mode based on the config flag
// (unclaimed Agent Mode key). This is the growth-doc property used to
// join init → add → search funnels in PostHog.
const payload = {
api_key: POSTHOG_API_KEY,
distinct_id: distinctId,
event: eventName,
properties: {
source: "CLI",
language: "node",
cli_version: CLI_VERSION,
agent_mode: Boolean(config.platform.agentMode),
node_version: process.version,
os: process.platform,
...properties,
$process_person_profile: false,
$lib: "posthog-node",
},
};
const context = {
payload,
posthogHost: POSTHOG_HOST,
needsEmail: !distinctId || !distinctId.includes("@"),
mem0ApiKey: config.platform.apiKey || "",
mem0BaseUrl: config.platform.baseUrl || "https://api.mem0.ai",
configPath: CONFIG_FILE,
anonDistinctIdToAlias: anonIdToAlias,
};
const child = spawn(
process.execPath,
[SENDER_SCRIPT, JSON.stringify(context)],
{ detached: true, stdio: "ignore" },
);
child.unref();
} catch {
/* silently swallow */
}
}
+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;
+129
View File
@@ -0,0 +1,129 @@
/**
* Standalone telemetry sender — runs as a detached child process.
*
* Usage: node telemetry-sender.cjs '<json context>'
*
* This script is spawned by telemetry.captureEvent() and runs independently
* of the parent CLI process. It:
*
* 1. Resolves the user's email via /v1/ping/ if not already cached
* 2. Caches the email in ~/.mem0/config.json for future runs
* 3. Sends the PostHog event
*
* All errors are silently swallowed — this process must never produce output
* or affect the user experience.
*/
"use strict";
const https = require("https");
const fs = require("fs");
function httpsRequest(url, method, headers, body) {
return new Promise((resolve, reject) => {
const u = new URL(url);
const opts = {
hostname: u.hostname,
path: u.pathname + u.search,
method,
headers,
timeout: 10000,
};
const req = https.request(opts, (res) => {
let data = "";
res.on("data", (chunk) => (data += chunk));
res.on("end", () => {
try {
resolve(JSON.parse(data));
} catch {
resolve({});
}
});
});
req.on("error", reject);
req.on("timeout", () => {
req.destroy();
reject(new Error("timeout"));
});
if (body) {
req.end(body);
} else {
req.end();
}
});
}
async function resolveAndCacheEmail(ctx, payload) {
try {
const pingUrl = ctx.mem0BaseUrl.replace(/\/+$/, "") + "/v1/ping/";
const data = await httpsRequest(pingUrl, "GET", {
Authorization: "Token " + ctx.mem0ApiKey,
"Content-Type": "application/json",
});
if (data.user_email) {
payload.distinct_id = data.user_email;
cacheEmail(ctx.configPath, data.user_email);
}
} catch {
// silently swallow
}
}
function cacheEmail(configPath, email) {
if (!configPath) return;
try {
const raw = fs.readFileSync(configPath, "utf-8");
const cfg = JSON.parse(raw);
if (!cfg.platform) cfg.platform = {};
cfg.platform.user_email = email;
fs.writeFileSync(configPath, JSON.stringify(cfg, null, 2));
} catch {
// silently swallow
}
}
async function sendPosthogEvent(posthogHost, payload) {
try {
const body = JSON.stringify(payload);
await httpsRequest(posthogHost, "POST", {
"Content-Type": "application/json",
"Content-Length": Buffer.byteLength(body),
}, body);
} catch {
// silently swallow
}
}
async function sendIdentifyEvent(ctx, payload, anonId) {
const identifyPayload = {
api_key: payload.api_key,
event: "$identify",
distinct_id: payload.distinct_id,
properties: {
$anon_distinct_id: anonId,
$lib: (payload.properties && payload.properties.$lib) || "posthog-node",
},
};
await sendPosthogEvent(ctx.posthogHost, identifyPayload);
}
async function main() {
const ctx = JSON.parse(process.argv[2]);
const payload = ctx.payload;
if (ctx.needsEmail && ctx.mem0ApiKey) {
await resolveAndCacheEmail(ctx, payload);
}
// Fire $identify *after* email resolution so PostHog links the stored
// anonymous id directly to the final identity (email, not the api-key
// hash). The regular event is sent next so it lands under the merged
// profile.
if (ctx.anonDistinctIdToAlias) {
await sendIdentifyEvent(ctx, payload, ctx.anonDistinctIdToAlias);
}
await sendPosthogEvent(ctx.posthogHost, payload);
}
main().catch(() => {});
+141
View File
@@ -0,0 +1,141 @@
/**
* Parity tests for `mem0 init --agent` (Agent Mode bootstrap).
*
* Mirror of `cli/python/tests/test_agent_mode.py` — both files MUST stay
* in sync so that the Python and Node CLIs expose an identical surface
* for the Agent Mode entrypoint. If you add a flag here, add the same
* assertion on the Python side (and vice versa).
*
* Network-bound bootstrap is covered by the platform-side E2E suite
* (`backend/tests/e2e/test_05_agent_mode.py`); these tests only verify
* the CLI surface that ships in the binary.
*/
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 };
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,
};
}
}
function cleanHome(): string {
return fs.mkdtempSync(path.join(os.tmpdir(), "mem0-test-"));
}
describe("init flag surface", () => {
it("init --help lists --agent", () => {
const result = run(["init", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--agent");
});
it("init --help describes Agent Mode", () => {
const result = run(["init", "--help"]);
expect(result.exitCode).toBe(0);
// Description must mention what --agent actually does so an agent
// reading the help can self-discover the bootstrap entrypoint.
expect(
result.stdout.includes("Agent Mode") ||
result.stdout.toLowerCase().includes("unattended"),
).toBe(true);
});
it("init --help lists --source", () => {
const result = run(["init", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--source");
});
it("init --help lists --email and --code", () => {
const result = run(["init", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--email");
expect(result.stdout).toContain("--code");
});
});
describe("argv preprocessing — --agent reaches init subcommand", () => {
// Regression for the bug where the global --agent JSON-alias swallowed
// the init-level --agent flag, making `mem0 init --agent` behave like
// the plain interactive wizard.
it("init --agent triggers bootstrap branch (not the wizard)", () => {
const home = cleanHome();
const result = run(["init", "--agent"], {
home,
env: {
MEM0_BASE_URL: "http://127.0.0.1:1", // blackhole
FORCE_COLOR: "0",
},
});
const combined = (result.stdout + result.stderr).toLowerCase();
// Either bootstrap-attempt error, or a connection/network error —
// both prove the --agent path executed (the wizard would prompt for
// input and succeed/hang, not surface a network error).
expect(
combined.includes("agent") ||
combined.includes("connect") ||
combined.includes("network") ||
combined.includes("fetch") ||
combined.includes("bootstrap"),
).toBe(true);
fs.rmSync(home, { recursive: true, force: true });
});
});
describe("JSON envelope on network failure", () => {
it("init --agent --json does not leak a stack trace when backend is unreachable", () => {
const home = cleanHome();
const result = run(["init", "--agent", "--json"], {
home,
env: {
MEM0_BASE_URL: "http://127.0.0.1:1",
FORCE_COLOR: "0",
},
});
const combined = result.stdout + result.stderr;
// No raw Node stack should escape the agent-mode handler.
expect(combined).not.toMatch(/at \w+\s*\(.+\.ts:\d+/);
expect(combined).not.toContain("UnhandledPromiseRejection");
expect(result.exitCode).not.toBe(0);
fs.rmSync(home, { recursive: true, force: true });
});
});
describe("top-level help lists init", () => {
// `mem0 --help` must list `init` so agents walking the top-level help
// can discover the Agent Mode entrypoint without prior knowledge.
it("--help lists init", () => {
const result = run(["--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("init");
});
});
+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("#8b5cf6");
});
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 --output flag", () => {
const result = run(["add", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--output");
});
it("search help has --rerank flag", () => {
const result = run(["search", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--rerank");
});
it("list help has --category flag", () => {
const result = run(["list", "--help"]);
expect(result.exitCode).toBe(0);
expect(result.stdout).toContain("--category");
});
});
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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,
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");
});
});
+107
View File
@@ -0,0 +1,107 @@
/**
* 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("");
});
});
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");
});
});
+168
View File
@@ -0,0 +1,168 @@
/**
* Unit tests for init internals — decision tree primitives + plugin sync.
*
* Mirror of `cli/python/tests/test_init_internals.py`. Both files MUST stay
* in sync — if you add a behavioral assertion here, mirror it on the Python
* side and vice versa.
*
* - `pingKey` must NOT treat network errors as "invalid key" (else a VPN
* flap silently mints a new shadow over a working key).
* - `plugin_sync` must only update entries that already exist, preserve
* trailing newlines, and never mangle other lines.
*/
import fs from "node:fs";
import os from "node:os";
import path from "node:path";
import { afterEach, beforeEach, describe, expect, it, vi } from "vitest";
import { pingKey } from "../src/commands/init.js";
import { updateClaudeSettings, updateShellRc } from "../src/plugin-sync.js";
// ── pingKey ──────────────────────────────────────────────────────────────
describe("pingKey — network vs auth distinction", () => {
const origFetch = globalThis.fetch;
afterEach(() => {
globalThis.fetch = origFetch;
vi.restoreAllMocks();
});
it("returns true for 200", async () => {
globalThis.fetch = vi.fn().mockResolvedValue({ status: 200 } as Response);
await expect(pingKey("k", "http://x")).resolves.toBe(true);
});
it("returns false for 401 (definitively invalid)", async () => {
globalThis.fetch = vi.fn().mockResolvedValue({ status: 401 } as Response);
await expect(pingKey("k", "http://x")).resolves.toBe(false);
});
it("returns false for 403 (definitively invalid)", async () => {
globalThis.fetch = vi.fn().mockResolvedValue({ status: 403 } as Response);
await expect(pingKey("k", "http://x")).resolves.toBe(false);
});
it("returns true for 5xx (transient upstream — prefer reuse)", async () => {
globalThis.fetch = vi.fn().mockResolvedValue({ status: 503 } as Response);
await expect(pingKey("k", "http://x")).resolves.toBe(true);
});
it("returns true on network error (prefer reuse over re-mint)", async () => {
globalThis.fetch = vi.fn().mockRejectedValue(new Error("ECONNREFUSED"));
await expect(pingKey("k", "http://x")).resolves.toBe(true);
});
it("returns true on timeout (prefer reuse)", async () => {
globalThis.fetch = vi.fn().mockRejectedValue(new Error("aborted"));
await expect(pingKey("k", "http://x")).resolves.toBe(true);
});
});
// ── updateShellRc ────────────────────────────────────────────────────────
describe("updateShellRc — exists-only contract", () => {
let tmpDir: string;
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mem0-test-"));
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it("updates existing export and preserves trailing newline", () => {
const rc = path.join(tmpDir, ".zshrc");
fs.writeFileSync(rc, 'export MEM0_API_KEY="old"\n');
expect(updateShellRc(rc, "newkey")).toBe(true);
expect(fs.readFileSync(rc, "utf-8")).toBe('export MEM0_API_KEY="newkey"\n');
});
it("does NOT create a new export when none exists", () => {
const rc = path.join(tmpDir, ".zshrc");
fs.writeFileSync(rc, "alias ll='ls -la'\n");
expect(updateShellRc(rc, "newkey")).toBe(false);
expect(fs.readFileSync(rc, "utf-8")).toBe("alias ll='ls -la'\n");
});
it("preserves surrounding content", () => {
const rc = path.join(tmpDir, ".zshrc");
const original =
"# my zshrc\n" +
"alias ll='ls -la'\n" +
"export MEM0_API_KEY='old'\n" +
"export OTHER=keepme\n";
fs.writeFileSync(rc, original);
updateShellRc(rc, "newkey");
const after = fs.readFileSync(rc, "utf-8");
expect(after).toContain("alias ll='ls -la'\n");
expect(after).toContain("export OTHER=keepme\n");
expect(after).toContain("# my zshrc\n");
expect(after).toContain('export MEM0_API_KEY="newkey"\n');
});
it("is idempotent when value already matches", () => {
const rc = path.join(tmpDir, ".zshrc");
fs.writeFileSync(rc, 'export MEM0_API_KEY="same"\n');
expect(updateShellRc(rc, "same")).toBe(false);
});
it("is a no-op for missing files", () => {
const rc = path.join(tmpDir, ".zshrc"); // does not exist
expect(updateShellRc(rc, "x")).toBe(false);
});
});
// ── updateClaudeSettings ─────────────────────────────────────────────────
describe("updateClaudeSettings — never creates entries", () => {
let tmpDir: string;
beforeEach(() => {
tmpDir = fs.mkdtempSync(path.join(os.tmpdir(), "mem0-test-"));
});
afterEach(() => {
fs.rmSync(tmpDir, { recursive: true, force: true });
});
it("does not create env block when none exists", () => {
const settings = path.join(tmpDir, "settings.json");
fs.writeFileSync(settings, JSON.stringify({ otherKey: 1 }));
expect(updateClaudeSettings(settings, "newkey")).toBe(false);
expect(JSON.parse(fs.readFileSync(settings, "utf-8"))).toEqual({
otherKey: 1,
});
});
it("does not create MEM0_API_KEY entry in existing env block", () => {
const settings = path.join(tmpDir, "settings.json");
fs.writeFileSync(settings, JSON.stringify({ env: { OTHER_KEY: "x" } }));
expect(updateClaudeSettings(settings, "newkey")).toBe(false);
});
it("updates existing entry and preserves siblings", () => {
const settings = path.join(tmpDir, "settings.json");
fs.writeFileSync(
settings,
JSON.stringify({ env: { MEM0_API_KEY: "old", OTHER: "y" } }, null, 2),
);
expect(updateClaudeSettings(settings, "fresh")).toBe(true);
const data = JSON.parse(fs.readFileSync(settings, "utf-8"));
expect(data.env.MEM0_API_KEY).toBe("fresh");
expect(data.env.OTHER).toBe("y");
});
it("is idempotent when value already matches", () => {
const settings = path.join(tmpDir, "settings.json");
fs.writeFileSync(
settings,
JSON.stringify({ env: { MEM0_API_KEY: "same" } }),
);
expect(updateClaudeSettings(settings, "same")).toBe(false);
});
it("is a no-op for malformed JSON", () => {
const settings = path.join(tmpDir, "settings.json");
fs.writeFileSync(settings, "{ this is not json");
expect(updateClaudeSettings(settings, "x")).toBe(false);
});
});
+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)
+349
View File
@@ -0,0 +1,349 @@
# mem0 CLI (Python)
The official command-line interface for [mem0](https://mem0.ai) — the memory layer for AI agents. Python implementation.
> **Built for AI agents.** Pass `--agent` (or `--json`) as a global flag on any command to get structured JSON output optimized for programmatic consumption — sanitized fields, no colors or spinners, and errors as JSON too.
## Prerequisites
- Python **3.10+**
## 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
# Interactive setup wizard
mem0 init
# Or login via email
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
# Get a specific memory
mem0 get <memory-id>
# Update a memory
mem0 update <memory-id> "I switched to light mode"
# Delete a memory
mem0 delete <memory-id>
```
## Commands
### `mem0 init`
Interactive setup wizard. Prompts for your API key and default user ID.
```bash
mem0 init
mem0 init --api-key m0-xxx --user-id alice
mem0 init --email alice@company.com
```
If an existing configuration is detected, the CLI asks for confirmation before overwriting. Use `--force` to skip the prompt (useful in CI/CD).
```bash
mem0 init --api-key m0-xxx --user-id alice --force
```
| Flag | Description |
|------|-------------|
| `--api-key` | API key (skip prompt) |
| `-u, --user-id` | Default user ID (skip prompt) |
| `--email` | Login via email verification code |
| `--code` | Verification code (use with `--email` for non-interactive login) |
| `--force` | Overwrite existing config without confirmation |
### `mem0 add`
Add a memory from text, a JSON messages array, a file, or stdin.
```bash
mem0 add "I prefer dark mode" --user-id alice
mem0 add --file conversation.json --user-id alice
echo "Loves hiking on weekends" | mem0 add --user-id alice
```
| Flag | Description |
|------|-------------|
| `-u, --user-id` | Scope to a user |
| `--agent-id` | Scope to an agent |
| `--messages` | Conversation messages as JSON |
| `-f, --file` | Read messages from a JSON file |
| `-m, --metadata` | Custom metadata as JSON |
| `--categories` | Categories (JSON array or comma-separated) |
| `--graph / --no-graph` | Enable or disable graph memory extraction |
| `-o, --output` | Output format: `text`, `json`, `quiet` |
### `mem0 search`
Search memories using natural language.
```bash
mem0 search "dietary restrictions" --user-id alice
mem0 search "preferred tools" --user-id alice --output json --top-k 5
```
| Flag | Description |
|------|-------------|
| `-u, --user-id` | Filter by user |
| `-k, --top-k` | Number of results (default: 10) |
| `--threshold` | Minimum similarity score (default: 0.3) |
| `--rerank` | Enable reranking |
| `--keyword` | Use keyword search instead of semantic |
| `--filter` | Advanced filter expression (JSON) |
| `--graph / --no-graph` | Enable or disable graph in search |
| `-o, --output` | Output format: `text`, `json`, `table` |
### `mem0 list`
List memories with optional filters and pagination.
```bash
mem0 list --user-id alice
mem0 list --user-id alice --category preferences --output json
mem0 list --user-id alice --after 2024-01-01 --page-size 50
```
| Flag | Description |
|------|-------------|
| `-u, --user-id` | Filter by user |
| `--page` | Page number (default: 1) |
| `--page-size` | Results per page (default: 100) |
| `--category` | Filter by category |
| `--after` | Created after date (YYYY-MM-DD) |
| `--before` | Created before date (YYYY-MM-DD) |
| `-o, --output` | Output format: `text`, `json`, `table` |
### `mem0 get`
Retrieve a specific memory by ID.
```bash
mem0 get 7b3c1a2e-4d5f-6789-abcd-ef0123456789
mem0 get 7b3c1a2e-4d5f-6789-abcd-ef0123456789 --output json
```
### `mem0 update`
Update the text or metadata of an existing memory.
```bash
mem0 update <memory-id> "Updated preference text"
mem0 update <memory-id> --metadata '{"priority": "high"}'
echo "new text" | mem0 update <memory-id>
```
### `mem0 delete`
Delete a single memory, all memories for a scope, or an entire entity.
```bash
# Delete a single memory
mem0 delete <memory-id>
# Delete all memories for a user
mem0 delete --all --user-id alice --force
# Delete all memories project-wide
mem0 delete --all --project --force
# Preview what would be deleted
mem0 delete --all --user-id alice --dry-run
```
| Flag | Description |
|------|-------------|
| `--all` | Delete all memories matching scope filters |
| `--entity` | Delete the entity and all its memories |
| `--project` | With `--all`: delete all memories project-wide |
| `--dry-run` | Preview without deleting |
| `--force` | Skip confirmation prompt |
### `mem0 import`
Bulk import memories from a JSON file.
```bash
mem0 import data.json --user-id alice
```
The file should be a JSON array where each item has a `memory` (or `text` or `content`) field and optional `user_id`, `agent_id`, and `metadata` fields.
### `mem0 config`
View or modify the local CLI configuration.
```bash
mem0 config show # Display current config (secrets redacted)
mem0 config get api_key # Get a specific value
mem0 config set user_id bob # Set a value
```
### `mem0 entity`
List or delete entities (users, agents, apps, runs).
```bash
mem0 entity list users
mem0 entity list agents --output json
mem0 entity delete --user-id alice --force
```
### `mem0 event`
Inspect background processing events created by async operations (e.g. bulk deletes, large add jobs).
```bash
# List recent events
mem0 event list
# Check the status of a specific event
mem0 event status <event-id>
```
| Flag | Description |
|------|-------------|
| `-o, --output` | Output format: `text`, `json` |
### `mem0 status`
Verify your API connection and display the current project.
```bash
mem0 status
```
### `mem0 version`
Print the CLI version.
```bash
mem0 version
```
## 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
mem0 --agent delete --all --user-id alice --force
```
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`) |
## Global flags
These flags are available on all commands:
| Flag | Description |
|------|-------------|
| `--json` | Enable agent mode: structured JSON envelope output, no colors or spinners |
| `--agent` | Alias for `--json` |
| `--api-key` | Override the configured API key for this request |
| `--base-url` | Override the configured API base URL for this request |
| `-o, --output` | Set the output format |
## 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`) |
Environment variables take precedence over values in the config file, which take precedence over defaults.
## Development
```bash
cd cli/python
python -m venv .venv && source .venv/bin/activate
pip install -e ".[dev]"
# Run during development
python -m mem0_cli --help
mem0 add "test memory" --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.1`)
For a pre-release, use a beta version like `0.2.1b1` and check the **pre-release** checkbox.
## Documentation
Full documentation is available at [docs.mem0.ai/platform/cli](https://docs.mem0.ai/platform/cli).
## 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.5"
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.2.4"
+5
View File
@@ -0,0 +1,5 @@
"""Allow running with `python -m mem0_cli`."""
from mem0_cli.app import main
main()
+36
View File
@@ -0,0 +1,36 @@
"""Detect whether the CLI is being invoked from inside an AI-agent context.
Used by `mem0 init` to auto-enter Agent Mode (Rule 3 bootstrap) when an
agent runtime env var is present. The return value is a context **trigger
only** — the canonical agent identity is self-declared by the agent via
``--agent-caller <name>`` (Proof Editor-style) and never sniffed from env
vars to fill the ``agent_caller`` field on the APIKey row.
Returns a short name or None. The list is curated, not exhaustive — env
vars we don't recognise fall through to None (caller treated as
non-agent). Honest reporting depends on ``--agent-caller``; this list is
just enough to enable the zero-friction auto-bootstrap UX.
"""
from __future__ import annotations
import os
_AGENT_CALLER_ENV: tuple[tuple[str, tuple[str, ...]], ...] = (
("claude-code", ("CLAUDECODE", "CLAUDE_CODE")),
("cursor", ("CURSOR_AGENT", "CURSOR_SESSION_ID")),
("codex", ("CODEX_CLI", "OPENAI_CODEX")),
("cline", ("CLINE_AGENT", "CLINE")),
("continue", ("CONTINUE_AGENT", "CONTINUE_SESSION")),
("aider", ("AIDER_SESSION",)),
("goose", ("GOOSE_AGENT",)),
("windsurf", ("WINDSURF_AGENT",)),
)
def detect_agent_caller() -> str | None:
"""Return a canonical agent name if any agent env var is set, else None."""
for name, env_vars in _AGENT_CALLER_ENV:
if any(os.environ.get(v) for v in env_vars):
return name
return None
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"]
+115
View File
@@ -0,0 +1,115 @@
"""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,
) -> 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,
) -> 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,
) -> 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)
+361
View File
@@ -0,0 +1,361 @@
"""Platform (SaaS) backend — communicates with api.mem0.ai."""
from __future__ import annotations
from typing import Any
import httpx
from mem0_cli import __version__
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",
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "python",
"X-Mem0-Client-Version": __version__,
},
timeout=30.0,
)
def _request(self, method: str, path: str, **kwargs: Any) -> Any:
from mem0_cli.state import capture_notice, is_agent_mode
self._client.headers["X-Mem0-Caller-Type"] = "agent" if is_agent_mode() else "user"
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 {}
data = resp.json()
# Pull the unclaimed-Agent-Mode notice out of the body (or the header
# fallback for endpoints that return non-dict / non-dict-leading
# payloads) and stash it for end-of-command surfacing.
notice = None
if isinstance(data, dict) and "mem0_notice" in data:
notice = data.pop("mem0_notice")
elif (
isinstance(data, list)
and data
and isinstance(data[0], dict)
and "mem0_notice" in data[0]
):
notice = data[0].pop("mem0_notice")
if notice is None:
notice = resp.headers.get("X-Mem0-Notice-Message") or None
capture_notice(notice)
return data
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,
) -> 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
payload["source"] = "CLI"
return self._request("POST", "/v3/memories/add/", 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 v3 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,
) -> 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
payload["source"] = "CLI"
result = self._request("POST", "/v3/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}/", params={"source": "CLI"})
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,
) -> list[dict]:
payload: dict[str, Any] = {}
params = {"page": str(page), "page_size": str(page_size)}
# Build filters — 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
payload["source"] = "CLI"
result = self._request("POST", "/v3/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
payload["source"] = "CLI"
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] = {"source": "CLI"}
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}/", params={"source": "CLI"})
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}/", params={"source": "CLI"}
)
return result
def ping(self, timeout: float | None = None) -> dict:
"""Call the ping endpoint and return the raw response.
When *timeout* is given it overrides the client-level timeout so that
validation pings can fail fast without blocking the user.
"""
if timeout is not None:
resp = self._client.get("/v1/ping/", timeout=timeout)
if resp.status_code == 401:
raise AuthError("Authentication failed. Your API key may be invalid or expired.")
resp.raise_for_status()
return resp.json()
return self._request("GET", "/v1/ping/")
def status(
self,
*,
user_id: str | None = None,
agent_id: str | None = None,
) -> dict[str, Any]:
"""Check connectivity using the ping endpoint."""
try:
self.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
+178
View File
@@ -0,0 +1,178 @@
"""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 = "#8b5cf6" # Purple
ACCENT_COLOR = "#a78bfa"
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
from rich.markup import escape
sym = _sym("✗", "[error]")
console.print(f"[{ERROR_COLOR}]{sym} Error:[/] {escape(str(message))}")
if hint:
console.print(f" [{DIM_COLOR}]{escape(str(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?utm_source=oss&utm_medium=cli-python[/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,239 @@
"""Agent Mode commands — bootstrap (unattended signup) and claim (OTP-based human upgrade)."""
from __future__ import annotations
import json
import sys
from datetime import datetime, timezone
from typing import Any
import httpx
import typer
from rich.console import Console
from rich.prompt import Prompt
from mem0_cli.branding import (
BRAND_COLOR,
DIM_COLOR,
print_error,
print_success,
)
from mem0_cli.config import Mem0Config, save_config
console = Console()
err_console = Console(stderr=True)
_SOURCE_HEADERS = {
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "python",
}
def _validate_envelope(envelope: Any) -> None:
"""Defend against partial/malformed backend responses.
A backend regression that returns ``{"api_key": null}`` would otherwise be
silently persisted, producing confusing downstream errors far from the
source. Fail fast with a clear message if the required fields are missing.
"""
if not isinstance(envelope, dict):
print_error(err_console, "Bootstrap response was not a JSON object.")
raise typer.Exit(1)
for field in ("api_key", "default_user_id"):
value = envelope.get(field)
if not isinstance(value, str) or not value:
print_error(
err_console,
f"Bootstrap response missing required field {field!r} — please update the CLI.",
)
raise typer.Exit(1)
def bootstrap_via_backend(
config: Mem0Config,
*,
source: str | None = None,
agent_caller: str | None = None,
) -> None:
"""POST /api/v1/auth/agent_mode/ and mutate config in place.
Args:
config: Mem0Config mutated in place with the new platform values.
source: ``--source`` flag passthrough (analytics tag, free-form).
agent_caller: Self-declared agent identity passed via ``--agent-caller``
(e.g. ``claude-code``, ``cursor``). May be None when the caller
omitted the flag; the agent can backfill later via
``mem0 identify <name>``. Sent to the backend in the request body
and saved into ``platform.agent_caller`` for local introspection.
Raises typer.Exit(1) on failure.
"""
base_url = (config.platform.base_url or "https://api.mem0.ai").rstrip("/")
body: dict[str, Any] = {}
if source:
body["source"] = source
if agent_caller:
body["agent_caller"] = agent_caller
try:
with httpx.Client(timeout=30.0) as client:
resp = client.post(
f"{base_url}/api/v1/auth/agent_mode/",
headers={**_SOURCE_HEADERS, "Content-Type": "application/json"},
json=body,
)
except httpx.HTTPError as exc:
print_error(err_console, f"Network error contacting Mem0: {exc}")
raise typer.Exit(1) from exc
if resp.status_code == 429:
print_error(err_console, "Rate-limited. Try again in a few minutes.")
raise typer.Exit(1)
if resp.status_code == 503:
print_error(err_console, "Agent Mode is temporarily disabled. Try again later.")
raise typer.Exit(1)
if resp.status_code != 200:
detail = resp.text
try:
err_body = resp.json()
detail = err_body.get("error") or err_body.get("detail") or resp.text
except (json.JSONDecodeError, ValueError, AttributeError):
pass
# Backend's @ratelimit decorator raises PermissionDenied, which DRF
# translates to a generic 403 "You do not have permission to perform
# this action." That's opaque — surface as the rate-limit it actually is.
if resp.status_code == 403 and "permission" in str(detail).lower():
print_error(
err_console,
"Daily Agent Mode signup limit reached for this network (5/day). Try again from a different IP or after midnight UTC.",
)
raise typer.Exit(1)
print_error(err_console, f"Bootstrap failed: {detail}")
raise typer.Exit(1)
envelope = resp.json()
_validate_envelope(envelope)
config.platform.api_key = envelope["api_key"]
config.platform.base_url = base_url
config.platform.agent_mode = True
config.platform.created_via = "agent_mode"
config.platform.agent_caller = agent_caller or ""
config.platform.claimed_at = ""
config.platform.default_user_id = envelope["default_user_id"]
# Adopt the slug-derived user_id as the default scope for memory ops.
config.defaults.user_id = envelope["default_user_id"]
save_config(config)
print_success(console, f"Agent Mode active. Default user_id: {envelope['default_user_id']}")
notice = envelope.get("mem0_notice")
if notice:
console.print(f"\n[yellow]🔔 {notice}[/yellow]\n")
else:
# Fallback if the backend hasn't deployed the unified notice yet.
claim_cmd = envelope.get("claim_command", "mem0 init --email <your-email>")
console.print(f" [{DIM_COLOR}]To claim this account later: {claim_cmd}[/]")
if not agent_caller:
console.print(
f" [{DIM_COLOR}]If you are an AI agent, identify yourself: "
f"`mem0 identify <your-name>` (e.g. claude-code, cursor).[/]"
)
def claim_via_otp(config: Mem0Config, *, email: str, code: str | None = None) -> None:
"""Claim an existing Agent Mode account via OTP — no browser, no polling.
Reuses the standard email-code flow (`/api/v1/auth/email_code/` then
`/.../verify/`) and adds the local agent-mode API key in the verify body
as `agent_mode_api_key`. Backend's `verify_email_code` runs the
upgrade-in-place transaction inline and returns claim result.
On success: flips `platform.agent_mode=false`, sets `claimed_at`, stamps
`user_email`. The api_key value itself never changes.
"""
base_url = (config.platform.base_url or "https://api.mem0.ai").rstrip("/")
if not config.platform.api_key or not config.platform.agent_mode:
print_error(
err_console,
"This command requires an active Agent Mode config. Run `mem0 init` first.",
)
raise typer.Exit(1)
raw_key = config.platform.api_key
with httpx.Client(timeout=30.0) as client:
# Step 1: request OTP (unless --code provided)
if not code:
send = client.post(
f"{base_url}/api/v1/auth/email_code/",
headers={**_SOURCE_HEADERS, "Content-Type": "application/json"},
json={"email": email},
)
if send.status_code == 429:
print_error(err_console, "Too many attempts. Try again in a few minutes.")
raise typer.Exit(1)
if send.status_code != 200:
try:
detail = send.json().get("error", send.text)
except Exception:
detail = send.text
print_error(err_console, f"Failed to send code: {detail}")
raise typer.Exit(1)
print_success(console, f"Verification code sent to {email}. Check your inbox.")
if not sys.stdin.isatty():
print_error(
err_console,
"No --code provided and terminal is non-interactive.",
hint=f"Re-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 2: verify + claim in one shot
verify = client.post(
f"{base_url}/api/v1/auth/email_code/verify/",
headers={**_SOURCE_HEADERS, "Content-Type": "application/json"},
json={
"email": email,
"code": code.strip(),
"agent_mode_api_key": raw_key,
},
)
if verify.status_code != 200:
try:
err_body = verify.json()
detail = err_body.get("error", verify.text)
code_str = err_body.get("code", "")
except (json.JSONDecodeError, ValueError, AttributeError):
detail = verify.text
code_str = ""
print_error(err_console, f"Claim failed: {detail}")
if code_str == "email_already_claimed":
console.print(
f" [{DIM_COLOR}]Tip: this email already has a Mem0 account. Sign in there and run `mem0 link <key>` to attach this agent.[/]"
)
raise typer.Exit(1)
claim_body = verify.json()
if not claim_body.get("claimed"):
print_error(err_console, f"Unexpected verify response: {claim_body}")
raise typer.Exit(1)
config.platform.agent_mode = False
config.platform.claimed_at = claim_body.get("claimed_at") or _utcnow_iso()
config.platform.user_email = email
config.platform.created_via = "email"
save_config(config)
print_success(console, f"Agent claimed to {email}. Your API key is unchanged.")
def _utcnow_iso() -> str:
return datetime.now(timezone.utc).isoformat()
@@ -0,0 +1,127 @@
"""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,
},
"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("", "")
# 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,75 @@
"""mem0 identify — declare which agent owns the current agent-mode key.
Used when `mem0 init --agent` ran without --agent-caller, so the backend
saved agent_caller=NULL. The agent re-runs `mem0 identify <name>` to PATCH
its own row with its real identity. Idempotent — running it again just
overwrites.
"""
from __future__ import annotations
import httpx
import typer
from rich.console import Console
from mem0_cli.branding import print_error, print_success
from mem0_cli.config import load_config, save_config
console = Console()
err_console = Console(stderr=True)
_SOURCE_HEADERS = {
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "python",
}
def run_identify(name: str) -> None:
"""PATCH the active agent-mode key's agent_caller field."""
config = load_config()
if not config.platform.api_key:
print_error(
err_console,
"No API key configured. Run `mem0 init --agent` first.",
)
raise typer.Exit(1)
if not config.platform.agent_mode:
print_error(
err_console,
"This command only works on unclaimed agent-mode keys.",
)
raise typer.Exit(1)
name = (name or "").strip()
if not name:
print_error(err_console, "Agent name is required.")
raise typer.Exit(1)
base_url = (config.platform.base_url or "https://api.mem0.ai").rstrip("/")
try:
with httpx.Client(timeout=30.0) as client:
resp = client.patch(
f"{base_url}/api/v1/auth/agent_mode/caller/",
headers={
**_SOURCE_HEADERS,
"Authorization": f"Token {config.platform.api_key}",
"Content-Type": "application/json",
},
json={"agent_caller": name},
)
except httpx.HTTPError as exc:
print_error(err_console, f"Network error: {exc}")
raise typer.Exit(1) from exc
if resp.status_code != 200:
try:
detail = resp.json().get("error", resp.text)
except Exception:
detail = resp.text
print_error(err_console, f"Identify failed: {detail}")
raise typer.Exit(1)
canonical = resp.json().get("agent_caller", name)
config.platform.agent_caller = canonical
save_config(config)
print_success(console, f"Identified as {canonical}.")
@@ -0,0 +1,566 @@
"""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 _ping_key(api_key: str, base_url: str, timeout: float = 5.0) -> bool:
"""Validate api_key against /v1/ping/.
Returns False ONLY on a definitive "invalid key" signal (HTTP 401 / 403).
Network errors, timeouts, and 5xx responses return True so we prefer
reusing an existing key over silently minting a new shadow on a transient
blip (which would also clobber config + plugin-sync targets).
"""
try:
resp = httpx.get(
f"{base_url.rstrip('/')}/v1/ping/",
headers={"Authorization": f"Token {api_key}"},
timeout=timeout,
)
except httpx.HTTPError:
return True # unknown — prefer reuse
return resp.status_code not in (401, 403)
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("/")
_source_headers = {
"X-Mem0-Source": "cli",
"X-Mem0-Client-Language": "python",
}
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},
headers=_source_headers,
)
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()},
headers=_source_headers,
)
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,
source: str | None = None,
agent: bool = False,
agent_caller: str | None = None,
) -> 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.
Agent Mode dispatch (no email/api-key flags):
- If existing config has an active API key → reuse (existing_key path).
- Else if any positive agent signal (--agent, --json global, agent env
var, or `agent` flag) → POST /api/v1/auth/agent_mode/ and write config.
- Else fall through to the interactive wizard.
Claim dispatch:
- If `--email` is set AND existing config has `agent_mode=true`, run the
claim device-flow against the existing key instead of minting a new
email-based key.
"""
from mem0_cli.agent_detect import detect_agent_caller
from mem0_cli.commands.agent_mode_cmd import bootstrap_via_backend, claim_via_otp
from mem0_cli.state import is_agent_mode as _global_agent_mode
from mem0_cli.telemetry import capture_event
def _fire_init(mode: str, *, claimed: bool = False) -> None:
"""Fire cli.init telemetry with M1-M6 properties."""
props: dict = {"command": "init", "mode": mode}
if agent_caller:
# Self-declared via --agent-caller; not sniffed from env vars.
props["agent_caller"] = agent_caller
if source:
props["signup_source"] = source
if claimed:
props["claimed_agent_mode"] = True
capture_event("cli.init", props)
config = Mem0Config()
base_url = os.environ.get("MEM0_BASE_URL", config.platform.base_url or DEFAULT_BASE_URL)
config.platform.base_url = base_url
if code and not email:
print_error(err_console, "--code requires --email.")
raise typer.Exit(1)
# ── Email + existing agent-mode config → claim flow ─────────────────
if email and CONFIG_FILE.exists():
existing = load_config()
if existing.platform.agent_mode and existing.platform.api_key:
email = email.strip().lower()
_validate_email(email)
print_info(console, f"Claiming Agent Mode account to {email}...")
claim_via_otp(existing, email=email, code=code)
_fire_init("email", claimed=True)
return
# ── Agent Mode path runs BEFORE the existing-config guard ──────────
# Rules 1/2 REUSE a valid existing key (not overwrite), so we must
# short-circuit before the guard prompts. Rule 3 mints only when there
# is no valid key to reuse — in that case overwriting is correct.
_agent_ctx = agent or _global_agent_mode() or (detect_agent_caller() is not None)
if not api_key and not email and _agent_ctx:
from mem0_cli.output import format_json_envelope
from mem0_cli.state import is_agent_mode as _is_json_mode
def _emit_reuse(source: str) -> None:
if _is_json_mode():
format_json_envelope(
console,
command="init",
data={
"api_key_saved": False,
"api_key_source": source,
"agent_mode": False,
"message": "Existing Mem0 API key found and reused. No Agent Mode key was created.",
},
)
else:
msg = (
"Existing MEM0_API_KEY is valid; reusing it. No new Agent Mode key was minted."
if source == "env"
else "Existing API key in config is valid; reusing it. No new Agent Mode key was minted."
)
print_success(console, msg)
def _maybe_identify(key: str) -> None:
"""Best-effort PATCH agent_caller when --agent-caller is supplied on a
reused key. Silent no-op on any failure — reuse must not break.
"""
if not agent_caller:
return
try:
resp = httpx.patch(
f"{base_url.rstrip('/')}/api/v1/auth/agent_mode/caller/",
headers={
"Authorization": f"Token {key}",
"Content-Type": "application/json",
},
json={"agent_caller": agent_caller},
timeout=10.0,
)
# Also reflect in local config so introspection matches backend.
if resp.status_code == 200 and CONFIG_FILE.exists():
try:
cfg = load_config()
cfg.platform.agent_caller = resp.json().get("agent_caller", agent_caller)
save_config(cfg)
except Exception:
pass
except httpx.HTTPError:
pass
# Rule 1: env MEM0_API_KEY valid → reuse, no new key.
_env_key = (os.environ.get("MEM0_API_KEY") or "").strip()
if _env_key and _ping_key(_env_key, base_url):
_maybe_identify(_env_key)
_emit_reuse("env")
_fire_init("existing_key")
return
# Rule 2: existing config api_key valid → reuse.
if CONFIG_FILE.exists():
_existing = load_config()
if _existing.platform.api_key and _ping_key(_existing.platform.api_key, base_url):
_maybe_identify(_existing.platform.api_key)
_emit_reuse("config")
_fire_init("existing_key")
return
# Rule 3: mint a fresh shadow (no valid key to reuse).
# agent_caller is the agent's self-declared identity from --agent-caller
# (Proof Editor-style). Env-var auto-detect is still used above to
# decide we're in an agent context, but never to fill identity.
bootstrap_via_backend(config, source=source, agent_caller=agent_caller)
_fire_init("agent")
return
# 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.platform.user_email = email
config.platform.created_via = "email"
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) ───────────────────────────────────────
# (Agent Mode branch runs earlier — see above, before the existing-config
# guard, so Rules 1/2 can REUSE a valid key without prompting overwrite.)
# 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>, --email <addr>, or --agent for unattended Agent Mode bootstrap.",
)
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.platform.created_via = "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.platform.user_email = email_addr
config.platform.created_via = "email"
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
config.platform.created_via = "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?utm_source=oss&utm_medium=cli-python[/]"
)
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
config.platform.created_via = "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!")
# Cache user_email from ping response for telemetry distinct_id
try:
ping_data = backend.ping()
user_email = ping_data.get("user_email") if isinstance(ping_data, dict) else None
if user_email:
config.platform.user_email = user_email
except Exception:
pass
else:
print_error(
err_console,
f"Could not connect: {status.get('error', 'Unknown error')}",
hint="Visit https://app.mem0.ai/dashboard/api-keys?utm_source=oss&utm_medium=cli-python to get a new key, then run mem0 init again.",
)
except Exception as e:
print_error(err_console, f"Connection test failed: {e}")
+671
View File
@@ -0,0 +1,671 @@
"""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,
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,
)
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,
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,
)
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,
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,
)
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?utm_source=oss&utm_medium=cli-python[/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.")
+227
View File
@@ -0,0 +1,227 @@
"""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
user_email: str = ""
# Agent Mode (unclaimed-shadow signup)
agent_mode: bool = False # True while the key is an unclaimed agent-mode key
created_via: str = "" # "agent_mode" | "email" | "api_key" | "existing_key"
agent_caller: str = (
"" # canonical agent name when created_via == "agent_mode" (e.g. "claude-code")
)
claimed_at: str = "" # ISO timestamp once the agent has been claimed by a human
default_user_id: str = "" # `user_<slug>` returned by bootstrap; used as auto-default
@dataclass
class DefaultsConfig:
user_id: str = ""
agent_id: str = ""
app_id: str = ""
run_id: str = ""
@dataclass
class TelemetryConfig:
anonymous_id: str = ""
@dataclass
class Mem0Config:
version: int = CONFIG_VERSION
defaults: DefaultsConfig = field(default_factory=DefaultsConfig)
platform: PlatformConfig = field(default_factory=PlatformConfig)
telemetry: TelemetryConfig = field(default_factory=TelemetryConfig)
SHORT_KEY_ALIASES: dict[str, str] = {
"api_key": "platform.api_key",
"base_url": "platform.base_url",
"user_email": "platform.user_email",
"user_id": "defaults.user_id",
"agent_id": "defaults.agent_id",
"app_id": "defaults.app_id",
"run_id": "defaults.run_id",
}
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)
config.platform.user_email = plat.get("user_email", "")
config.platform.agent_mode = bool(plat.get("agent_mode", False))
config.platform.created_via = plat.get("created_via", "")
config.platform.agent_caller = plat.get("agent_caller", "")
config.platform.claimed_at = plat.get("claimed_at", "")
config.platform.default_user_id = plat.get("default_user_id", "")
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", "")
telemetry = data.get("telemetry", {})
config.telemetry.anonymous_id = telemetry.get("anonymous_id", "")
# 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
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,
},
"platform": {
"api_key": config.platform.api_key,
"base_url": config.platform.base_url,
"user_email": config.platform.user_email,
"agent_mode": config.platform.agent_mode,
"created_via": config.platform.created_via,
"agent_caller": config.platform.agent_caller,
"claimed_at": config.platform.claimed_at,
"default_user_id": config.platform.default_user_id,
},
"telemetry": {
"anonymous_id": config.telemetry.anonymous_id,
},
}
with open(CONFIG_FILE, "w") as f:
json.dump(data, f, indent=2)
os.chmod(CONFIG_FILE, stat.S_IRUSR | stat.S_IWUSR) # 0600
# Propagate the active api_key to ecosystem touchpoints (Claude Code
# plugin env injection, shell rc exports). Idempotent — only updates
# EXISTING entries; never creates new ones. Best-effort: any IOError
# in the sync is swallowed so config.json is always the authoritative
# write, never blocked by plugin-state issues.
if config.platform.api_key:
try:
from mem0_cli.plugin_sync import sync_api_key
sync_api_key(config.platform.api_key)
except Exception:
pass
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
+378
View File
@@ -0,0 +1,378 @@
"""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
# If the platform flagged this as an unclaimed Agent Mode account, surface
# the notice inside the JSON envelope so an agent consuming the output
# sees it without needing to inspect HTTP headers.
from mem0_cli.state import take_notice
notice = take_notice()
if notice:
envelope["mem0_notice"] = notice
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)
# Surface the unclaimed-Agent-Mode notice (if any) in the envelope so an
# agent reading the JSON output sees it without inspecting HTTP headers.
from mem0_cli.state import take_notice
notice = take_notice()
if notice:
envelope["mem0_notice"] = notice
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
+119
View File
@@ -0,0 +1,119 @@
"""Sync the active Mem0 API key into other ecosystem touchpoints.
Why this exists:
The CLI canonical state lives in ``~/.mem0/config.json``. But MCP servers
(Claude Code plugin, Codex plugin, etc.) read ``MEM0_API_KEY`` from env
vars or their own config files. Without a sync, an agent-mode bootstrap
mints a new key into config.json but the plugin's MCP keeps using the
old key from env — silent surprise.
Design:
- Update ONLY entries that already exist (never create new ones)
- Preserve all surrounding content / formatting / other keys
- Atomic writes (tmpfile + rename) so a crash mid-write doesn't corrupt
- Idempotent — re-running with the same key is a no-op
- Skip on dry_run
Targets currently handled:
- ``~/.claude/settings.json::env::MEM0_API_KEY`` (Claude Code env injection)
- ``~/.zshrc`` / ``~/.bashrc`` ``export MEM0_API_KEY="..."`` lines
Out of scope (deliberately not touched):
- Codex / Cursor MCP configs — would require schema-aware edits and
those tools don't have mem0 entries by default
- Plugin's own ``<plugin-dir>/.api_key`` file — plugin-managed
"""
from __future__ import annotations
import contextlib
import json
import os
import re
import tempfile
from pathlib import Path
# Files we know how to update safely.
_CLAUDE_SETTINGS = Path.home() / ".claude" / "settings.json"
_SHELL_RCS = [Path.home() / ".zshrc", Path.home() / ".bashrc", Path.home() / ".bash_profile"]
def sync_api_key(api_key: str) -> list[str]:
"""Propagate ``api_key`` into known ecosystem touchpoints.
Returns the list of paths actually updated. Empty list means nothing
needed updating (either targets didn't exist or already had this value).
"""
if not api_key:
return []
updated: list[str] = []
if _update_claude_settings(_CLAUDE_SETTINGS, api_key):
updated.append(str(_CLAUDE_SETTINGS))
for rc in _SHELL_RCS:
if _update_shell_rc(rc, api_key):
updated.append(str(rc))
return updated
def _update_claude_settings(path: Path, api_key: str) -> bool:
"""Update ``env.MEM0_API_KEY`` in path. Returns True if file was changed."""
if not path.is_file():
return False
try:
with path.open("r", encoding="utf-8") as f:
data = json.load(f)
except (json.JSONDecodeError, OSError):
return False
env = data.get("env")
if not isinstance(env, dict) or "MEM0_API_KEY" not in env:
# No existing entry — don't create one.
return False
if env["MEM0_API_KEY"] == api_key:
return False # already in sync
env["MEM0_API_KEY"] = api_key
_atomic_write_text(path, json.dumps(data, indent=2, ensure_ascii=False) + "\n")
return True
# Match `export MEM0_API_KEY="..."` (or single quotes, or no quotes).
# Use [ \t]* (not \s*) for trailing whitespace so a trailing newline at
# end-of-file is preserved when MEM0_API_KEY is the last line.
_RC_LINE = re.compile(
r'^([ \t]*export[ \t]+MEM0_API_KEY[ \t]*=[ \t]*)(["\']?)([^"\'\n]*)(["\']?)[ \t]*$',
re.MULTILINE,
)
def _update_shell_rc(path: Path, api_key: str) -> bool:
"""Update an existing ``export MEM0_API_KEY=...`` line in path."""
if not path.is_file():
return False
try:
text = path.read_text(encoding="utf-8")
except OSError:
return False
match = _RC_LINE.search(text)
if not match:
return False # no existing line
if match.group(3) == api_key:
return False
new_text = _RC_LINE.sub(lambda m: f'{m.group(1)}"{api_key}"', text, count=1)
_atomic_write_text(path, new_text)
return True
def _atomic_write_text(path: Path, content: str) -> None:
"""Write content to path atomically (temp + rename)."""
dirname = path.parent
fd, tmp_path = tempfile.mkstemp(prefix=f".{path.name}.", suffix=".tmp", dir=dirname)
try:
with os.fdopen(fd, "w", encoding="utf-8") as f:
f.write(content)
# Preserve mode if the original existed.
if path.exists():
os.chmod(tmp_path, path.stat().st_mode & 0o777)
os.replace(tmp_path, path)
except Exception:
with contextlib.suppress(OSError):
os.unlink(tmp_path)
raise
+45
View File
@@ -0,0 +1,45 @@
"""Agent mode state — set by the root callback, read by commands and branding."""
from __future__ import annotations
_agent_mode: bool = False
_current_command: str = ""
_pending_notice: 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
def capture_notice(notice: str | None) -> None:
"""Stash a Mem0 backend notice for end-of-command surfacing.
Called from the platform backend after each response so the notice can
be printed once per command (regardless of how many sub-requests fired).
Last-write-wins is fine — the message text is identical across requests.
"""
global _pending_notice
if notice:
_pending_notice = notice
def take_notice() -> str:
"""Return and clear the pending notice."""
global _pending_notice
msg = _pending_notice
_pending_notice = ""
return msg
+148
View File
@@ -0,0 +1,148 @@
"""CLI telemetry — anonymous usage tracking via PostHog.
Sends fire-and-forget events to PostHog by spawning a detached subprocess
(telemetry_sender.py). The parent CLI process exits immediately; the
subprocess handles email resolution, caching, and the HTTP POST.
Disable with: MEM0_TELEMETRY=false
"""
from __future__ import annotations
import contextlib
import hashlib
import json
import os
import platform
import subprocess
import sys
import uuid
from typing import Any
POSTHOG_API_KEY = "phc_hgJkUVJFYtmaJqrvf6CYN67TIQ8yhXAkWzUn9AMU4yX"
POSTHOG_HOST = "https://us.i.posthog.com/i/v0/e/"
def _is_telemetry_enabled() -> bool:
val = os.environ.get("MEM0_TELEMETRY", "true").lower()
return val not in ("false", "0", "no")
def _get_or_create_anonymous_id() -> str:
"""Return a persistent per-machine anonymous ID, generating one if needed.
Stored in ~/.mem0/config.json under `telemetry.anonymous_id` so that
repeat runs on the same machine share one PostHog identity instead of
collapsing into a single shared fallback string.
"""
from mem0_cli.config import load_config, save_config
config = load_config()
if config.telemetry.anonymous_id:
return config.telemetry.anonymous_id
new_id = f"cli-anon-{uuid.uuid4().hex}"
config.telemetry.anonymous_id = new_id
with contextlib.suppress(Exception):
save_config(config)
return new_id
def _get_distinct_id() -> str:
"""Return a stable anonymous identifier for the current user.
Priority: cached user_email (from /v1/ping/) > MD5(api_key) >
persistent per-machine anonymous ID.
"""
try:
from mem0_cli.config import load_config
config = load_config()
if config.platform.user_email:
return config.platform.user_email
if config.platform.api_key:
return hashlib.md5(config.platform.api_key.encode()).hexdigest()
except Exception:
pass
try:
return _get_or_create_anonymous_id()
except Exception:
return f"cli-anon-{uuid.uuid4().hex}"
def capture_event(
event_name: str,
properties: dict[str, Any] | None = None,
pre_resolved_email: str | None = None,
) -> None:
"""Fire a PostHog event via a detached subprocess (non-blocking).
When *pre_resolved_email* is provided (e.g. from an upfront ping
validation), it is used directly as the PostHog distinct ID and the
subprocess skips its own ``/v1/ping/`` call.
"""
if not _is_telemetry_enabled():
return
try:
from mem0_cli import __version__
from mem0_cli.config import CONFIG_FILE, load_config, save_config
config = load_config()
distinct_id = pre_resolved_email or _get_distinct_id()
# Detect anonymous → identified transition. If a stored anonymous_id
# exists and we just resolved to a real identity, fire a one-shot
# $identify event so PostHog stitches the pre-signup history onto
# the authenticated profile. Clear the stored id so we don't re-alias.
anon_id_to_alias: str | None = None
if (
distinct_id
and not distinct_id.startswith("cli-anon-")
and config.telemetry.anonymous_id
):
anon_id_to_alias = config.telemetry.anonymous_id
config.telemetry.anonymous_id = ""
with contextlib.suppress(Exception):
save_config(config)
# M4: every cli.* event carries agent_mode based on the config flag
# (unclaimed Agent Mode key). This is the growth-doc property used to
# join init → add → search funnels in PostHog.
payload = {
"api_key": POSTHOG_API_KEY,
"distinct_id": distinct_id,
"event": event_name,
"properties": {
"source": "CLI",
"language": "python",
"cli_version": __version__,
"agent_mode": bool(config.platform.agent_mode),
"python_version": sys.version,
"os": sys.platform,
"os_version": platform.version(),
"$process_person_profile": False,
"$lib": "posthog-python",
**(properties or {}),
},
}
context = {
"payload": payload,
"posthog_host": POSTHOG_HOST,
"needs_email": not distinct_id or "@" not in distinct_id,
"mem0_api_key": config.platform.api_key or "",
"mem0_base_url": config.platform.base_url or "https://api.mem0.ai",
"config_path": str(CONFIG_FILE),
"anon_distinct_id_to_alias": anon_id_to_alias,
}
subprocess.Popen(
[sys.executable, "-m", "mem0_cli.telemetry_sender", json.dumps(context)],
stdout=subprocess.DEVNULL,
stderr=subprocess.DEVNULL,
start_new_session=True,
close_fds=True,
)
except Exception:
pass

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