Skip to content
Open
Show file tree
Hide file tree
Changes from all 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
18 changes: 5 additions & 13 deletions python/samples/02-agents/context_providers/mem0/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,7 @@ This folder contains examples demonstrating how to use the Mem0 context provider
| File | Description |
|------|-------------|
| [`mem0_basic.py`](mem0_basic.py) | Basic example of using Mem0 context provider to store and retrieve user preferences across different conversation threads. |
| [`mem0_sessions.py`](mem0_sessions.py) | Advanced example demonstrating different thread scoping strategies with Mem0. Covers global thread scope (memories shared across all operations), per-operation thread scope (memories isolated per thread), and multiple agents with different memory configurations for personal vs. work contexts. |
| [`mem0_sessions.py`](mem0_sessions.py) | Example demonstrating different memory scoping strategies with Mem0. Covers user-scoped memory (memories shared across all sessions for the same user), agent-scoped memory (memories isolated per agent), and multiple agents with different memory configurations for personal vs. work contexts. |
| [`mem0_oss.py`](mem0_oss.py) | Example of using the Mem0 Open Source self-hosted version as the context provider. Demonstrates setup and configuration for local deployment. |

## Prerequisites
Expand Down Expand Up @@ -40,16 +40,8 @@ Set the following environment variables:

### Memory Scoping

The Mem0 context provider supports different scoping strategies:
The Mem0 context provider supports scoping via identifiers:

- **Global Scope** (`scope_to_per_operation_thread_id=False`): Memories are shared across all conversation threads
- **Thread Scope** (`scope_to_per_operation_thread_id=True`): Memories are isolated per conversation thread

### Memory Association

Mem0 records can be associated with different identifiers:

- `user_id`: Associate memories with a specific user
- `agent_id`: Associate memories with a specific agent
- `thread_id`: Associate memories with a specific conversation thread
- `application_id`: Associate memories with an application context
- **User scope** (`user_id`): Associate memories with a specific user, shared across all sessions
- **Agent scope** (`agent_id`): Isolate memories per agent persona
- **Application scope** (`application_id`): Associate memories with an application context
75 changes: 34 additions & 41 deletions python/samples/02-agents/context_providers/mem0/mem0_sessions.py
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
# Copyright (c) Microsoft. All rights reserved.

import asyncio
import uuid

from agent_framework import tool
from agent_framework.azure import AzureAIAgentClient
Expand All @@ -27,51 +26,48 @@ def get_user_preferences(user_id: str) -> str:
return preferences.get(user_id, "No specific preferences found")


async def example_global_thread_scope() -> None:
"""Example 1: Global thread_id scope (memories shared across all operations)."""
print("1. Global Thread Scope Example:")
async def example_user_scoped_memory() -> None:
"""Example 1: User-scoped memory (memories shared across all sessions for the same user)."""
print("1. User-Scoped Memory Example:")
print("-" * 40)

global_thread_id = str(uuid.uuid4())
user_id = "user123"

async with (
AzureCliCredential() as credential,
AzureAIAgentClient(credential=credential).as_agent(
name="GlobalMemoryAssistant",
name="UserMemoryAssistant",
instructions="You are an assistant that remembers user preferences across conversations.",
tools=get_user_preferences,
context_providers=[
Mem0ContextProvider(
source_id="mem0",
user_id=user_id,
thread_id=global_thread_id,
scope_to_per_operation_thread_id=False, # Share memories across all sessions
)
],
) as global_agent,
) as user_agent,
):
# Store some preferences in the global scope
# Store some preferences
query = "Remember that I prefer technical responses with code examples when discussing programming."
print(f"User: {query}")
result = await global_agent.run(query)
result = await user_agent.run(query)
print(f"Agent: {result}\n")

# Create a new session - but memories should still be accessible due to global scope
new_session = global_agent.create_session()
# Create a new session - memories should still be accessible via user_id scoping
new_session = user_agent.create_session()
query = "What do you know about my preferences?"
print(f"User (new session): {query}")
result = await global_agent.run(query, session=new_session)
result = await user_agent.run(query, session=new_session)
print(f"Agent: {result}\n")


async def example_per_operation_thread_scope() -> None:
"""Example 2: Per-operation thread scope (memories isolated per session).
async def example_agent_scoped_memory() -> None:
"""Example 2: Agent-scoped memory (memories isolated per agent_id).

