From 6f6ee61834b820cd85026ad3a0ada94235b9cfaa Mon Sep 17 00:00:00 2001 From: Giles Odigwe <79032838+giles17@users.noreply.github.com> Date: Wed, 1 Apr 2026 14:35:16 -0700 Subject: [PATCH] Python: Fix broken samples and add missing READMEs (#5038) * Python: Fix broken samples and add missing READMEs - simple_context_provider: move instructions kwarg into options dict - suspend_resume_session: use OpenAIChatCompletionClient for in-memory demo - foundry_chat_client_with_hosted_mcp: move store kwarg into options dict - Add README.md for context_providers and conversations sample folders Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix additional sample issues in context_providers - mem0_basic: send preferences query before sleep so Mem0 can learn them, print result from new session recall - mem0_sessions: add session for multi-turn conversation in agent-scoped example, remove user_id from agent-scoped provider (Mem0 API stores memories without user_id when agent_id is provided), use single message for storing preferences - redis_basics: print retrieved context messages instead of raw object - redis_sessions: add missing load_dotenv() call - redis_basics/redis_sessions: fix docstrings referencing wrong client type - azure_redis_conversation: replace duplicate copyright with load_dotenv() Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> * Python: Fix broken link in declarative README openai_responses_agent.py was renamed to openai_agent.py Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --------- Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com> --- .../02-agents/context_providers/README.md | 28 +++++++++++++++++++ .../context_providers/mem0/mem0_basic.py | 9 +++++- .../context_providers/mem0/mem0_sessions.py | 27 +++++------------- .../redis/azure_redis_conversation.py | 3 +- .../context_providers/redis/redis_basics.py | 2 +- .../context_providers/redis/redis_sessions.py | 6 ++-- .../simple_context_provider.py | 8 ++++-- .../samples/02-agents/conversations/README.md | 28 +++++++++++++++++++ .../conversations/suspend_resume_session.py | 3 +- .../samples/02-agents/declarative/README.md | 2 +- .../foundry_chat_client_with_hosted_mcp.py | 4 +-- 11 files changed, 88 insertions(+), 32 deletions(-) create mode 100644 python/samples/02-agents/context_providers/README.md create mode 100644 python/samples/02-agents/conversations/README.md diff --git a/python/samples/02-agents/context_providers/README.md b/python/samples/02-agents/context_providers/README.md new file mode 100644 index 0000000000..86137b344b --- /dev/null +++ b/python/samples/02-agents/context_providers/README.md @@ -0,0 +1,28 @@ +# Context Provider Samples + +These samples demonstrate how to use context providers to enrich agent conversations with external knowledge — from custom logic to Azure AI Search (RAG) and memory services. + +## Samples + +| File / Folder | Description | +|---------------|-------------| +| [`simple_context_provider.py`](simple_context_provider.py) | Implement a custom context provider by extending `BaseContextProvider` to extract and inject structured user information across turns. | +| [`azure_ai_foundry_memory.py`](azure_ai_foundry_memory.py) | Use `FoundryMemoryProvider` to add semantic memory — automatically retrieves, searches, and stores memories via Azure AI Foundry. | +| [`azure_ai_search/`](azure_ai_search/) | Retrieval Augmented Generation (RAG) with Azure AI Search in semantic and agentic modes. See its own [README](azure_ai_search/README.md). | +| [`mem0/`](mem0/) | Memory-powered context using the Mem0 integration (open-source and managed). See its own [README](mem0/README.md). | +| [`redis/`](redis/) | Redis-backed context providers for conversation memory and sessions. See its own [README](redis/README.md). | + +## Prerequisites + +**For `simple_context_provider.py`:** +- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint +- `FOUNDRY_MODEL`: Model deployment name +- Azure CLI authentication (`az login`) + +**For `azure_ai_foundry_memory.py`:** +- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint +- `FOUNDRY_MODEL`: Chat/responses model deployment name +- `AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME`: Embedding model deployment name (e.g., `text-embedding-ada-002`) +- Azure CLI authentication (`az login`) + +See each subfolder's README for provider-specific prerequisites. diff --git a/python/samples/02-agents/context_providers/mem0/mem0_basic.py b/python/samples/02-agents/context_providers/mem0/mem0_basic.py index 489e5bfcae..712d3b0491 100644 --- a/python/samples/02-agents/context_providers/mem0/mem0_basic.py +++ b/python/samples/02-agents/context_providers/mem0/mem0_basic.py @@ -57,12 +57,16 @@ async def main() -> None: # Now tell the agent the company code and the report format that you want to use # and it should be able to invoke the tool and return the report. query = "I always work with CNTS and I always want a detailed report format. Please remember and retrieve it." + print(f"User: {query}") + result = await agent.run(query) + print(f"Agent: {result}\n") + # Mem0 processes and indexes memories asynchronously. # Wait for memories to be indexed before querying in a new thread. # In production, consider implementing retry logic or using Mem0's # eventual consistency handling instead of a fixed delay. print("Waiting for memories to be processed...") - await asyncio.sleep(12) # Empirically determined delay for Mem0 indexing + await asyncio.sleep(15) # Empirically determined delay for Mem0 indexing print("\nRequest within a new session:") # Create a new session for the agent. # The new session has no context of the previous conversation. @@ -70,7 +74,10 @@ async def main() -> None: # Since we have the mem0 component in the session, the agent should be able to # retrieve the company report without asking for clarification, as it will # be able to remember the user preferences from Mem0 component. + query = "Please retrieve my company report" + print(f"User: {query}") result = await agent.run(query, session=session) + print(f"Agent: {result}") if __name__ == "__main__": diff --git a/python/samples/02-agents/context_providers/mem0/mem0_sessions.py b/python/samples/02-agents/context_providers/mem0/mem0_sessions.py index d402b34baf..2ba6113d15 100644 --- a/python/samples/02-agents/context_providers/mem0/mem0_sessions.py +++ b/python/samples/02-agents/context_providers/mem0/mem0_sessions.py @@ -71,8 +71,6 @@ async def example_agent_scoped_memory() -> None: print("2. Agent-Scoped Memory Example:") print("-" * 40) - user_id = "user123" - async with ( AzureCliCredential() as credential, Agent( @@ -83,34 +81,23 @@ async def example_agent_scoped_memory() -> None: context_providers=[ Mem0ContextProvider( source_id="mem0", - user_id=user_id, agent_id="scoped_assistant", ) ], ) as scoped_agent, ): - # Store some information - query = "Remember that for this conversation, I'm working on a Python project about data analysis." + query = ( + "Remember that I'm working on a Python project about data analysis " + "and I prefer using pandas and matplotlib." + ) print(f"User: {query}") result = await scoped_agent.run(query) print(f"Agent: {result}\n") - # Test memory retrieval - query = "What project am I working on?" - print(f"User: {query}") - result = await scoped_agent.run(query) - print(f"Agent: {result}\n") - - # Store more information - query = "Also remember that I prefer using pandas and matplotlib for this project." - print(f"User: {query}") - result = await scoped_agent.run(query) - print(f"Agent: {result}\n") - - # Test comprehensive memory retrieval + new_session = scoped_agent.create_session() query = "What do you know about my current project and preferences?" - print(f"User: {query}") - result = await scoped_agent.run(query) + print(f"User (new session): {query}") + result = await scoped_agent.run(query, session=new_session) print(f"Agent: {result}\n") diff --git a/python/samples/02-agents/context_providers/redis/azure_redis_conversation.py b/python/samples/02-agents/context_providers/redis/azure_redis_conversation.py index ee0757d907..a9e72d83d7 100644 --- a/python/samples/02-agents/context_providers/redis/azure_redis_conversation.py +++ b/python/samples/02-agents/context_providers/redis/azure_redis_conversation.py @@ -30,9 +30,10 @@ from agent_framework.foundry import FoundryChatClient from agent_framework.redis import RedisHistoryProvider from azure.identity import AzureCliCredential from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential +from dotenv import load_dotenv from redis.credentials import CredentialProvider -# Copyright (c) Microsoft. All rights reserved. +load_dotenv() class AzureCredentialProvider(CredentialProvider): diff --git a/python/samples/02-agents/context_providers/redis/redis_basics.py b/python/samples/02-agents/context_providers/redis/redis_basics.py index efe433e5d4..bf9e163a49 100644 --- a/python/samples/02-agents/context_providers/redis/redis_basics.py +++ b/python/samples/02-agents/context_providers/redis/redis_basics.py @@ -100,7 +100,7 @@ def search_flights(origin_airport_code: str, destination_airport_code: str, deta def create_chat_client() -> FoundryChatClient: - """Create an Azure OpenAI Responses client using a Foundry project endpoint.""" + """Create a FoundryChatClient using a Foundry project endpoint.""" return FoundryChatClient( project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], model=os.environ["FOUNDRY_MODEL"], diff --git a/python/samples/02-agents/context_providers/redis/redis_sessions.py b/python/samples/02-agents/context_providers/redis/redis_sessions.py index 7819894f7b..de1ef8e095 100644 --- a/python/samples/02-agents/context_providers/redis/redis_sessions.py +++ b/python/samples/02-agents/context_providers/redis/redis_sessions.py @@ -34,10 +34,12 @@ from agent_framework import Agent from agent_framework.foundry import FoundryChatClient from agent_framework.redis import RedisContextProvider from azure.identity import AzureCliCredential +from dotenv import load_dotenv from redisvl.extensions.cache.embeddings import EmbeddingsCache from redisvl.utils.vectorize import OpenAITextVectorizer -# Copyright (c) Microsoft. All rights reserved. +# Load environment variables from .env file +load_dotenv() # Default Redis URL for local Redis Stack. @@ -48,7 +50,7 @@ REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379") # Please set OPENAI_API_KEY to use the OpenAI vectorizer. # For chat responses, also set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL. def create_chat_client() -> FoundryChatClient: - """Create an Azure OpenAI Responses client using a Foundry project endpoint.""" + """Create a FoundryChatClient using a Foundry project endpoint.""" return FoundryChatClient( project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], model=os.environ["FOUNDRY_MODEL"], diff --git a/python/samples/02-agents/context_providers/simple_context_provider.py b/python/samples/02-agents/context_providers/simple_context_provider.py index 5f2a0f409a..dd8da8cbe6 100644 --- a/python/samples/02-agents/context_providers/simple_context_provider.py +++ b/python/samples/02-agents/context_providers/simple_context_provider.py @@ -50,9 +50,11 @@ class UserInfoMemory(ContextProvider): # Use the chat client to extract structured information result = await self._chat_client.get_response( messages=request_messages, # type: ignore - instructions="Extract the user's name and age from the message if present. " - "If not present return nulls.", - options={"response_format": UserInfo}, + options={ + "instructions": "Extract the user's name and age from the message if present. " + "If not present return nulls.", + "response_format": UserInfo, + }, ) # Update user info with extracted data diff --git a/python/samples/02-agents/conversations/README.md b/python/samples/02-agents/conversations/README.md new file mode 100644 index 0000000000..ab527890fa --- /dev/null +++ b/python/samples/02-agents/conversations/README.md @@ -0,0 +1,28 @@ +# Conversation & Session Management Samples + +These samples demonstrate different approaches to managing conversation history and session state in Agent Framework. + +## Samples + +| File | Description | +|------|-------------| +| [`suspend_resume_session.py`](suspend_resume_session.py) | Suspend and resume conversation sessions, comparing service-managed sessions (Azure AI Foundry) with in-memory sessions (OpenAI). | +| [`custom_history_provider.py`](custom_history_provider.py) | Implement a custom history provider by extending `BaseHistoryProvider`, enabling conversation persistence in your preferred storage backend. | +| [`redis_history_provider.py`](redis_history_provider.py) | Use Redis as a history provider for persistent conversation history storage across sessions. | + +## Prerequisites + +**For `suspend_resume_session.py`:** +- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint (service-managed session) +- `FOUNDRY_MODEL`: The Foundry model deployment name +- `OPENAI_API_KEY`: Your OpenAI API key (in-memory session) +- Azure CLI authentication (`az login`) + +**For `custom_history_provider.py`:** +- `OPENAI_API_KEY`: Your OpenAI API key + +**For `redis_history_provider.py`:** +- `OPENAI_API_KEY`: Your OpenAI API key +- A running Redis server — default URL is `redis://localhost:6379` + - Override via the `REDIS_URL` environment variable for remote or authenticated instances + - Quickstart with Docker: `docker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest` diff --git a/python/samples/02-agents/conversations/suspend_resume_session.py b/python/samples/02-agents/conversations/suspend_resume_session.py index a5e9c44248..457ffd9a6d 100644 --- a/python/samples/02-agents/conversations/suspend_resume_session.py +++ b/python/samples/02-agents/conversations/suspend_resume_session.py @@ -4,6 +4,7 @@ import asyncio from agent_framework import Agent, AgentSession from agent_framework.foundry import FoundryChatClient +from agent_framework.openai import OpenAIChatCompletionClient from azure.identity.aio import AzureCliCredential from dotenv import load_dotenv @@ -62,7 +63,7 @@ async def suspend_resume_in_memory_session() -> None: # OpenAI Chat Client is used as an example here, # other chat clients can be used as well. agent = Agent( - client=FoundryChatClient(), + client=OpenAIChatCompletionClient(), name="MemoryBot", instructions="You are a helpful assistant that remembers our conversation.", ) diff --git a/python/samples/02-agents/declarative/README.md b/python/samples/02-agents/declarative/README.md index 4bc4489dc2..8cb6eadcd8 100644 --- a/python/samples/02-agents/declarative/README.md +++ b/python/samples/02-agents/declarative/README.md @@ -68,7 +68,7 @@ Illustrates a basic agent using Azure OpenAI with structured responses. **Key concepts**: Azure OpenAI integration, credential management, structured outputs -### 5. **OpenAI Responses Agent** ([`openai_responses_agent.py`](./openai_responses_agent.py)) +### 5. **OpenAI Responses Agent** ([`openai_agent.py`](./openai_agent.py)) Demonstrates the simplest possible agent using OpenAI directly. diff --git a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_hosted_mcp.py b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_hosted_mcp.py index 214ff005e9..3089fd1856 100644 --- a/python/samples/02-agents/providers/foundry/foundry_chat_client_with_hosted_mcp.py +++ b/python/samples/02-agents/providers/foundry/foundry_chat_client_with_hosted_mcp.py @@ -51,7 +51,7 @@ async def handle_approvals_with_session(query: str, agent: "SupportsAgentRun", s """Here we let the session deal with the previous responses, and we just rerun with the approval.""" from agent_framework import Message - result = await agent.run(query, session=session, store=True) + result = await agent.run(query, session=session, options={"store": True}) while len(result.user_input_requests) > 0: new_input: list[Any] = [] for user_input_needed in result.user_input_requests: @@ -66,7 +66,7 @@ async def handle_approvals_with_session(query: str, agent: "SupportsAgentRun", s contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")], ) ) - result = await agent.run(new_input, session=session, store=True) + result = await agent.run(new_input, session=session, options={"store": True}) return result