Skip to content

Feat: Forgetting mechanism and recency boost #45

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 24 commits into from
Aug 13, 2025

Conversation

abrookins
Copy link
Collaborator

Summary

  • Adds policy-based forgetting (deletions) and DB-level recency-aware ranking for long-term memories.
  • Extends Redis memory schema with pinned and access_count.
  • Improves client robustness and keeps MCP responses JSON-compatible.

Why

  • Control unbounded growth (forgetting) and prioritize fresher, useful memories (recency).
  • Enable future queries on pinned/usage stats.
  • Eliminate client-side rerank bottlenecks by computing scores in Redis.

What’s in this PR

  • Forgetting

    • New endpoint: POST /v1/long-term-memory/forget with dry-run support.
    • Policy supports TTL, inactivity, and budget “keep top N” via forgetting_budget_keep_top_n.
    • Selection is explainable; deletions are explicit.
  • Recency-aware ranking

    • Redis path now uses FT.AGGREGATE (via RedisVL AggregationQuery) with:
      • KNN via VectorQuery/RangeQuery (uses num_results, distance_threshold).
      • LOAD required fields + __vector_score.
      • APPLY freshness/novelty/recency/sim and composite boosted_score.
      • SORTBY boosted_score DESC; paging via limit(offset, limit).
    • Fallback: LangChainVectorStoreAdapter now reranks in Python if DB-level path isn’t available.
  • Redis schema/index

    • Adds pinned (tag) and access_count (numeric) to metadata schema and index.
    • Normalizes list-like fields (topics, entities, extracted_from) during mapping.

Config

  • Rename: forgetting_budget_per_userforgetting_budget_keep_top_n (non-user-centric naming).

Implementation notes

  • Uses RedisVL query types end-to-end:
    • VectorQuery/RangeQuery for KNN selection.
    • AggregationQuery with APPLY/SORTBY for scoring.
  • Accesses RedisVL index via self.vectorstore._index for aggregate queries. (TODO: property decorator...?)

Backward compatibility

  • API remains compatible; MCP output remains JSON string.
  • Config rename is additive in code paths; updated docs reflect the new name.

Follow-ups

  • Expose recency params in docs with recommended defaults.
  • Add query-time toggles for pin/usage-aware retrieval if needed.

@Copilot Copilot AI review requested due to automatic review settings August 9, 2025 01:05
Copy link
Contributor

@Copilot Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull Request Overview

This PR implements a comprehensive forgetting mechanism and recency-aware ranking system for long-term memories. It introduces policy-based memory deletion with dry-run support and extends the Redis schema to support pinned memories and access count tracking. The system also implements server-side recency ranking using Redis aggregations for improved performance.

  • Adds a new forgetting system with TTL, inactivity, and budget-based deletion policies
  • Implements recency-aware ranking that combines semantic similarity with freshness/novelty scores
  • Extends Redis schema with pinned and access_count fields for enhanced memory management

Reviewed Changes

Copilot reviewed 18 out of 19 changed files in this pull request and generated 7 comments.

Show a summary per file
File Description
tests/test_recency_aggregation.py Tests for Redis aggregation-based recency ranking functionality
tests/test_forgetting_job.py Tests for the forgetting endpoint including dry-run and deletion scenarios
tests/test_forgetting.py Unit tests for core forgetting algorithms and recency scoring functions
tests/test_api.py API endpoint tests for forget functionality and recency boost features
docs/api.md Documentation updates for new forget endpoint and recency parameters
agent_memory_server/vectorstore_factory.py Redis schema updates to include pinned and access_count fields
agent_memory_server/vectorstore_adapter.py Enhanced adapters with server-side recency support and list field normalization
agent_memory_server/utils/redis_query.py New Redis aggregation query helper for recency-aware ranking
agent_memory_server/models.py Model extensions for pinned/access_count fields and recency parameters
agent_memory_server/mcp.py MCP response format updates to ensure JSON compatibility
agent_memory_server/long_term_memory.py Core forgetting and recency algorithms implementation
agent_memory_server/docket_tasks.py Task registration for periodic forgetting functionality
agent_memory_server/config.py Configuration settings for forgetting policies
agent_memory_server/api.py New forget endpoint and recency-aware search implementation
agent-memory-client/tests/test_client.py Client tests for recency configuration
agent-memory-client/agent_memory_client/models.py Client-side RecencyConfig model
agent-memory-client/agent_memory_client/client.py Client integration for recency parameters
CLAUDE.md Development workflow documentation updates

