Skip to content

feat: add Redis Cloud memory migration#305

Open
jsachs1300 wants to merge 1 commit into
redis:mainfrom
jsachs1300:feat/redis-cloud-memory-migration
Open

feat: add Redis Cloud memory migration#305
jsachs1300 wants to merge 1 commit into
redis:mainfrom
jsachs1300:feat/redis-cloud-memory-migration

Conversation

@jsachs1300

@jsachs1300 jsachs1300 commented Jun 11, 2026

Copy link
Copy Markdown

Summary

Adds a Redis Cloud Agent Memory Service to local agent-memory-server migration path.

This handles the schema/key-layout mismatch found when Cloud Agent Memory data is copied directly into a local Redis deployment:

  • Cloud keys: memory:<store_id>:ltm:<memory_id>
  • Local keys: memory_idx:<memory_id>
  • Cloud fields: id, owner_id, text_vector
  • Local fields: id_, user_id, vector
  • Cloud vectors observed as 1536 float64 values / 12288-byte blobs
  • Local RedisVL schema expects 1536 FLOAT32 values / 6144-byte blobs

The migration command copies source hashes into local-shaped hashes, preserves source data, converts timestamps from milliseconds to seconds, and downcasts vectors when needed.

New command

agent-memory migrate-cloud-long-term-memory --store-id <store-id>
agent-memory migrate-cloud-long-term-memory --store-id <store-id> --apply
agent-memory rebuild-index

Docs

  • Adds README usage section
  • Adds detailed migration guide: docs/redis-cloud-migration.md

Test plan

uv run pytest tests/test_redis_cloud_migration.py tests/test_cli.py::TestRebuildIndex::test_rebuild_index_command -q
uv run ruff check agent_memory_server/redis_cloud_migration.py agent_memory_server/cli.py tests/test_redis_cloud_migration.py

Local results:

  • 5 passed
  • All checks passed!

Real-world verification

On a local Redis copy of Cloud Agent Memory data:

  • Dry run found 753 eligible Cloud records, 0 failures
  • Applied migration copied 753 records into local schema
  • Rebuilt memory_records
  • FT.INFO memory_records reported num_docs=753, hash_indexing_failures=0
  • Local /v1/long-term-memory/search returned migrated memories successfully

Note

Cursor Bugbot is generating a summary for commit fe16ef0. Configure here.

@jit-ci

jit-ci Bot commented Jun 11, 2026

Copy link
Copy Markdown

Hi, I’m Jit, a friendly security platform designed to help developers build secure applications from day zero with an MVS (Minimal viable security) mindset.

In case there are security findings, they will be communicated to you as a comment inside the PR.

Hope you’ll enjoy using Jit.

Questions? Comments? Want to learn more? Get in touch with us.

@cursor cursor Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cursor Bugbot has reviewed your changes and found 3 potential issues.

Fix All in Cursor

Reviewed by Cursor Bugbot for commit fe16ef0. Configure here.

target = cloud_hash_to_local_hash(source)
if target is None:
stats.failed += 1
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Malformed records abort migration

High Severity

The migration loop calls cloud_hash_to_local_hash without catching errors, yet that helper can raise on bad pinned/access_count values or tag fields with commas via int() and encode_tag_values. One bad Cloud hash stops the entire run instead of incrementing failed, contrary to the module’s stated per-record handling.

Additional Locations (1)
Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fe16ef0. Configure here.

}
target["memory_hash"] = hashlib.sha256(
json.dumps(content_fields, sort_keys=True).encode()
).hexdigest()

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Generated hash mismatches server

Medium Severity

When Cloud omits memory_hash, the migration builds one from content_fields using empty strings for missing user_id, session_id, and namespace. generate_memory_hash in utils/recency.py serializes those optional fields as JSON null, so the same memory content gets a different hash after migration than for natively created records.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fe16ef0. Configure here.


if not overwrite and await redis.exists(target_key):
stats.skipped_existing += 1
continue

Copy link
Copy Markdown

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Cross-store IDs collide silently

Medium Severity

Target keys are only {target_prefix}:{memory_id}, so scanning memory:*:ltm:* merges every store into one namespace. Duplicate IDs across stores are skipped as existing or overwritten with --overwrite, with no warning that memories from another store were dropped.

Fix in Cursor Fix in Web

Reviewed by Cursor Bugbot for commit fe16ef0. Configure here.

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