Note: When scope_to_per_operation_thread_id=True, the provider is bound to a single session
throughout its lifetime. Use the same session object for all operations with that provider.
Note: Use different agent_id values to isolate memories between different
agent personas, even when the user_id is the same.
"""
print("2. Per-Operation Thread Scope Example:")
print("2. Agent-Scoped Memory Example:")
print("-" * 40)

user_id = "user123"
Expand All @@ -80,48 +76,45 @@ async def example_per_operation_thread_scope() -> None:
AzureCliCredential() as credential,
AzureAIAgentClient(credential=credential).as_agent(
name="ScopedMemoryAssistant",
instructions="You are an assistant with thread-scoped memory.",
instructions="You are an assistant with agent-scoped memory.",
tools=get_user_preferences,
context_providers=[
Mem0ContextProvider(
source_id="mem0",
user_id=user_id,
scope_to_per_operation_thread_id=True, # Isolate memories per session
agent_id="scoped_assistant",
)
],
) as scoped_agent,
):
# Create a specific session for this scoped provider
dedicated_session = scoped_agent.create_session()

# Store some information in the dedicated session
# Store some information
query = "Remember that for this conversation, I'm working on a Python project about data analysis."
print(f"User (dedicated session): {query}")
result = await scoped_agent.run(query, session=dedicated_session)
print(f"User: {query}")
result = await scoped_agent.run(query)
print(f"Agent: {result}\n")

# Test memory retrieval in the same dedicated session
# Test memory retrieval
query = "What project am I working on?"
print(f"User (same dedicated session): {query}")
result = await scoped_agent.run(query, session=dedicated_session)
print(f"User: {query}")
result = await scoped_agent.run(query)
print(f"Agent: {result}\n")

# Store more information in the same session
# Store more information
query = "Also remember that I prefer using pandas and matplotlib for this project."
print(f"User (same dedicated session): {query}")
result = await scoped_agent.run(query, session=dedicated_session)
print(f"User: {query}")
result = await scoped_agent.run(query)
print(f"Agent: {result}\n")

# Test comprehensive memory retrieval
query = "What do you know about my current project and preferences?"
print(f"User (same dedicated session): {query}")
result = await scoped_agent.run(query, session=dedicated_session)
print(f"User: {query}")
result = await scoped_agent.run(query)
print(f"Agent: {result}\n")


async def example_multiple_agents() -> None:
"""Example 3: Multiple agents with different thread configurations."""
print("3. Multiple Agents with Different Thread Configurations:")
"""Example 3: Multiple agents with different memory configurations."""
print("3. Multiple Agents with Different Memory Configurations:")
print("-" * 40)

agent_id_1 = "agent_personal"
Expand Down Expand Up @@ -174,11 +167,11 @@ async def example_multiple_agents() -> None:


async def main() -> None:
"""Run all Mem0 thread management examples."""
print("=== Mem0 Thread Management Example ===\n")
"""Run all Mem0 memory management examples."""
print("=== Mem0 Memory Management Example ===\n")

await example_global_thread_scope()
await example_per_operation_thread_scope()
await example_user_scoped_memory()
await example_agent_scoped_memory()
await example_multiple_agents()


Expand Down
11 changes: 5 additions & 6 deletions python/samples/02-agents/context_providers/redis/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,7 @@ This folder contains an example demonstrating how to use the Redis context provi
| [`azure_redis_conversation.py`](azure_redis_conversation.py) | Demonstrates conversation persistence with RedisHistoryProvider and Azure Redis with Azure AD (Entra ID) authentication using credential provider. |
| [`redis_basics.py`](redis_basics.py) | Shows standalone provider usage and agent integration. Demonstrates writing messages to Redis, retrieving context via full‑text or hybrid vector search, and persisting preferences across threads. Also includes a simple tool example whose outputs are remembered. |
| [`redis_conversation.py`](redis_conversation.py) | Simple example showing conversation persistence with RedisContextProvider using traditional connection string authentication. |
| [`redis_sessions.py`](redis_sessions.py) | Demonstrates thread scoping. Includes: (1) global thread scope with a fixed `thread_id` shared across operations; (2) per‑operation thread scope where `scope_to_per_operation_thread_id=True` binds memory to a single thread for the provider's lifetime; and (3) multiple agents with isolated memory via different `agent_id` values. |
| [`redis_sessions.py`](redis_sessions.py) | Demonstrates memory scoping strategies. Includes: (1) global memory scope with `application_id`, `agent_id`, and `user_id` shared across operations; (2) agent-scoped memory with a custom vectorizer for hybrid search; and (3) multiple agents with isolated memory via different `agent_id` values. |


## Prerequisites
Expand Down Expand Up @@ -61,8 +61,7 @@ The provider supports both full‑text only and hybrid vector search:

- Set `vectorizer_choice` to `"openai"` or `"hf"` to enable embeddings and hybrid search.
- When using a vectorizer, also set `vector_field_name` (e.g., `"vector"`).
- Partition fields for scoping memory: `application_id`, `agent_id`, `user_id`, `thread_id`.
- Thread scoping: `scope_to_per_operation_thread_id=True` isolates memory per operation thread.
- Partition fields for scoping memory: `application_id`, `agent_id`, `user_id`.
- Index management: `index_name`, `overwrite_redis_index`, `drop_redis_index`.

## What the example does
Expand Down Expand Up @@ -104,8 +103,8 @@ You should see the agent responses and, when using embeddings, context retrieved

### Memory scoping

- Global scope: set `application_id`, `agent_id`, `user_id`, or `thread_id` on the provider to filter memory.
- Per‑operation thread scope: set `scope_to_per_operation_thread_id=True` to isolate memory to the current thread created by the framework.
- Global scope: set `application_id`, `agent_id`, or `user_id` on the provider to filter memory.
- Agent isolation: use different `agent_id` values to keep memories separated for different agent personas.

### Hybrid vector search (optional)

Expand All @@ -118,7 +117,7 @@ You should see the agent responses and, when using embeddings, context retrieved

## Troubleshooting

- Ensure at least one of `application_id`, `agent_id`, `user_id`, or `thread_id` is set; the provider requires a scope.
- Ensure at least one of `application_id`, `agent_id`, or `user_id` is set; the provider requires a scope.
- Verify `AZURE_AI_PROJECT_ENDPOINT` and `AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME` are set for the chat client.
- If using embeddings, verify `OPENAI_API_KEY` is set and reachable.
- Make sure Redis exposes RediSearch (Redis Stack image or managed service with search enabled).
44 changes: 22 additions & 22 deletions python/samples/02-agents/context_providers/redis/redis_sessions.py
Original file line number Diff line number Diff line change
@@ -1,17 +1,17 @@
# Copyright (c) Microsoft. All rights reserved.

"""Redis Context Provider: Thread scoping examples
"""Redis Context Provider: Memory scoping examples

This sample demonstrates how conversational memory can be scoped when using the
Redis context provider. It covers three scenarios:

1) Global thread scope
- Provide a fixed thread_id to share memories across operations/threads.
1) Global memory scope
- Use application_id, agent_id, and user_id to share memories across
all operations/sessions.

2) Per-operation thread scope
- Enable scope_to_per_operation_thread_id to bind the provider to a single
thread for the lifetime of that provider instance. Use the same thread
object for reads/writes with that provider.
2) Agent-scoped memory with custom vectorizer
- Use a custom vectorizer with the provider for hybrid vector search.
Memories are scoped by application_id, user_id, and agent_id.

3) Multiple agents with isolated memory
- Use different agent_id values to keep memories separated for different
Expand All @@ -23,7 +23,7 @@
- Optionally an OpenAI API key for the chat client in this demo

Run:
python redis_threads.py
python redis_sessions.py
"""

import asyncio
Expand Down Expand Up @@ -55,9 +55,9 @@ def create_chat_client() -> AzureOpenAIResponsesClient:
)


async def example_global_thread_scope() -> None:
"""Example 1: Global thread_id scope (memories shared across all operations)."""
print("1. Global Thread Scope Example:")
async def example_global_memory_scope() -> None:
"""Example 1: Global memory scope (memories shared across all operations)."""
print("1. Global Memory Scope Example:")
print("-" * 40)

client = create_chat_client()
Expand Down Expand Up @@ -98,13 +98,13 @@ async def example_global_thread_scope() -> None:
await provider.redis_index.delete()


async def example_per_operation_thread_scope() -> None:
"""Example 2: Per-operation thread scope (memories isolated per session).
async def example_agent_scoped_memory() -> None:
"""Example 2: Agent-scoped memory with custom vectorizer.

Note: When scope_to_per_operation_thread_id=True, the provider is bound to a single session
throughout its lifetime. Use the same session object for all operations with that provider.
Demonstrates using a custom OpenAI vectorizer for hybrid vector search.
Memories are scoped by application_id, user_id, and agent_id.
"""
print("2. Per-Operation Thread Scope Example:")
print("2. Agent-Scoped Memory with Vectorizer Example:")
print("-" * 40)

client = create_chat_client()
Expand All @@ -130,7 +130,7 @@ async def example_per_operation_thread_scope() -> None:

agent = client.as_agent(
name="ScopedMemoryAssistant",
instructions="You are an assistant with thread-scoped memory.",
instructions="You are an assistant with agent-scoped memory.",
context_providers=[provider],
)

Expand Down Expand Up @@ -166,8 +166,8 @@ async def example_per_operation_thread_scope() -> None:


async def example_multiple_agents() -> None:
"""Example 3: Multiple agents with different thread configurations (isolated via agent_id) but within 1 index."""
print("3. Multiple Agents with Different Thread Configurations:")
"""Example 3: Multiple agents with different memory configurations (isolated via agent_id) but within 1 index."""
print("3. Multiple Agents with Different Memory Configurations:")
print("-" * 40)

client = create_chat_client()
Expand Down Expand Up @@ -243,9 +243,9 @@ async def example_multiple_agents() -> None:


async def main() -> None:
print("=== Redis Thread Scoping Examples ===\n")
await example_global_thread_scope()
await example_per_operation_thread_scope()
print("=== Redis Memory Scoping Examples ===\n")
await example_global_memory_scope()
await example_agent_scoped_memory()
await example_multiple_agents()


Expand Down