abrookins and others added 2 commits August 11, 2025 15:22
…ic, update docs

- Add numbers.Number-based type checking with _is_numeric() helper
- Extract Redis aggregation logic into separate _search_with_redis_aggregation() method
- Add safe _get_vectorstore_index() method to avoid direct _index access
- Document hard_age_multiplier parameter in select_ids_for_forgetting docstring
- Remove stale TDD comment from test file

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Update core algorithms to use descriptive names (freshness_weight, novelty_weight, etc.)
- Add backward compatibility for old short names (wf, wa, w_sem, w_recency)
- Update API models with new descriptive field names while preserving old ones
- Add helper function to build recency params with fallback to old names
- Update tests to demonstrate new preferred parameter naming
- Internal functions now use clear variable names (semantic_weight vs w_sem)

Old names still work for backward compatibility:
- wf → freshness_weight
- wa → novelty_weight
- w_sem → semantic_weight
- w_recency → recency_weight

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Raises:
Exception: If Redis aggregation fails (caller should handle fallback)
"""
from datetime import UTC as _UTC, datetime as _dt
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

ugh

abrookins and others added 4 commits August 12, 2025 11:27
- Update client-side reranking fallback to use descriptive parameter names
- Add backward compatibility for old parameter names in parameter extraction
- Both server-side and client-side recency paths now use descriptive names internally
- Update task memory with completion status

All server-side components now use readable parameter names:
- semantic_weight (vs w_sem)
- recency_weight (vs w_recency)
- freshness_weight (vs wf)
- novelty_weight (vs wa)

Full backward compatibility maintained - all old names still work.
Comprehensive test suite passes: 35+ tests across all affected modules.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Remove TDD section comment as requested
- Extract SECONDS_PER_DAY constant (86400.0) to global variable
- Fix docstring to reference vectorstore adapter instead of RedisVL directly

All single/two-letter variable expansion was already completed in previous commits.
Tests continue to pass after these cleanup changes.

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Updates client library models and implementation with descriptive parameter names:
- Add new descriptive fields to RecencyConfig model alongside legacy ones
- Update client.py parameter mapping with precedence logic
- Add comprehensive test for parameter precedence validation
- Maintain full backward compatibility with legacy short names

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Updates documentation with clean examples using descriptive parameter names:
- Update API documentation example with descriptive recency parameters
- Add comprehensive recency configuration section to client README
- Demonstrate clear, readable parameter naming

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
abrookins and others added 3 commits August 12, 2025 12:13
…adapter

- Remove duplicate client-side reranking logic by extracting shared helper method
- Use SECONDS_PER_DAY constant instead of magic number 86400.0 in redis_query.py
- Add type annotations and improve docstrings for helper methods
- Remove stale TODO comments and improve code documentation
- Remove duplicate _parse_list_field method in RedisVectorStoreAdapter
- Clean up comment formatting and remove unnecessary complexity

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
Move all scattered inline imports to the top of the file for better
organization and readability. Use lazy imports only where necessary
to avoid circular dependencies with long_term_memory module.

- Consolidated all standard library and third-party imports at top
- Used lazy imports for generate_memory_hash, rerank_with_recency,
  and RecencyAggregationQuery to prevent circular import issues
- Maintained proper import organization per Python conventions
- All tests passing, no functional changes

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
result = await core_memory_prompt(
params=MemoryPromptRequest(query=query, **_params)
)
return TextContent(type="text", text=_json.dumps(result.model_dump()))
Copy link
Collaborator Author

Choose a reason for hiding this comment

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

Why are we not returning the Pydantic objects?

abrookins and others added 6 commits August 12, 2025 17:22
- Move recency functions to utils.recency to eliminate circular imports
- Update MCP methods to return Pydantic objects instead of JSON strings
- Move imports to top of files now that circular dependencies are resolved

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Merged recency configuration and optimize_query parameter in client
- Updated MCP search to use optimize_query while preserving Pydantic returns

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Update test imports to use utils.recency module for moved functions
- Remove unnecessary comments from imports

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
- Add proper error handling for malformed LLM responses in extract_memories_from_session_thread
- Check response structure before accessing choices[0].message.content
- Return empty list instead of crashing when response is malformed

🤖 Generated with [Claude Code](https://claude.ai/code)

Co-Authored-By: Claude <[email protected]>
@abrookins abrookins merged commit 9b1c318 into main Aug 13, 2025
10 checks passed
@abrookins abrookins deleted the feature/forgetting-recency branch August 13, 2025 03:48
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