Skip to content
Open
Show file tree
Hide file tree
Changes from 5 commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -13,7 +13,7 @@ A memory layer for AI agents.
- **Dual Interface**: REST API and Model Context Protocol (MCP) server
- **Two-Tier Memory**: Working memory (session-scoped) and long-term memory (persistent)
- **Configurable Memory Strategies**: Customize how memories are extracted (discrete, summary, preferences, custom)
- **Semantic Search**: Vector-based similarity search with metadata filtering
- **Semantic, Keyword & Hybrid Search**: Vector-based similarity, full-text keyword, and combined hybrid search with metadata filtering
- **Flexible Backends**: Pluggable memory vector database factory system
- **Multi-Provider LLM Support**: OpenAI, Anthropic, AWS Bedrock, Ollama, Azure, Gemini via [LiteLLM](https://docs.litellm.ai/)
- **AI Integration**: Automatic topic extraction, entity recognition, and conversation summarization
Expand Down Expand Up @@ -268,7 +268,7 @@ See **[LLM Providers](https://redis.github.io/agent-memory-server/llm-providers/
```
Working Memory (Session-scoped) → Long-term Memory (Persistent)
↓ ↓
- Messages - Semantic search
- Messages - Semantic, keyword & hybrid search
- Structured memories - Topic modeling
- Summary of past messages - Entity recognition
- Metadata - Deduplication
Expand Down
23 changes: 19 additions & 4 deletions agent-memory-client/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -165,7 +165,7 @@ working_memory = WorkingMemory(
response = await client.put_working_memory("user-session-123", working_memory)

# Retrieve working memory
memory = await client.get_working_memory("user-session-123")
created, memory = await client.get_or_create_working_memory("user-session-123")

# Convenience method for data storage
await client.set_working_memory_data(
Expand Down Expand Up @@ -201,6 +201,21 @@ results = await client.search_long_term_memory(
user_id=UserId(eq="user-123"),
limit=20
)

# Keyword search - full-text matching
results = await client.search_long_term_memory(
text="science fiction",
search_mode="keyword",
limit=20
)

# Hybrid search - combines semantic and keyword matching
results = await client.search_long_term_memory(
text="science fiction",
search_mode="hybrid",
hybrid_alpha=0.7, # 0.0=keyword, 1.0=semantic
limit=20
)
```

## Enhanced Features
Expand Down Expand Up @@ -333,9 +348,9 @@ from agent_memory_client.exceptions import (
)

try:
memory = await client.get_working_memory("nonexistent-session")
except MemoryNotFoundError:
print("Session not found")
created, memory = await client.get_or_create_working_memory("nonexistent-session")
if created:
print("New session created")
except MemoryServerError as e:
print(f"Server error {e.status_code}: {e}")
except MemoryClientError as e:
Expand Down
17 changes: 16 additions & 1 deletion agent-memory-client/agent-memory-client-js/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -90,14 +90,29 @@ await client.createLongTermMemory([
},
]);

// Search with filters
// Search with filters (default: semantic search)
const results = await client.searchLongTermMemory({
text: "science fiction",
topics: new Topics({ any: ["books", "entertainment"] }),
userId: new UserId({ eq: "user-123" }),
limit: 20,
});

// Keyword search - full-text matching
const keywordResults = await client.searchLongTermMemory({
text: "science fiction",
searchMode: "keyword",
limit: 20,
});

// Hybrid search - combines semantic and keyword matching
const hybridResults = await client.searchLongTermMemory({
text: "science fiction",
searchMode: "hybrid",
hybridAlpha: 0.7, // 0.0=keyword, 1.0=semantic
limit: 20,
});

// Get by ID
const memory = await client.getLongTermMemory("memory-id");

Expand Down
40 changes: 31 additions & 9 deletions agent-memory-client/agent_memory_client/client.py
Original file line number Diff line number Diff line change
Expand Up @@ -1386,13 +1386,13 @@ async def handle_tool_calls(client, tool_calls):
"type": "function",
"function": {
"name": "search_memory",
"description": "Search long-term memory for relevant information using semantic vector search. Use this when you need to find previously stored information about the user, such as their preferences, past conversations, or important facts. Examples: 'Find information about user food preferences', 'What did they say about their job?', 'Look for travel preferences'. This searches only long-term memory, not current working memory - use get_working_memory for current session info. IMPORTANT: The result includes 'memories' with an 'id' field; use these IDs when calling edit_long_term_memory or delete_long_term_memories.",
"description": "Search long-term memory for relevant information using semantic, keyword, or hybrid search. Use this when you need to find previously stored information about the user, such as their preferences, past conversations, or important facts. Examples: 'Find information about user food preferences', 'What did they say about their job?', 'Look for travel preferences'. This searches only long-term memory, not current working memory - use get_or_create_working_memory for current session info. IMPORTANT: The result includes 'memories' with an 'id' field; use these IDs when calling edit_long_term_memory or delete_long_term_memories.",
"parameters": {
"type": "object",
"properties": {
"query": {
"type": "string",
"description": "The query for vector search describing what information you're looking for",
"description": "The search query describing what information you're looking for",
},
"search_mode": {
"type": "string",
Expand Down Expand Up @@ -1875,7 +1875,7 @@ def get_update_memory_data_tool_schema(cls) -> ToolSchema:
"type": "function",
"function": {
"name": "update_working_memory_data",
"description": "Store or update structured session data (JSON objects) in working memory. Use this for complex session-specific information that needs to be accessed and modified during the conversation. Examples: Travel itinerary {'destination': 'Paris', 'dates': ['2024-03-15', '2024-03-20']}, project details {'name': 'Website Redesign', 'deadline': '2024-04-01', 'status': 'in_progress'}. Different from add_memory_to_working_memory which stores simple text facts.",
"description": "Store or update structured session data (JSON objects) in working memory. Use this for complex session-specific information that needs to be accessed and modified during the conversation. Examples: Travel itinerary {'destination': 'Paris', 'dates': ['2024-03-15', '2024-03-20']}, project details {'name': 'Website Redesign', 'deadline': '2024-04-01', 'status': 'in_progress'}. Different from lazily_create_long_term_memory which stores simple text facts for later promotion to long-term storage.",
"parameters": {
"type": "object",
"properties": {
Expand Down Expand Up @@ -2692,9 +2692,15 @@ async def _resolve_search_memory(self, args: dict[str, Any]) -> dict[str, Any]:
max_results = args.get("max_results", 5)
min_relevance = args.get("min_relevance")
user_id = args.get("user_id")
search_mode = args.get("search_mode", "semantic")
hybrid_alpha = args.get("hybrid_alpha")
text_scorer = args.get("text_scorer")

return await self.search_memory_tool(
query=query,
search_mode=search_mode,
hybrid_alpha=hybrid_alpha,
text_scorer=text_scorer,
topics=topics,
entities=entities,
memory_type=memory_type,
Expand All @@ -2706,7 +2712,7 @@ async def _resolve_search_memory(self, args: dict[str, Any]) -> dict[str, Any]:
async def _resolve_get_working_memory(
self, session_id: str, namespace: str | None, user_id: str | None = None
) -> dict[str, Any]:
"""Resolve get_working_memory function call."""
"""Resolve get_working_memory (deprecated) function call."""
return await self.get_working_memory_tool(
session_id=session_id,
namespace=namespace,
Expand All @@ -2731,7 +2737,7 @@ async def _resolve_add_memory(
namespace: str | None,
user_id: str | None = None,
) -> dict[str, Any]:
"""Resolve add_memory_to_working_memory function call."""
"""Resolve lazily_create_long_term_memory (formerly add_memory_to_working_memory) function call."""
text = args.get("text", "")
if not text:
raise ValueError("Text parameter is required for adding memory")
Expand Down Expand Up @@ -2790,11 +2796,11 @@ async def _resolve_get_long_term_memory(
async def _resolve_create_long_term_memory(
self, args: dict[str, Any], namespace: str | None, user_id: str | None = None
) -> dict[str, Any]:
"""Resolve create_long_term_memory function call."""
"""Resolve eagerly_create_long_term_memory (and deprecated create_long_term_memory alias) function call."""
memories_data = args.get("memories")
if not memories_data:
raise ValueError(
"memories parameter is required for create_long_term_memory"
"memories parameter is required for eagerly_create_long_term_memory"
)
Copy link

Copilot AI Mar 27, 2026

Choose a reason for hiding this comment

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

The resolver explicitly supports the deprecated create_long_term_memory alias, but the error message only references eagerly_create_long_term_memory. This can be confusing when the caller invoked the deprecated name. Consider making the error message function-name-agnostic (e.g., just “memories parameter is required”) or mentioning both names.

Copilot uses AI. Check for mistakes.

# Convert dict memories to ClientMemoryRecord objects
Expand Down Expand Up @@ -2907,7 +2913,7 @@ async def resolve_function_calls(
# Handle multiple function calls
calls = [
{"name": "search_memory", "arguments": {"query": "user preferences"}},
{"name": "get_working_memory", "arguments": {}},
{"name": "get_or_create_working_memory", "arguments": {}},
]

results = await client.resolve_function_calls(calls, "session123")
Expand Down Expand Up @@ -3392,6 +3398,9 @@ async def hydrate_memory_prompt(
user_id: dict[str, Any] | None = None,
distance_threshold: float | None = None,
memory_type: dict[str, Any] | None = None,
search_mode: SearchModeEnum | str = SearchModeEnum.SEMANTIC,
hybrid_alpha: float | None = None,
text_scorer: str | None = None,
limit: int = 10,
offset: int = 0,
optimize_query: bool = False,
Expand All @@ -3403,7 +3412,7 @@ async def hydrate_memory_prompt(
long-term memory search with the specified filters.

Args:
query: The query for vector search to find relevant context for
query: The search query to find relevant context for
session_id: Optional session ID filter (as dict)
namespace: Optional namespace filter (as dict)
topics: Optional topics filter (as dict)
Expand All @@ -3413,6 +3422,9 @@ async def hydrate_memory_prompt(
user_id: Optional user ID filter (as dict)
distance_threshold: Optional distance threshold
memory_type: Optional memory type filter (as dict)
search_mode: Search strategy to use ("semantic", "keyword", or "hybrid")
hybrid_alpha: Optional weight for vector similarity in hybrid search (0.0-1.0)
text_scorer: Optional Redis full-text scoring algorithm for keyword and hybrid search
limit: Maximum number of long-term memories to include
offset: Offset for pagination (default: 0)
optimize_query: Whether to optimize the query for vector search using a fast model (default: True)
Expand Down Expand Up @@ -3443,6 +3455,16 @@ async def hydrate_memory_prompt(
long_term_search["distance_threshold"] = distance_threshold
if memory_type is not None:
long_term_search["memory_type"] = memory_type
normalized_search_mode = (
search_mode.value
if isinstance(search_mode, SearchModeEnum)
else str(search_mode)
)
long_term_search["search_mode"] = normalized_search_mode
if hybrid_alpha is not None:
long_term_search["hybrid_alpha"] = hybrid_alpha
if text_scorer is not None:
long_term_search["text_scorer"] = text_scorer

return await self.memory_prompt(
query=query,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -127,7 +127,7 @@ def get_memory_tools(
tool_configs = {
"search_memory": {
"name": "search_memory",
"description": "Search long-term memory for relevant information using semantic search. Use this to recall past conversations, user preferences, or stored facts. Returns memories ranked by relevance with scores.",
"description": "Search long-term memory for relevant information using semantic, keyword, or hybrid search. Use this to recall past conversations, user preferences, or stored facts. Returns memories ranked by relevance with scores.",
"func": _create_search_memory_func(memory_client),
},
"get_or_create_working_memory": {
Expand Down Expand Up @@ -224,6 +224,9 @@ def _create_search_memory_func(client: MemoryAPIClient) -> Any:

async def search_memory(
query: str,
search_mode: Literal["semantic", "keyword", "hybrid"] = "semantic",
hybrid_alpha: float | None = None,
text_scorer: str | None = None,
topics: list[str] | None = None,
entities: list[str] | None = None,
memory_type: str | None = None,
Expand All @@ -234,6 +237,9 @@ async def search_memory(
"""Search long-term memory for relevant information."""
result = await client.search_memory_tool(
query=query,
search_mode=search_mode,
hybrid_alpha=hybrid_alpha,
text_scorer=text_scorer,
topics=topics,
entities=entities,
memory_type=memory_type,
Expand Down
15 changes: 12 additions & 3 deletions agent_memory_server/mcp.py
Original file line number Diff line number Diff line change
Expand Up @@ -643,12 +643,15 @@ async def memory_prompt(
user_id: UserId | None = None,
memory_type: MemoryType | None = None,
distance_threshold: float | None = None,
search_mode: SearchModeEnum = SearchModeEnum.SEMANTIC,
hybrid_alpha: float = 0.7,
text_scorer: str = "BM25STD",
limit: int = 10,
offset: int = 0,
optimize_query: bool = False,
) -> dict[str, Any]:
"""
Hydrate a query for vector search with relevant session history and long-term memories.
Hydrate a query with relevant session history and long-term memories.

This tool enriches the query by retrieving:
1. Context from the current conversation session
Expand All @@ -657,7 +660,7 @@ async def memory_prompt(
The tool returns both the relevant memories AND the user's query in a format ready for
generating comprehensive responses.

The function uses the query field as the query for vector search,
The function uses the query field for searching (semantic, keyword, or hybrid),
and any filters to retrieve relevant memories.

DATETIME INPUT FORMAT:
Expand Down Expand Up @@ -722,7 +725,7 @@ async def memory_prompt(
```

Args:
- query: The query for vector search
- query: The search query text
- session_id: Add conversation history from a working memory session
- namespace: Filter session and long-term memory namespace
- topics: Search for long-term memories matching topics
Expand All @@ -731,6 +734,9 @@ async def memory_prompt(
- last_accessed: Search for long-term memories matching last access date
- user_id: Search for long-term memories matching user ID
- distance_threshold: Distance threshold for semantic search
- search_mode: Search strategy to use (semantic, keyword, or hybrid)
- hybrid_alpha: Weight assigned to vector similarity in hybrid search (0.0-1.0)
- text_scorer: Redis full-text scoring algorithm for keyword and hybrid search
- limit: Maximum number of long-term memory results
- offset: Offset for pagination of long-term memory results
- optimize_query: Whether to optimize the query for vector search (default: False - LLMs typically provide already optimized queries)
Expand Down Expand Up @@ -768,6 +774,9 @@ async def memory_prompt(
user_id=user_id,
distance_threshold=distance_threshold,
memory_type=memory_type,
search_mode=search_mode,
hybrid_alpha=hybrid_alpha,
text_scorer=text_scorer,
limit=limit,
offset=offset,
)
Expand Down
2 changes: 1 addition & 1 deletion docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -75,7 +75,7 @@ For contributors and advanced users:

| Feature | REST API | MCP Server | CLI | Documentation |
|---------|----------|------------|-----|---------------|
| **Memory Search** | ✅ `/v1/long-term-memory/search` | ✅ `search_long_term_memory` | ❌ | [REST API](api.md), [MCP](mcp.md) |
| **Memory Search** (semantic, keyword, hybrid) | ✅ `/v1/long-term-memory/search` | ✅ `search_long_term_memory` | ❌ | [REST API](api.md), [MCP](mcp.md) |
| **Memory Editing** | ✅ `PATCH /v1/long-term-memory/{id}` | ✅ `edit_long_term_memory` | ❌ | [Memory Editing](memory-lifecycle.md#memory-editing) |
| **Query Optimization** | ✅ `optimize_query` param | ✅ `optimize_query` param | ❌ | [Query Optimization](query-optimization.md) |
| **Recency Boost** | ✅ Default enabled | ✅ Available | ❌ | [Recency Boost](recency-boost.md) |
Expand Down
4 changes: 2 additions & 2 deletions docs/agent-examples.md
Original file line number Diff line number Diff line change
Expand Up @@ -93,7 +93,7 @@ A comprehensive travel assistant that demonstrates the most complete integration
- **Automatic Tool Discovery**: Uses `MemoryAPIClient.get_all_memory_tool_schemas()` to automatically discover and integrate all available memory tools
- **Unified Tool Resolution**: Leverages `client.resolve_tool_call()` to handle all memory tool calls uniformly across different LLM providers
- **Working Memory Management**: Session-based conversation state and structured memory storage
- **Long-term Memory**: Persistent memory storage and semantic search capabilities
- **Long-term Memory**: Persistent memory storage with semantic, keyword, and hybrid search capabilities
- **Optional Web Search**: Cached web search using Tavily API with Redis caching

### Available Tools
Expand Down Expand Up @@ -402,7 +402,7 @@ from agent_memory_client import create_memory_client
client = await create_memory_client(base_url="http://localhost:8000")

# Get only the 3 most recent messages
memory = await client.get_working_memory(
created, memory = await client.get_or_create_working_memory(
session_id="my-session",
namespace="demo",
context_window_max=3
Expand Down
6 changes: 3 additions & 3 deletions docs/index.md
Original file line number Diff line number Diff line change
Expand Up @@ -38,7 +38,7 @@ Transform your AI agents from goldfish 🐠 into elephants 🐘 with Redis-power
Redis Agent Memory Server is a production-ready memory system for AI agents and applications that:

- **🧠 Remembers everything**: Stores conversation history, user preferences, and important facts across sessions
- **🔍 Finds relevant context**: Uses semantic search to surface the right information at the right time
- **🔍 Finds relevant context**: Uses semantic, keyword, and hybrid search to surface the right information at the right time
- **📈 Gets smarter over time**: Automatically extracts, organizes, and deduplicates memories from interactions
- **🔌 Works with any AI model**: REST API and MCP interfaces compatible with OpenAI, Anthropic, and others
- **🌐 Multi-provider support**: Use [100+ LLM providers](llm-providers.md) via LiteLLM (OpenAI, Anthropic, AWS Bedrock, Ollama, Azure, Gemini, and more)
Expand Down Expand Up @@ -95,11 +95,11 @@ print(f"Found: {results.memories[0].text}")

!!! success "Long-Term Memory (Persistent)"
- User preferences, facts, and important information
- Semantic search with vector embeddings
- Flexible search: semantic (vector embeddings), keyword (full-text), and hybrid (combined)
- Advanced filtering by time, topics, entities, users

### 🔍 Intelligent Search
- **Semantic similarity**: Find memories by meaning, not just keywords
- **Multiple search modes**: Semantic (vector similarity), keyword (full-text), and hybrid (combined) search
- **Advanced filters**: Search by user, session, time, topics, entities
- **Query optimization**: AI-powered query refinement for better results
- **Recency boost**: Time-aware ranking that surfaces relevant recent information
Expand Down
Loading
Loading