Default the episodic long-term memory backend to event memory by edwinyyyu · Pull Request #1518 · MemMachine/MemMachine · GitHub
Skip to content

Default the episodic long-term memory backend to event memory - #1518

Open
edwinyyyu wants to merge 1 commit into
MemMachine:mainfrom
edwinyyyu:default-event-backend
Open

Default the episodic long-term memory backend to event memory#1518
edwinyyyu wants to merge 1 commit into
MemMachine:mainfrom
edwinyyyu:default-event-backend

Conversation

@edwinyyyu

Copy link
Copy Markdown
Contributor

Purpose of the change

The configuration wizard has defaulted to backend: event for a while (configuration_wizard.py), but the static shipped configs still wired the declarative VectorGraphStore backend. So every install that does not go through the wizard -- memmachine-compose.sh, the Helm chart, anyone copying a sample -- landed on declarative. This makes the event backend (VectorStore + SegmentStore) the default in the configuration files too, and makes the shipped stacks actually able to run it.

Description

Configuration files

  • sample_configs/episodic_memory_config.{cpu,gpu}.sample: backend: event with vector_store: event_vector_store and segment_store: profile_storage. The declarative block is preserved as the commented alternative.
  • sample_configs/episodic_memory_config.nebula.sample stays declarative on purpose -- it exists to demonstrate NebulaGraph as the vector_graph_store -- and now states backend: declarative explicitly with a pointer to the cpu/gpu samples.
  • The Neo4j resource is commented out in the cpu/gpu samples. Resources are not lazy: ResourceManager.build() calls DatabaseManager.build_all(validate=True), which connects to and validates every configured database. A configured-but-unused Neo4j entry would keep a reachable Neo4j server as a hard startup requirement, so leaving it defined would defeat the switch.
  • Helm ConfigMap: the episodic block and the database resources are now selected by a new memmachine.longTermMemory.backend value (default event). Only the resource the selected backend needs is emitted, for the same eager-validation reason.

Making the default runnable

Qdrant is the shipped vector store, matching what the samples already recommended and what the wizard picks when the extra is available.

  • Dockerfile: installs the qdrant extra. Without it, qdrant-client is missing inside the published image and a qdrant resource fails to build. (No pyproject.toml/uv.lock change -- qdrant-client>=1.17.0 is already a declared optional dependency and is in the lock.)
  • docker-compose.yml: adds a qdrant service (pinned v1.19.0), a qdrant_data volume, and a healthcheck. The healthcheck probes the port with bash's /dev/tcp because the Qdrant image ships neither curl nor wget.
  • docker-compose.yml: Neo4j moves behind a declarative compose profile, and MemMachine's depends_on switches from Neo4j to Qdrant. A default docker compose up no longer starts or blocks on a database nothing uses; docker compose --profile declarative up brings it back.
  • memmachine-compose.sh: waits on Qdrant, keeps the Neo4j wait for when that profile is running, prints the Qdrant endpoint, and rewrites localhost to the qdrant service name for qdrant resources -- the same treatment postgres and neo4j already get.
  • Helm: new qdrant-deployment.yaml / qdrant-service.yaml / qdrant-pvc, a qdrant.* values block, and the app's init container waits on Qdrant or Neo4j to match the selected backend.

One deliberate asymmetry to flag for review. In docker-compose the Neo4j service is kept (behind a profile) so switching back is easy. In Helm, neo4j.enabled now defaults to false, so the chart does not provision an in-cluster Neo4j Deployment plus a 5Gi RWX PVC that nothing reads. Switching back is --set memmachine.longTermMemory.backend=declarative --set neo4j.enabled=true. Happy to flip that to true if you would rather the chart keep deploying Neo4j by default.

Not changed: the parse-time default. A config with no backend field still loads as declarative (_long_term_memory_backend_discriminator), so pre-discriminator configs keep working.

Fixes/Closes

N/A

Type of change

  • New feature (non-breaking change which adds functionality)
  • Documentation update
  • Project Maintenance (updates to build scripts, CI, etc., that do not affect the main project)

How Has This Been Tested?

  • Unit Test
  • Test Script (please provide)

test_existing_sample_yaml_loads_as_declarative_via_full_configuration pinned both "the cpu sample has no backend field" and "a backend-less config resolves to declarative". Those are now separate tests: one asserts the sample declares the event backend and resolves to EventLongTermMemoryConf, the other synthesizes a pre-discriminator config and asserts it still resolves to DeclarativeLongTermMemoryConf.

Test Results:

$ uv run --package memmachine-server pytest \
    packages/server/server_tests/memmachine_server/common/configuration \
    packages/server/server_tests/memmachine_server/installation \
    packages/server/server_tests/memmachine_server/server/api_v2/test_config_service.py \
    packages/server/server_tests/memmachine_server/server/api_v2/test_config_router.py -q
240 passed, 3 warnings in 2.78s

Every changed config was also validated against the real Configuration pydantic model, not just parsed as YAML:

Config Result
cpu sample OK -- backend='event' vector_store='event_vector_store' segment_store='profile_storage'
gpu sample OK -- same
cpu sample after memmachine-compose.sh's awk rewrite OK -- hosts rewritten to postgres / qdrant
helm template default OK -- backend='event' vector_store='db_qdrant' segment_store='db_postgres'
helm template --set memmachine.longTermMemory.backend=declarative --set neo4j.enabled=true OK -- backend='declarative' vector_graph_store='db_neo4j'

Also checked:

  • docker compose config passes; config --services lists qdrant, docs, postgres, memmachine by default and adds neo4j under --profile declarative.
  • helm template renders cleanly in both backend modes; the default render contains no Neo4j Deployment/Service/PVC.
  • bash -n memmachine-compose.sh, and the awk rewrite was extracted and run against the new sample.
  • uv lock --check passes (no dependency change).

Not tested: an actual docker compose up end to end, since no Docker daemon was available in this environment. The Qdrant healthcheck's reliance on bash in the Qdrant image is worth one live confirmation before merge (the image is debian:13-slim-based, which includes bash but not curl).

Checklist

  • I have signed the commit(s) within this pull request
  • My code follows the style guidelines of this project (See STYLE_GUIDE.md)
  • I have performed a self-review of my own code
  • I have commented my code
  • I have made corresponding changes to the documentation
  • My changes generate no new warnings
  • I have added unit tests that prove my fix is effective or that my feature works
  • New and existing unit tests pass locally with my changes

Screenshots/Gifs

N/A

Further comments

Migration. This does not migrate data. Memories written through the declarative backend live in Neo4j and are not visible to the event backend. Existing deployments are unaffected until they adopt the new samples, because configuration.yml is gitignored and generated only when absent -- but a fresh install after this change will not see an old install's memories.

Docs left alone deliberately. The provider-specific guides (docs/install_guide/{ollama,aws_bedrock}.mdx and their config/ copies, the NebulaGraph integration guide, and the CloudFormation template in cloud_deploy/aws_cloudformation.mdx) still show the declarative backend. Each is tied to a specific stack, and the CloudFormation one would need Qdrant added to the template. Happy to do those in a follow-up if you want them converted.

Unrelated pre-existing issue noticed. sample_configs/episodic_memory_config.nebula.sample fails Configuration.model_validate on semantic_memory.config_database being required but absent. This reproduces identically on main, so it is untouched here -- flagging it rather than mixing the fix into this PR.

🤖 Generated with Claude Code

@edwinyyyu
edwinyyyu requested a review from malatewang August 19, 2026 20:16
The configuration wizard has defaulted to `backend: event` for a while, but
the static shipped configs (sample_configs/*.sample, the Helm ConfigMap) still
wired the declarative VectorGraphStore backend, so every non-wizard install
landed on declarative.

Switch those defaults to the event backend (VectorStore + SegmentStore) and
make the shipped stacks able to run it:

- sample_configs cpu/gpu: `backend: event` with `vector_store: event_vector_store`
  and `segment_store: profile_storage`; the declarative block moves to a
  commented alternative. The nebula sample stays declarative on purpose -- it
  exists to demonstrate NebulaGraph -- and now says `backend: declarative`
  explicitly.
- The Neo4j resource is commented out in the cpu/gpu samples. Resources are not
  lazy: ResourceManager.build() calls build_all(validate=True), so a configured
  but unused Neo4j entry would still make a reachable Neo4j server a startup
  requirement.
- Qdrant becomes the shipped vector store. It is added to docker-compose (with
  a volume and a healthcheck that uses bash's /dev/tcp, since the image has no
  curl) and to the Helm chart (Deployment, Service, PVC, values). The Dockerfile
  now installs the `qdrant` extra, without which a qdrant resource fails to
  build inside the published image.
- docker-compose moves Neo4j behind a `declarative` profile so the default `up`
  no longer starts or waits on it; `docker compose --profile declarative up`
  brings it back. memmachine-compose.sh waits on Qdrant instead, keeps the Neo4j
  wait for when that profile is active, and rewrites `localhost` to `qdrant` for
  qdrant resources the way it already does for postgres and neo4j.
- Helm gains `memmachine.longTermMemory.backend` (default `event`). Only the
  database resource the selected backend needs is emitted, and the app's init
  container waits on Qdrant or Neo4j to match. `neo4j.enabled` now defaults to
  false so the chart does not provision an unused StatefulSet and RWX PVC.

The parse-time default is unchanged: a config with no `backend` field still
loads as declarative for backwards compatibility. The test that pinned this
behaviour to the cpu sample is split in two -- one asserting the sample now
declares the event backend, one asserting a synthesized pre-discriminator
config still resolves to declarative.

This does not migrate data. Memories written through the declarative backend
live in Neo4j and are not visible to the event backend; existing deployments
keep their own configuration.yml and are unaffected until they adopt the new
samples.

Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
Signed-off-by: Edwin Yu <edwinyyyu@gmail.com>
@edwinyyyu
edwinyyyu force-pushed the default-event-backend branch from 5cb3625 to 6c5b4bd Compare August 25, 2026 23:13
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant