Files
Giles Odigwe 6f6ee61834 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>
2026-04-01 21:35:16 +00:00

126 lines
4.6 KiB
Python

# Copyright (c) Microsoft. All rights reserved.
import asyncio
import os
from contextlib import suppress
from typing import Any
from agent_framework import Agent, AgentSession, ContextProvider, SessionContext, SupportsChatGetResponse
from agent_framework.foundry import FoundryChatClient
from azure.identity import AzureCliCredential
from dotenv import load_dotenv
from pydantic import BaseModel
# Load environment variables from .env file
load_dotenv()
class UserInfo(BaseModel):
name: str | None = None
age: int | None = None
class UserInfoMemory(ContextProvider):
DEFAULT_SOURCE_ID = "user_info_memory"
def __init__(self, source_id: str = DEFAULT_SOURCE_ID, *, client: SupportsChatGetResponse, **kwargs: Any):
"""Create the memory.
If you pass in kwargs, they will be attempted to be used to create a UserInfo object.
"""
super().__init__(source_id)
self._chat_client = client
async def after_run(
self,
*,
agent: Any,
session: AgentSession | None,
context: SessionContext,
state: dict[str, Any],
) -> None:
"""Extract user information from messages after each agent call."""
# ensure you get all the messages you want to parse from, including the input in this case.
request_messages = context.get_messages(include_input=True, include_response=True)
# Check if we need to extract user info from user messages
user_messages = [msg for msg in request_messages if hasattr(msg, "role") and msg.role == "user"] # type: ignore
if (state["user_info"].name is None or state["user_info"].age is None) and user_messages:
with suppress(Exception):
# Use the chat client to extract structured information
result = await self._chat_client.get_response(
messages=request_messages, # type: ignore
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
with suppress(Exception):
extracted = result.value
if state["user_info"].name is None and extracted.name:
state["user_info"].name = extracted.name
if state["user_info"].age is None and extracted.age:
state["user_info"].age = extracted.age
async def before_run(
self,
*,
agent: Any,
session: AgentSession | None,
context: SessionContext,
state: dict[str, Any],
) -> None:
"""Provide user information context before each agent call."""
state.setdefault("user_info", UserInfo())
context.extend_instructions(
self.source_id,
"Ask the user for their name and politely decline to answer any questions until they provide it."
if state["user_info"].name is None
else f"The user's name is {state['user_info'].name}.",
)
context.extend_instructions(
self.source_id,
"Ask the user for their age and politely decline to answer any questions until they provide it."
if state["user_info"].age is None
else f"The user's age is {state['user_info'].age}.",
)
async def main():
client = FoundryChatClient(
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
model=os.environ["FOUNDRY_MODEL"],
credential=AzureCliCredential(),
)
context_name = UserInfoMemory.DEFAULT_SOURCE_ID
# Create the memory provider
memory_provider = UserInfoMemory(context_name, client=client)
# Create the agent with memory
async with Agent(
client=client,
instructions="You are a friendly assistant. Always address the user by their name.",
context_providers=[memory_provider],
) as agent:
# Create a new session for the conversation
session = agent.create_session()
for msg in ["Hello, what is the square root of 9?", "My name is RuaidhrĂ­", "I am 20 years old"]:
print(f"User: {msg}")
print(f"Assistant: {await agent.run(msg, session=session)}")
# Access the memory component and inspect the memories
print()
print(f"MEMORY - User Name: {session.state[context_name]['user_info'].name}")
print(f"MEMORY - User Age: {session.state[context_name]['user_info'].age}")
if __name__ == "__main__":
asyncio.run(main())