From 9e2f3bea41ed747470fbc994963b6e40f0c6d274 Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Wed, 8 Oct 2025 16:13:55 -0700 Subject: [PATCH 01/10] Author Readme files for each library and integrate those into the long descriptions. Those descriptions should show up in PyPi. This is following thie guidance found on Python Packaging here: https://packaging.python.org/en/latest/guides/making-a-pypi-friendly-readme/ --- libraries/microsoft-agents-activity/readme.md | 113 +++ libraries/microsoft-agents-activity/setup.py | 8 +- .../readme.md | 227 +++++ .../setup.py | 5 + .../readme.md | 310 +++++++ .../setup.py | 8 +- .../readme.md | 355 ++++++++ .../microsoft-agents-hosting-aiohttp/setup.py | 6 + .../microsoft-agents-hosting-core/readme.md | 573 +++++++++++++ .../microsoft-agents-hosting-core/setup.py | 6 + .../microsoft-agents-hosting-teams/readme.md | 590 +++++++++++++ .../microsoft-agents-hosting-teams/setup.py | 5 + .../microsoft-agents-storage-blob/readme.md | 529 ++++++++++++ .../microsoft-agents-storage-cosmos/readme.md | 774 ++++++++++++++++++ .../microsoft-agents-storage-cosmos/setup.py | 5 + 15 files changed, 3512 insertions(+), 2 deletions(-) create mode 100644 libraries/microsoft-agents-activity/readme.md create mode 100644 libraries/microsoft-agents-authentication-msal/readme.md create mode 100644 libraries/microsoft-agents-copilotstudio-client/readme.md create mode 100644 libraries/microsoft-agents-hosting-aiohttp/readme.md create mode 100644 libraries/microsoft-agents-hosting-core/readme.md create mode 100644 libraries/microsoft-agents-hosting-teams/readme.md create mode 100644 libraries/microsoft-agents-storage-blob/readme.md create mode 100644 libraries/microsoft-agents-storage-cosmos/readme.md diff --git a/libraries/microsoft-agents-activity/readme.md b/libraries/microsoft-agents-activity/readme.md new file mode 100644 index 00000000..c30149c7 --- /dev/null +++ b/libraries/microsoft-agents-activity/readme.md @@ -0,0 +1,113 @@ +# Microsoft Agents Activity + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) + +Core types and schemas for building conversational AI agents that work across Microsoft 365 platforms like Teams, Copilot Studio, and Webchat. + +## What is this? + +This library provides the fundamental building blocks for agent communication - think of it as the "language" that agents use to talk to each other and to users. An Activity is basically a message, event, or action in a conversation. + +## Installation + +```bash +pip install microsoft-agents-activity +``` + +## Quick Start + +```python +from microsoft_agents.activity import Activity, ActivityTypes + +# Send a simple message +activity = Activity( + type=ActivityTypes.message, + text="Hello! How can I help you today?" +) + +# Create a reply +reply = activity.create_reply("Thanks for reaching out!") + +# Show typing indicator +typing = Activity.create_typing_activity() +``` + +## Common Use Cases + +### Rich Messages with Cards + +```python +from microsoft_agents.activity import HeroCard, CardAction, Attachment + +# Create a welcome card +card = HeroCard( + title="Welcome to our service!", + subtitle="Let's get you started", + text="Choose an option below to continue", + actions=[ + CardAction(type="imBack", title="Get Help", value="help"), + CardAction(type="imBack", title="View Profile", value="profile") + ] +) + +activity = Activity( + type=ActivityTypes.message, + text="Welcome!", + attachments=[Attachment( + content_type="application/vnd.microsoft.card.hero", + content=card + )] +) +``` + +### Handle @Mentions + +```python +from microsoft_agents.activity import Mention, ChannelAccount + +# Check for mentions in incoming messages +mentions = activity.get_mentions() +for mention in mentions: + mentioned_user = mention.mentioned + print(f"User {mentioned_user.name} was mentioned") +``` + +### Teams Integration + +```python +from microsoft_agents.activity.teams import TeamsChannelAccount + +# Work with Teams-specific user info +teams_user = TeamsChannelAccount( + id="user123", + name="John Doe", + user_principal_name="john.doe@company.com" +) +``` + +## Activity Types + +The library supports different types of communication: + +- **Message** - Regular chat messages with text, cards, attachments +- **Typing** - Show typing indicators +- **ConversationUpdate** - People joining/leaving chats +- **Event** - Custom events and notifications +- **Invoke** - Direct function calls +- **EndOfConversation** - End chat sessions + +## Key Features + +✅ **Type-safe** - Built with Pydantic for automatic validation +✅ **Rich content** - Support for cards, images, videos, and interactive elements +✅ **Teams ready** - Full Microsoft Teams integration +✅ **Cross-platform** - Works across all Microsoft 365 chat platforms +✅ **Migration friendly** - Easy upgrade from Bot Framework + +## Need Help? + +- 📖 [Full SDK Documentation](https://aka.ms/agents) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) +- 💬 [Ask Questions](https://stackoverflow.com/questions/tagged/microsoft-agents) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file diff --git a/libraries/microsoft-agents-activity/setup.py b/libraries/microsoft-agents-activity/setup.py index 9b6de9af..fdc62fbe 100644 --- a/libraries/microsoft-agents-activity/setup.py +++ b/libraries/microsoft-agents-activity/setup.py @@ -1,8 +1,14 @@ from os import environ from setuptools import setup +from pathlib import Path + +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, -) + long_description=long_description, + long_description_content_type='text/markdown' +) \ No newline at end of file diff --git a/libraries/microsoft-agents-authentication-msal/readme.md b/libraries/microsoft-agents-authentication-msal/readme.md new file mode 100644 index 00000000..5d585e1d --- /dev/null +++ b/libraries/microsoft-agents-authentication-msal/readme.md @@ -0,0 +1,227 @@ +# Microsoft Agents MSAL Authentication + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) + +MSAL-based authentication library for Microsoft 365 Agents SDK. Handles Azure AD authentication with support for client secrets, certificates, and managed identities. + +## What is this? + +This library provides secure authentication for your agents using Microsoft Authentication Library (MSAL). It handles getting tokens from Azure AD so your agent can securely communicate with Microsoft services like Teams, Graph API, and other Azure resources. + +## Installation + +```bash +pip install microsoft-agents-authentication-msal +``` + +## Quick Start + +### Basic Setup with Client Secret + +```python +from microsoft_agents.authentication.msal import MsalAuth +from microsoft_agents.hosting.core import AgentAuthConfiguration, AuthTypes + +# Configure authentication +config = AgentAuthConfiguration( + AUTH_TYPE=AuthTypes.client_secret, + TENANT_ID="your-tenant-id", + CLIENT_ID="your-client-id", + CLIENT_SECRET="your-client-secret" +) + +# Create auth provider +auth = MsalAuth(config) + +# Get access token +token = await auth.get_access_token( + resource_url="https://graph.microsoft.com", + scopes=["https://graph.microsoft.com/.default"] +) +``` + +### Using with Connection Manager + +```python +from microsoft_agents.authentication.msal import MsalConnectionManager + +# Load from environment variables +connection_manager = MsalConnectionManager(**agents_sdk_config) + +# Use with hosting adapter +from microsoft_agents.hosting.aiohttp import CloudAdapter +adapter = CloudAdapter(connection_manager=connection_manager) +``` + +## Authentication Types + +### 1. Client Secret (Most Common) +```python +config = AgentAuthConfiguration( + AUTH_TYPE=AuthTypes.client_secret, + TENANT_ID="your-tenant-id", + CLIENT_ID="your-app-id", + CLIENT_SECRET="your-secret" +) +``` + +### 2. Certificate-based +```python +config = AgentAuthConfiguration( + AUTH_TYPE=AuthTypes.client_certificate, + TENANT_ID="your-tenant-id", + CLIENT_ID="your-app-id", + CLIENT_CERTIFICATE_PATH="/path/to/cert.pem", + CLIENT_CERTIFICATE_PRIVATE_KEY_PATH="/path/to/key.pem" +) +``` + +### 3. Managed Identity (Azure hosting) +```python +# System-assigned managed identity +config = AgentAuthConfiguration( + AUTH_TYPE=AuthTypes.system_managed_identity +) + +# User-assigned managed identity +config = AgentAuthConfiguration( + AUTH_TYPE=AuthTypes.user_managed_identity, + CLIENT_ID="managed-identity-client-id" +) +``` + +## Environment Configuration + +Set up your `.env` file: + +```bash +# Basic authentication +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=your-tenant-id +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=your-client-id +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=your-client-secret + +# Multiple connections +CONNECTIONS__GRAPH__SETTINGS__TENANTID=your-tenant-id +CONNECTIONS__GRAPH__SETTINGS__CLIENTID=your-graph-app-id +CONNECTIONS__GRAPH__SETTINGS__CLIENTSECRET=your-graph-secret +``` + +Load configuration: +```python +from microsoft_agents.activity import load_configuration_from_env +from os import environ + +agents_sdk_config = load_configuration_from_env(environ) +connection_manager = MsalConnectionManager(**agents_sdk_config) +``` + +## Advanced Features + +### On-Behalf-Of (OBO) Flow +```python +# Get token on behalf of user +user_token = await auth.acquire_token_on_behalf_of( + scopes=["https://graph.microsoft.com/User.Read"], + user_assertion="user-jwt-token" +) +``` + +### Agentic Authentication +```python +# For multi-agent scenarios +app_token = await auth.get_agentic_application_token("agent-instance-id") +user_token = await auth.get_agentic_user_token( + "agent-instance-id", + "user@company.com", + ["User.Read"] +) +``` + +### Connection Mapping +```python +# Map different connections to different services +connection_manager = MsalConnectionManager( + connections_map=[ + {"CONNECTION": "GRAPH", "SERVICEURL": "graph.microsoft.com"}, + {"CONNECTION": "TEAMS", "SERVICEURL": "teams.microsoft.com"} + ] +) +``` + +## Common Use Cases + +### Teams Agent +```python +from microsoft_agents.hosting.aiohttp import CloudAdapter + +CONFIG = AgentAuthConfiguration( + AUTH_TYPE=AuthTypes.client_secret, + TENANT_ID=environ.get("TENANT_ID"), + CLIENT_ID=environ.get("CLIENT_ID"), + CLIENT_SECRET=environ.get("CLIENT_SECRET") +) + +connection_manager = MsalConnectionManager( + connections_configurations={"SERVICE_CONNECTION": CONFIG} +) + +adapter = CloudAdapter(connection_manager=connection_manager) +``` + +### Graph API Access +```python +# Configure for Microsoft Graph +graph_config = AgentAuthConfiguration( + AUTH_TYPE=AuthTypes.client_secret, + TENANT_ID="your-tenant-id", + CLIENT_ID="your-app-id", + CLIENT_SECRET="your-secret", + SCOPES=["https://graph.microsoft.com/.default"] +) + +auth = MsalAuth(graph_config) +token = await auth.get_access_token("https://graph.microsoft.com") +``` + +## Key Classes + +- **`MsalAuth`** - Core authentication provider using MSAL +- **`MsalConnectionManager`** - Manages multiple authentication connections +- **`AgentAuthConfiguration`** - Configuration settings for auth providers + +## Features + +✅ **Multiple auth types** - Client secret, certificate, managed identity +✅ **Token caching** - Automatic token refresh and caching +✅ **Multi-tenant** - Support for different Azure AD tenants +✅ **Agent-to-agent** - Secure communication between agents +✅ **On-behalf-of** - Act on behalf of users +✅ **Scope resolution** - Dynamic scope handling with placeholders + +## Troubleshooting + +### Common Issues + +**Token acquisition failed** +- Verify your client ID, secret, and tenant ID +- Check that your app has the required permissions +- Ensure scopes are correctly formatted + +**Managed Identity not working** +- Verify you're running on Azure with managed identity enabled +- Check that the identity has required permissions + +## Security Best Practices + +- Store secrets in Azure Key Vault or environment variables +- Use managed identities when possible (no secrets to manage) +- Regularly rotate client secrets and certificates +- Use least-privilege principle for scopes and permissions + +## Need Help? + +- 📖 [Full SDK Documentation](https://aka.ms/agents) +- 🔐 [Azure AD App Registration Guide](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file diff --git a/libraries/microsoft-agents-authentication-msal/setup.py b/libraries/microsoft-agents-authentication-msal/setup.py index 67315bcf..63a68f83 100644 --- a/libraries/microsoft-agents-authentication-msal/setup.py +++ b/libraries/microsoft-agents-authentication-msal/setup.py @@ -1,10 +1,15 @@ from os import environ from setuptools import setup +from pathlib import Path +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, + long_description=long_description, + long_description_content_type='text/markdown', install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "msal>=1.31.1", diff --git a/libraries/microsoft-agents-copilotstudio-client/readme.md b/libraries/microsoft-agents-copilotstudio-client/readme.md new file mode 100644 index 00000000..9a0c4806 --- /dev/null +++ b/libraries/microsoft-agents-copilotstudio-client/readme.md @@ -0,0 +1,310 @@ +# Microsoft Agents Copilot Studio Client + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) + +The Copilot Studio Client is for connecting to and interacting with agents created in Microsoft Copilot Studio. This library allows you to integrate Copilot Studio agents into your Python applications. + +## What is this? + +This client library provides a direct connection to Copilot Studio agents, bypassing traditional chat channels. It's perfect for integrating AI conversations into your applications, building custom UIs, or creating agent-to-agent communication flows. + +## Installation + +```bash +pip install microsoft-agents-copilotstudio-client +``` + +## Quick Start + +### Basic Setup + +```python +from microsoft_agents.copilotstudio.client import ( + CopilotClient, + ConnectionSettings, + PowerPlatformCloud, + AgentType +) + +# Configure connection to your Copilot Studio agent +settings = ConnectionSettings( + environment_id="your-environment-id", + agent_identifier="your-agent-id", + cloud=PowerPlatformCloud.PROD, + copilot_agent_type=AgentType.PUBLISHED +) + +# Create client with access token +client = CopilotClient(settings, "your-access-token") +``` + +### Start a Conversation + +```python +import asyncio + +async def chat_with_agent(): + # Initialize conversation + async for activity in client.start_conversation(): + if activity.type == "message": + print(f"Agent: {activity.text}") + + # Send a question + async for activity in client.ask_question("Hello, how can you help me?"): + if activity.type == "message": + print(f"Agent: {activity.text}") + +# Run the conversation +asyncio.run(chat_with_agent()) +``` + +## Configuration Options + +### Connection Settings + +```python +settings = ConnectionSettings( + environment_id="12345678-1234-1234-1234-123456789012", # Required + agent_identifier="your-agent-name", # Required + cloud=PowerPlatformCloud.PROD, # Environment + copilot_agent_type=AgentType.PUBLISHED # Agent version +) +``` + +### Power Platform Clouds + +Choose the appropriate cloud environment: + +```python +from microsoft_agents.copilotstudio.client import PowerPlatformCloud + +# Production (default) +cloud = PowerPlatformCloud.PROD + +# Government clouds +cloud = PowerPlatformCloud.GOV +cloud = PowerPlatformCloud.HIGH +cloud = PowerPlatformCloud.DOD + +# Testing environments +cloud = PowerPlatformCloud.DEV +cloud = PowerPlatformCloud.TEST + +# Custom cloud +settings = ConnectionSettings( + environment_id="your-env-id", + agent_identifier="your-agent-id", + cloud=PowerPlatformCloud.OTHER, + custom_power_platform_cloud="your-custom-cloud.powerplatform.com" +) +``` + +### Agent Types + +```python +from microsoft_agents.copilotstudio.client import AgentType + +# Published agent (default) +agent_type = AgentType.PUBLISHED + +# Prebuilt agent +agent_type = AgentType.PREBUILT +``` + +## Authentication + +### Get Access Token + +You need a valid access token for the Power Platform API: + +```python +from msal import PublicClientApplication + +# MSAL configuration +app = PublicClientApplication( + client_id="your-app-client-id", + authority="https://login.microsoftonline.com/your-tenant-id" +) + +# Get token +result = app.acquire_token_interactive( + scopes=["https://api.powerplatform.com/.default"] +) + +if "access_token" in result: + token = result["access_token"] + client = CopilotClient(settings, token) +``` + +### Environment Variables + +Set up your `.env` file: + +```bash +# Required +ENVIRONMENT_ID=your-power-platform-environment-id +AGENT_IDENTIFIER=your-copilot-studio-agent-id +APP_CLIENT_ID=your-azure-app-client-id +TENANT_ID=your-azure-tenant-id + +# Optional +CLOUD=PROD +COPILOT_AGENT_TYPE=PUBLISHED +CUSTOM_POWER_PLATFORM_CLOUD=your-custom-cloud.com +``` + +## Advanced Usage + +### Send Custom Activities + +```python +from microsoft_agents.activity import Activity, ActivityTypes, ConversationAccount + +# Create custom activity +activity = Activity( + type=ActivityTypes.message, + text="Custom message with metadata", + conversation=ConversationAccount(id="conversation-123"), + value={"customData": "example"} +) + +# Send to agent +async for response in client.ask_question_with_activity(activity): + print(f"Response: {response.text}") +``` + +### Handle Different Activity Types + +```python +async def handle_conversation(): + async for activity in client.start_conversation(): + if activity.type == "message": + print(f"Message: {activity.text}") + + # Check for suggested actions + if activity.suggested_actions: + print("Available actions:") + for action in activity.suggested_actions.actions: + print(f" - {action.title}") + + elif activity.type == "typing": + print("Agent is typing...") + + elif activity.type == "event": + print(f"Event: {activity.name}") +``` + +### Conversation Management + +```python +class ConversationManager: + def __init__(self, client: CopilotClient): + self.client = client + self.conversation_id = None + + async def start_new_conversation(self): + async for activity in self.client.start_conversation(): + if activity.conversation: + self.conversation_id = activity.conversation.id + yield activity + + async def send_message(self, text: str): + async for activity in self.client.ask_question(text, self.conversation_id): + yield activity +``` + +## Integration with Microsoft 365 Agents SDK + +```python +from microsoft_agents.hosting.core import TurnContext, MessageFactory +from microsoft_agents.authentication.msal import MsalAuth + +# In your agent handler +async def handle_copilot_studio_query(context: TurnContext): + # Get OBO token + auth = MsalAuth(your_auth_config) + token = await auth.acquire_token_on_behalf_of( + scopes=["https://api.powerplatform.com/.default"], + user_assertion="user-token" + ) + + # Create Copilot Studio client + client = CopilotClient(settings, token) + + # Forward user message to Copilot Studio + user_message = context.activity.text + async for activity in client.ask_question(user_message): + await context.send_activity(MessageFactory.text(activity.text)) +``` + +## Console Chat Example + +```python +async def console_chat(): + # Start conversation + print("Starting conversation with Copilot Studio agent...") + async for activity in client.start_conversation(): + if activity.type == "message": + print(f"Agent: {activity.text}") + + # Chat loop + while True: + user_input = input("You: ") + if user_input.lower() in ['quit', 'exit']: + break + + async for activity in client.ask_question(user_input): + if activity.type == "message": + print(f"Agent: {activity.text}") +``` + +## Key Classes + +- **`CopilotClient`** - Main client for communicating with Copilot Studio agents +- **`ConnectionSettings`** - Configuration for connecting to your agent +- **`PowerPlatformCloud`** - Enum for different Power Platform environments +- **`AgentType`** - Enum for agent version types (published/prebuilt) + +## Features + +✅ **Real-time streaming** - Server-sent events for live responses +✅ **Multi-cloud support** - Works across all Power Platform clouds +✅ **Rich content** - Support for cards, actions, and attachments +✅ **Conversation management** - Maintain context across interactions +✅ **Custom activities** - Send structured data to agents +✅ **Async/await** - Modern Python async support + +## Troubleshooting + +### Common Issues + +**Authentication failed** +- Verify your app is registered in Azure AD +- Check that token has `https://api.powerplatform.com/.default` scope +- Ensure your app has permissions to the Power Platform environment + +**Agent not found** +- Verify the environment ID and agent identifier +- Check that the agent is published and accessible +- Confirm you're using the correct cloud setting + +**Connection timeout** +- Check network connectivity to Power Platform +- Verify firewall settings allow HTTPS traffic +- Try a different cloud region if available + +## Requirements + +- Python 3.9+ +- Valid Azure AD app registration +- Access to Microsoft Power Platform environment +- Published Copilot Studio agent + +## Need Help? + +- 📖 [Copilot Studio Documentation](https://docs.microsoft.com/power-virtual-agents/) +- 🔧 [Power Platform Admin Center](https://admin.powerplatform.microsoft.com/) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) +- 💬 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples/copilot_studio_client_sample) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file diff --git a/libraries/microsoft-agents-copilotstudio-client/setup.py b/libraries/microsoft-agents-copilotstudio-client/setup.py index 41a7da0d..93fb6897 100644 --- a/libraries/microsoft-agents-copilotstudio-client/setup.py +++ b/libraries/microsoft-agents-copilotstudio-client/setup.py @@ -1,5 +1,9 @@ from os import environ from setuptools import setup +from pathlib import Path + +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") @@ -7,5 +11,7 @@ version=package_version, install_requires=[ f"microsoft-agents-hosting-core=={package_version}", - ], + ], + long_description=long_description, + long_description_content_type='text/markdown', ) diff --git a/libraries/microsoft-agents-hosting-aiohttp/readme.md b/libraries/microsoft-agents-hosting-aiohttp/readme.md new file mode 100644 index 00000000..7ddb5d75 --- /dev/null +++ b/libraries/microsoft-agents-hosting-aiohttp/readme.md @@ -0,0 +1,355 @@ +# Microsoft Agents Hosting - aiohttp + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) + +Integration library for hosting Microsoft 365 Agents using aiohttp. This library provides HTTP adapters, middleware, and utilities for building web-based agent applications with the popular aiohttp framework. + +## What is this? + +This library bridges the Microsoft 365 Agents SDK with aiohttp, allowing you to create HTTP endpoints that handle agent conversations. It provides everything you need to host agents as web services, including request processing, authentication, and routing. + +## Installation + +```bash +pip install microsoft-agents-hosting-aiohttp +``` + +## Quick Start + +### Basic Agent Server + +```python +from aiohttp.web import Application, Request, Response, run_app +from microsoft_agents.hosting.aiohttp import ( + CloudAdapter, + jwt_authorization_middleware, + start_agent_process +) +from microsoft_agents.hosting.core import AgentApplication, TurnState, MemoryStorage +from microsoft_agents.authentication.msal import MsalConnectionManager + +# Create your agent application +storage = MemoryStorage() +connection_manager = MsalConnectionManager(**config) +adapter = CloudAdapter(connection_manager=connection_manager) +agent_app = AgentApplication[TurnState]( + storage=storage, + adapter=adapter, + **config +) + +# Set up message handler +async def messages(req: Request) -> Response: + return await start_agent_process(req, agent_app, adapter) + +# Create aiohttp application +app = Application(middlewares=[jwt_authorization_middleware]) +app.router.add_post("/api/messages", messages) +app["agent_configuration"] = auth_config +app["agent_app"] = agent_app +app["adapter"] = adapter + +# Run the server +run_app(app, host="localhost", port=3978) +``` + +### Simple Echo Agent + +```python +from microsoft_agents.hosting.core import AgentApplication, TurnState, TurnContext +from microsoft_agents.hosting.aiohttp import CloudAdapter + +# Create minimal agent +agent_app = AgentApplication[TurnState]( + storage=MemoryStorage(), + adapter=CloudAdapter() +) + +@agent_app.activity("message") +async def on_message(context: TurnContext, state: TurnState): + await context.send_activity(f"You said: {context.activity.text}") + +# Use the shared start_server helper +from shared import start_server +start_server(agent_app, auth_configuration=None) +``` + +## Core Components + +### CloudAdapter + +The main adapter for processing HTTP requests and converting them to agent activities: + +```python +from microsoft_agents.hosting.aiohttp import CloudAdapter +from microsoft_agents.authentication.msal import MsalConnectionManager + +# Basic setup +adapter = CloudAdapter(connection_manager=connection_manager) + +# With custom error handling +async def custom_error_handler(context, error): + print(f"Error: {error}") + await context.send_activity("Sorry, something went wrong.") + +adapter.on_turn_error = custom_error_handler + +# Process incoming requests +async def handle_messages(request: Request) -> Response: + return await adapter.process(request, your_agent) +``` + +### JWT Authorization Middleware + +Automatic JWT token validation for secure agent endpoints: + +```python +from microsoft_agents.hosting.aiohttp import ( + jwt_authorization_middleware, + jwt_authorization_decorator +) + +# As middleware (recommended) +app = Application(middlewares=[jwt_authorization_middleware]) + +# As decorator for specific routes +@jwt_authorization_decorator +async def protected_endpoint(request): + claims = request["claims_identity"] + return Response(text=f"Hello {claims.name}") +``` + +### Channel Service Routing + +For agent-to-agent communication: + +```python +from microsoft_agents.hosting.aiohttp import channel_service_route_table + +# Add agent-to-agent routes +app.router.add_routes( + channel_service_route_table(your_agent, "/api/botresponse") +) +``` + +## Authentication Setup + +### Environment Configuration + +```bash +# Required for authentication +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=your-tenant-id +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=your-client-id +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=your-client-secret +``` + +### Full Setup with Authentication + +```python +import os +from dotenv import load_dotenv +from microsoft_agents.activity import load_configuration_from_env +from microsoft_agents.authentication.msal import MsalConnectionManager +from microsoft_agents.hosting.aiohttp import CloudAdapter +from microsoft_agents.hosting.core import AgentApplication, Authorization + +load_dotenv() +config = load_configuration_from_env(os.environ) + +# Set up authentication and storage +storage = MemoryStorage() +connection_manager = MsalConnectionManager(**config) +adapter = CloudAdapter(connection_manager=connection_manager) +authorization = Authorization(storage, connection_manager, **config) + +# Create authenticated agent application +agent_app = AgentApplication[TurnState]( + storage=storage, + adapter=adapter, + authorization=authorization, + **config +) +``` + +## Advanced Features + +### Streaming Responses + +Support for real-time streaming responses: + +```python +from microsoft_agents.hosting.aiohttp import StreamingResponse, Citation + +# Create streaming response with citations +citations = [Citation(content="Source info", title="Reference")] +response = StreamingResponse(citations=citations) + +# Use in your agent logic +async def stream_response(context: TurnContext): + # Stream data to user in real-time + await context.send_activity("Starting stream...") + # Implementation depends on your streaming needs +``` + +### Custom Middleware + +Add your own middleware for logging, analytics, etc: + +```python +from aiohttp.web import middleware + +@middleware +async def logging_middleware(request, handler): + print(f"Incoming request: {request.method} {request.path}") + response = await handler(request) + print(f"Response status: {response.status}") + return response + +app = Application(middlewares=[ + logging_middleware, + jwt_authorization_middleware +]) +``` + +### Error Handling + +Customize error responses: + +```python +from microsoft_agents.hosting.core import MessageFactory + +async def custom_error_handler(context: TurnContext, error: Exception): + if isinstance(error, PermissionError): + await context.send_activity( + MessageFactory.text("You don't have permission for this action") + ) + else: + await context.send_activity( + MessageFactory.text("An unexpected error occurred") + ) + + # Send trace for debugging + await context.send_trace_activity( + "Error", str(error), "error", "OnTurnError" + ) + +adapter.on_turn_error = custom_error_handler +``` + +## Integration Patterns + +### Teams Integration + +```python +from microsoft_agents.hosting.teams import TeamsActivityHandler + +class MyTeamsAgent(TeamsActivityHandler): + async def on_message_activity(self, turn_context: TurnContext): + await turn_context.send_activity("Hello from Teams!") + +# Use with CloudAdapter +agent = MyTeamsAgent() +adapter = CloudAdapter(connection_manager=connection_manager) +``` + +### Multi-Agent Systems + +```python +# Agent 1 - Initiator +app.router.add_routes( + channel_service_route_table(agent1, "/api/agent1") +) + +# Agent 2 - Responder +app.router.add_routes( + channel_service_route_table(agent2, "/api/agent2") +) + +# Configure agent communication +from microsoft_agents.hosting.core import HttpAgentChannelFactory + +channel_factory = HttpAgentChannelFactory() +# Set up agent discovery and routing +``` + +### Development vs Production + +```python +# Development - simpler setup +if os.getenv("ENVIRONMENT") == "development": + adapter = CloudAdapter() # Anonymous mode +else: + # Production - full authentication + adapter = CloudAdapter(connection_manager=connection_manager) +``` + +## Testing Your Agent + +```python +import aiohttp +import asyncio + +async def test_agent(): + async with aiohttp.ClientSession() as session: + # Test message + activity = { + "type": "message", + "text": "Hello agent!", + "conversation": {"id": "test-conversation"}, + "from": {"id": "test-user"}, + "recipient": {"id": "test-agent"} + } + + async with session.post( + "http://localhost:3978/api/messages", + json=activity, + headers={"Content-Type": "application/json"} + ) as response: + print(f"Status: {response.status}") + if response.status == 200: + result = await response.json() + print(f"Response: {result}") + +# Run test +asyncio.run(test_agent()) +``` + +## Key Classes + +- **`CloudAdapter`** - Main HTTP adapter for processing agent requests +- **`start_agent_process`** - Helper function to start agent processing +- **`jwt_authorization_middleware`** - JWT authentication middleware +- **`channel_service_route_table`** - Routes for agent-to-agent communication +- **`StreamingResponse`** - Support for streaming responses + +## Features + +✅ **HTTP hosting** - Full aiohttp integration for web hosting +✅ **JWT authentication** - Built-in security with middleware +✅ **Agent-to-agent** - Support for multi-agent communication +✅ **Streaming** - Real-time response streaming +✅ **Error handling** - Comprehensive error management +✅ **Development friendly** - Hot reload and debugging support + +## Requirements + +- Python 3.9+ +- aiohttp 3.11.11+ +- Microsoft Agents hosting core library + +## Best Practices + +1. **Use middleware** for cross-cutting concerns like auth and logging +2. **Handle errors gracefully** with custom error handlers +3. **Secure your endpoints** with JWT middleware in production +4. **Structure routes** logically for agent communication +5. **Test thoroughly** with both unit and integration tests + +## Need Help? + +- 📖 [Full SDK Documentation](https://aka.ms/agents) +- 🌐 [aiohttp Documentation](https://docs.aiohttp.org/) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) +- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-aiohttp/setup.py b/libraries/microsoft-agents-hosting-aiohttp/setup.py index 426735ba..bdb3a65c 100644 --- a/libraries/microsoft-agents-hosting-aiohttp/setup.py +++ b/libraries/microsoft-agents-hosting-aiohttp/setup.py @@ -1,10 +1,16 @@ from os import environ from setuptools import setup +from pathlib import Path + +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, + long_description=long_description, + long_description_content_type='text/markdown', install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "aiohttp>=3.11.11", diff --git a/libraries/microsoft-agents-hosting-core/readme.md b/libraries/microsoft-agents-hosting-core/readme.md new file mode 100644 index 00000000..96214294 --- /dev/null +++ b/libraries/microsoft-agents-hosting-core/readme.md @@ -0,0 +1,573 @@ +# Microsoft Agents Hosting Core + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) + +The core hosting library for Microsoft 365 Agents SDK. This library provides the fundamental building blocks for creating conversational AI agents, including activity processing, state management, authentication, and channel communication. + +## What is this? + +This is the heart of the Microsoft 365 Agents SDK - think of it as the engine that powers your conversational agents. It handles the complex orchestration of conversations, manages state across turns, and provides the infrastructure needed to build production-ready agents that work across Microsoft 365 platforms. + +## Installation + +```bash +pip install microsoft-agents-hosting-core +``` + +## Quick Start + +### Simple Echo Agent + +```python +from microsoft_agents.hosting.core import ( + AgentApplication, + TurnState, + TurnContext, + MemoryStorage +) + +# Create your agent application +storage = MemoryStorage() +agent_app = AgentApplication[TurnState](storage=storage) + +@agent_app.activity("message") +async def on_message(context: TurnContext, state: TurnState): + await context.send_activity(f"You said: {context.activity.text}") + +# Agent is ready to process messages! +``` + +### Activity Handler Style + +```python +from microsoft_agents.hosting.core import ActivityHandler, TurnContext + +class MyAgent(ActivityHandler): + async def on_message_activity(self, turn_context: TurnContext): + await turn_context.send_activity("Hello from ActivityHandler!") + + async def on_conversation_update_activity(self, turn_context: TurnContext): + await turn_context.send_activity("Welcome to the conversation!") + +agent = MyAgent() +``` + +## Core Concepts + +### AgentApplication vs ActivityHandler + +**AgentApplication** - Modern, fluent API for building agents: +- Decorator-based routing (`@agent_app.activity("message")`) +- Built-in state management and middleware +- AI-ready with authorization support +- Type-safe with generics + +**ActivityHandler** - Traditional inheritance-based approach: +- Override methods for different activity types +- More familiar to Bot Framework developers +- Lower-level control over activity processing + +### Turn Context + +Every conversation "turn" (message exchange) gets a `TurnContext` containing: + +```python +async def handle_message(context: TurnContext, state: TurnState): + # The incoming activity (message, event, etc.) + user_message = context.activity.text + + # Send responses + await context.send_activity("Simple text response") + + # Send rich content + from microsoft_agents.hosting.core import MessageFactory + card_activity = MessageFactory.attachment(hero_card) + await context.send_activity(card_activity) + + # Access conversation metadata + conversation_id = context.activity.conversation.id + user_id = context.activity.from_property.id +``` + +### State Management + +Agents need to remember information across conversation turns: + +```python +from microsoft_agents.hosting.core import ( + TurnState, + ConversationState, + UserState, + MemoryStorage +) + +storage = MemoryStorage() +agent_app = AgentApplication[TurnState](storage=storage) + +@agent_app.activity("message") +async def on_message(context: TurnContext, state: TurnState): + # Conversation-scoped data (shared by all users in conversation) + conversation_data = state.conversation + conversation_data.set_value("topic", "weather") + + # User-scoped data (specific to this user across conversations) + user_data = state.user + user_data.set_value("name", "John") + + # Temporary data (only for this turn) + state.temp.set_value("processing_step", "validation") + + # State is automatically saved after the turn +``` + +#### Built-in State Scopes + +- **ConversationState** - Shared across all users in a conversation +- **UserState** - Specific to individual users across all conversations +- **TempState** - Temporary data for the current turn only + +## Advanced Features + +### Route-based Message Handling + +```python +# Handle all messages +@agent_app.activity("message") +async def handle_all_messages(context: TurnContext, state: TurnState): + await context.send_activity("Default handler") + +# Handle specific patterns +@agent_app.message(r"/weather (\w+)") +async def handle_weather(context: TurnContext, state: TurnState): + city = context.matches[1] # Extract from regex + await context.send_activity(f"Weather for {city}") + +# Handle conversation updates +@agent_app.conversation_update("membersAdded") +async def welcome_user(context: TurnContext, state: TurnState): + await context.send_activity("Welcome to the agent!") + +# Handle different activity types +@agent_app.activity("event") +async def handle_events(context: TurnContext, state: TurnState): + event_name = context.activity.name + await context.send_activity(f"Received event: {event_name}") +``` + +### Authentication & Authorization + +```python +from microsoft_agents.hosting.core import Authorization +from microsoft_agents.authentication.msal import MsalConnectionManager + +# Set up authentication +connection_manager = MsalConnectionManager(**config) +authorization = Authorization(storage, connection_manager, **config) + +agent_app = AgentApplication[TurnState]( + storage=storage, + authorization=authorization, + **config +) + +@agent_app.activity("message") +async def authenticated_handler(context: TurnContext, state: TurnState): + # Check if user is authenticated + if not agent_app.auth.is_signed_in(context): + await agent_app.auth.sign_in(context) + return + + # Get user token for Microsoft Graph + token = await agent_app.auth.get_token(context, ["User.Read"]) + # Use token to call Microsoft Graph APIs +``` + +### Middleware & Logging + +```python +from microsoft_agents.hosting.core.storage import ( + TranscriptLoggerMiddleware, + ConsoleTranscriptLogger +) + +# Add transcript logging +adapter.use(TranscriptLoggerMiddleware(ConsoleTranscriptLogger())) + +# Custom middleware +class CustomMiddleware: + async def on_turn(self, context: TurnContext, next_handler): + print(f"Before: {context.activity.text}") + await next_handler() + print("After: Turn completed") + +adapter.use(CustomMiddleware()) +``` + +### File Handling + +```python +from microsoft_agents.hosting.core import InputFile + +@agent_app.activity("message") +async def handle_files(context: TurnContext, state: TurnState): + # Check for file attachments + files = state.temp.input_files + if files: + for file in files: + if file.content_type.startswith("image/"): + # Process image file + content = await file.download() + await context.send_activity(f"Received image: {file.name}") +``` + +### Error Handling + +```python +@agent_app.error +async def on_error(context: TurnContext, error: Exception): + print(f"Error occurred: {error}") + await context.send_activity("Sorry, something went wrong.") + +# Custom error types +from microsoft_agents.hosting.core import ApplicationError + +async def risky_operation(): + if something_wrong: + raise ApplicationError("Custom error message") +``` + +## Storage Options + +### Memory Storage (Development) + +```python +from microsoft_agents.hosting.core import MemoryStorage + +storage = MemoryStorage() # Data lost when app restarts +``` + +### Persistent Storage (Production) + +```python +# Azure Blob Storage +from microsoft_agents.storage.blob import BlobStorage +storage = BlobStorage("connection_string", "container_name") + +# Azure Cosmos DB +from microsoft_agents.storage.cosmos import CosmosDbStorage +storage = CosmosDbStorage("connection_string", "database", "container") +``` + +## Channel Communication + +### Agent-to-Agent Communication + +```python +from microsoft_agents.hosting.core import ( + HttpAgentChannelFactory, + ConfigurationChannelHost +) + +# Set up agent communication +channel_factory = HttpAgentChannelFactory() +channel_host = ConfigurationChannelHost( + channel_factory, connection_manager, config, "HttpAgentClient" +) + +# Send message to another agent +await channel_host.send_to_agent( + agent_id="other-agent-id", + activity=message_activity +) +``` + +### Teams Integration + +```python +from microsoft_agents.hosting.teams import TeamsActivityHandler + +class TeamsAgent(TeamsActivityHandler): + async def on_teams_message_activity(self, turn_context: TurnContext): + # Teams-specific message handling + await turn_context.send_activity("Hello from Teams!") + + async def on_teams_members_added_activity(self, turn_context: TurnContext): + # Handle new team members + await turn_context.send_activity("Welcome to the team!") +``` + +## Rich Content Creation + +### Message Factory + +```python +from microsoft_agents.hosting.core import MessageFactory + +# Simple text +message = MessageFactory.text("Hello world!") + +# Text with suggested actions +message = MessageFactory.suggested_actions( + ["Option 1", "Option 2", "Option 3"], + "Choose an option:" +) + +# Attachment (cards, files, etc.) +message = MessageFactory.attachment(hero_card_attachment) +``` + +### Card Factory + +```python +from microsoft_agents.hosting.core import CardFactory + +# Hero card +hero_card = CardFactory.hero_card( + title="Card Title", + subtitle="Card Subtitle", + text="Card description text", + images=["https://example.com/image.jpg"], + buttons=[ + {"type": "imBack", "title": "Click Me", "value": "button_clicked"} + ] +) + +# Adaptive card +adaptive_card = CardFactory.adaptive_card({ + "type": "AdaptiveCard", + "version": "1.2", + "body": [ + { + "type": "TextBlock", + "text": "Hello Adaptive Cards!" + } + ] +}) +``` + +## Configuration & Deployment + +### Environment Setup + +```python +import os +from microsoft_agents.activity import load_configuration_from_env +from microsoft_agents.hosting.core import AgentApplication, TurnState + +# Load from environment variables +config = load_configuration_from_env(os.environ) + +# Create fully configured agent +agent_app = AgentApplication[TurnState]( + storage=storage, + adapter=adapter, + authorization=authorization, + **config +) +``` + +### Application Options + +```python +from microsoft_agents.hosting.core import ApplicationOptions + +options = ApplicationOptions( + agent_id="my-agent", + storage=storage, + authentication=auth_config, + logging_level="DEBUG" +) + +agent_app = AgentApplication[TurnState](options=options) +``` + +## Architecture Patterns + +### Layered Architecture + +```python +# Domain layer - business logic +class WeatherService: + async def get_weather(self, city: str) -> str: + return f"Weather in {city}: Sunny, 72°F" + +# Application layer - agent handlers +@agent_app.message(r"/weather (\w+)") +async def weather_handler(context: TurnContext, state: TurnState): + city = context.matches[1] + weather_service = WeatherService() + weather = await weather_service.get_weather(city) + await context.send_activity(weather) +``` + +### State Management Patterns + +```python +# Custom state scope +class OrderState(AgentState): + def get_storage_key(self, turn_context: TurnContext): + return f"order:{turn_context.activity.from_property.id}" + +# Use custom state +custom_state = OrderState(storage, "OrderState") +turn_state = TurnState.with_storage(storage, custom_state) +``` + +### Plugin Architecture + +```python +# Create reusable components +class GreetingPlugin: + @staticmethod + def register(app: AgentApplication): + @app.conversation_update("membersAdded") + async def greet(context: TurnContext, state: TurnState): + await context.send_activity("Welcome!") + +# Register plugins +GreetingPlugin.register(agent_app) +``` + +## Testing Your Agents + +### Unit Testing + +```python +import pytest +from microsoft_agents.hosting.core import TurnContext, MemoryStorage +from microsoft_agents.activity import Activity, ActivityTypes + +@pytest.mark.asyncio +async def test_echo_handler(): + # Create test activity + activity = Activity( + type=ActivityTypes.message, + text="test message", + conversation={"id": "test"}, + from_property={"id": "user"} + ) + + # Create test context + context = TurnContext(mock_adapter, activity) + state = TurnState.with_storage(MemoryStorage()) + + # Test your handler + await echo_handler(context, state) + + # Verify response + assert len(context.responses) == 1 + assert "test message" in context.responses[0].text +``` + +### Integration Testing + +```python +# Test with real storage and authentication +async def test_authenticated_flow(): + storage = MemoryStorage() + agent_app = AgentApplication[TurnState]( + storage=storage, + authorization=test_auth + ) + + # Simulate authenticated conversation + context = create_test_context(authenticated_user_activity) + await agent_app.on_turn(context) +``` + +## Performance & Best Practices + +### Efficient State Management + +```python +# Load only needed state scopes +minimal_state = TurnState() +minimal_state.add(ConversationState(storage)) # Only add what you need + +# Batch state operations +async def batch_operations(context: TurnContext, state: TurnState): + state.conversation.set_value("key1", "value1") + state.conversation.set_value("key2", "value2") + state.user.set_value("preference", "dark_mode") + # All saved together at end of turn +``` + +### Async Best Practices + +```python +@agent_app.activity("message") +async def efficient_handler(context: TurnContext, state: TurnState): + # Good: Use async/await for I/O operations + api_result = await external_api_call() + + # Good: Process concurrently when possible + results = await asyncio.gather( + call_service_a(), + call_service_b(), + call_service_c() + ) + + await context.send_activity(f"Results: {results}") +``` + +### Error Recovery + +```python +@agent_app.activity("message") +async def resilient_handler(context: TurnContext, state: TurnState): + try: + result = await unreliable_service() + await context.send_activity(f"Success: {result}") + except ServiceError: + # Graceful degradation + await context.send_activity("Service temporarily unavailable, try again later") + except Exception as e: + # Log and provide user-friendly message + logger.error(f"Unexpected error: {e}") + await context.send_activity("Something went wrong, please try again") +``` + +## Key Classes Reference + +### Core Classes +- **`AgentApplication`** - Main application class with fluent API +- **`ActivityHandler`** - Base class for inheritance-based agents +- **`TurnContext`** - Context for each conversation turn +- **`TurnState`** - State management across conversation turns + +### State Management +- **`ConversationState`** - Conversation-scoped state +- **`UserState`** - User-scoped state across conversations +- **`TempState`** - Temporary state for current turn +- **`MemoryStorage`** - In-memory storage (development) + +### Messaging +- **`MessageFactory`** - Create different types of messages +- **`CardFactory`** - Create rich card attachments +- **`InputFile`** - Handle file attachments + +### Authorization +- **`Authorization`** - Authentication and authorization manager +- **`ClaimsIdentity`** - User identity and claims + +## Migration from Bot Framework + +| Bot Framework | Microsoft Agents Core | +|---------------|----------------------| +| `BotFrameworkAdapter` | `CloudAdapter` | +| `ActivityHandler` | `ActivityHandler` or `AgentApplication` | +| `TurnContext` | `TurnContext` | +| `ConversationState` | `ConversationState` | +| `UserState` | `UserState` | +| `MemoryStorage` | `MemoryStorage` | +| `MessageFactory` | `MessageFactory` | + +## Need Help? + +- 📖 [Full SDK Documentation](https://aka.ms/agents) +- 🏗️ [Architecture Guide](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-activity-processing) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) +- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) +- 📚 [Bot Framework Migration Guide](https://docs.microsoft.com/azure/bot-service/migration/) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-core/setup.py b/libraries/microsoft-agents-hosting-core/setup.py index e7604116..a58ff8f7 100644 --- a/libraries/microsoft-agents-hosting-core/setup.py +++ b/libraries/microsoft-agents-hosting-core/setup.py @@ -1,10 +1,16 @@ from os import environ from setuptools import setup +from pathlib import Path + +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, + long_description=long_description, + long_description_content_type='text/markdown', install_requires=[ f"microsoft-agents-activity=={package_version}", "pyjwt>=2.10.1", diff --git a/libraries/microsoft-agents-hosting-teams/readme.md b/libraries/microsoft-agents-hosting-teams/readme.md new file mode 100644 index 00000000..64f312a0 --- /dev/null +++ b/libraries/microsoft-agents-hosting-teams/readme.md @@ -0,0 +1,590 @@ +# Microsoft Agents Hosting - Teams + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) + +Integration library for building Microsoft Teams agents using the Microsoft 365 Agents SDK. This library provides specialized handlers and utilities for Teams-specific functionality like messaging extensions, task modules, adaptive cards, and meeting events. + +## What is this? + +This library extends the core hosting capabilities with Teams-specific features. It handles Teams' unique interaction patterns like messaging extensions, tab applications, task modules, and meeting integrations. Think of it as the bridge that makes your agent "Teams-native" rather than just a generic chatbot. + +## Installation + +```bash +pip install microsoft-agents-hosting-teams +``` + +## Quick Start + +### Basic Teams Agent + +```python +from microsoft_agents.hosting.teams import TeamsActivityHandler +from microsoft_agents.hosting.core import TurnContext, MessageFactory + +class MyTeamsAgent(TeamsActivityHandler): + async def on_message_activity(self, turn_context: TurnContext): + await turn_context.send_activity( + MessageFactory.text("Hello from Teams!") + ) + + async def on_teams_members_added_activity(self, turn_context: TurnContext): + for member in turn_context.activity.members_added: + if member.id != turn_context.activity.recipient.id: + await turn_context.send_activity( + MessageFactory.text(f"Welcome to Teams, {member.name}!") + ) + +# Use with hosting adapter +agent = MyTeamsAgent() +``` + +### Teams-Specific Features + +```python +from microsoft_agents.hosting.teams import TeamsInfo + +class TeamsAgent(TeamsActivityHandler): + async def on_message_activity(self, turn_context: TurnContext): + text = turn_context.activity.text.lower() + + if "team info" in text: + # Get team member information + team_id = turn_context.activity.channel_data.get("team", {}).get("id") + member = await TeamsInfo.get_team_member( + turn_context, team_id, turn_context.activity.from_property.id + ) + await turn_context.send_activity(f"Member info: {member.name}") + + elif "send to channel" in text: + # Send message to specific channel + channel_id = "19:channel-id@thread.teams" + activity = MessageFactory.text("Broadcasting to channel!") + + await TeamsInfo.send_message_to_teams_channel( + turn_context, activity, channel_id + ) +``` + +## Core Components + +### TeamsActivityHandler + +The main class that extends `ActivityHandler` with Teams-specific event handling: + +```python +class MyTeamsAgent(TeamsActivityHandler): + # Standard message handling + async def on_message_activity(self, turn_context: TurnContext): + await turn_context.send_activity("Got your message!") + + # Teams conversation events + async def on_teams_members_added_activity(self, turn_context: TurnContext): + await turn_context.send_activity("Welcome to the team!") + + async def on_teams_members_removed_activity(self, turn_context: TurnContext): + await turn_context.send_activity("Someone left the team") + + # Meeting events + async def on_teams_meeting_start(self, meeting_details, turn_context: TurnContext): + await turn_context.send_activity("Meeting started!") + + async def on_teams_meeting_end(self, meeting_details, turn_context: TurnContext): + await turn_context.send_activity("Meeting ended!") +``` + +### TeamsInfo Utilities + +Helper class for Teams-specific operations: + +```python +from microsoft_agents.hosting.teams import TeamsInfo + +# Get team member information +member = await TeamsInfo.get_team_member(context, team_id, user_id) + +# Get meeting participant details +participant = await TeamsInfo.get_meeting_participant( + context, meeting_id, participant_id, tenant_id +) + +# Get meeting information +meeting_info = await TeamsInfo.get_meeting_info(context, meeting_id) + +# Send message to Teams channel +await TeamsInfo.send_message_to_teams_channel( + context, activity, channel_id, app_id +) +``` + +## Messaging Extensions + +Handle search and action-based messaging extensions: + +```python +class MessagingExtensionAgent(TeamsActivityHandler): + async def on_teams_messaging_extension_query( + self, turn_context: TurnContext, query + ): + # Handle search queries + search_term = query.parameters[0].value if query.parameters else "" + + results = [ + { + "type": "result", + "attachmentLayout": "list", + "attachments": [ + { + "contentType": "application/vnd.microsoft.card.thumbnail", + "content": { + "title": f"Search result for: {search_term}", + "text": "Found relevant information", + "tap": { + "type": "invoke", + "value": {"query": search_term} + } + } + } + ] + } + ] + + return MessagingExtensionResponse(compose_extension=results[0]) + + async def on_teams_messaging_extension_submit_action( + self, turn_context: TurnContext, action + ): + # Handle action submissions + user_input = action.data + + # Process the action and return response + card = { + "type": "AdaptiveCard", + "version": "1.2", + "body": [ + { + "type": "TextBlock", + "text": f"Processed: {user_input}" + } + ] + } + + return MessagingExtensionActionResponse( + task={ + "type": "continue", + "value": { + "card": card + } + } + ) +``` + +## Task Modules + +Interactive dialogs and forms in Teams: + +```python +class TaskModuleAgent(TeamsActivityHandler): + async def on_teams_task_module_fetch( + self, turn_context: TurnContext, task_module_request + ): + # Show a form to the user + adaptive_card = { + "type": "AdaptiveCard", + "version": "1.2", + "body": [ + { + "type": "TextBlock", + "text": "Please enter your information:" + }, + { + "type": "Input.Text", + "id": "name", + "placeholder": "Your name" + }, + { + "type": "Input.Text", + "id": "email", + "placeholder": "Your email" + } + ], + "actions": [ + { + "type": "Action.Submit", + "title": "Submit", + "data": {"action": "submit"} + } + ] + } + + return TaskModuleResponse( + task={ + "type": "continue", + "value": { + "card": adaptive_card, + "height": 250, + "width": 400, + "title": "User Information" + } + } + ) + + async def on_teams_task_module_submit( + self, turn_context: TurnContext, task_module_request + ): + # Process form submission + data = task_module_request.data + name = data.get("name", "") + email = data.get("email", "") + + # Save data and close task module + await self.save_user_info(name, email) + + return TaskModuleResponse( + task={ + "type": "message", + "value": f"Thank you {name}! Information saved." + } + ) +``` + +## Adaptive Cards & Actions + +Handle card interactions and button clicks: + +```python +class CardAgent(TeamsActivityHandler): + async def on_teams_card_action_invoke(self, turn_context: TurnContext): + # Handle adaptive card button clicks + action_data = turn_context.activity.value + action_type = action_data.get("action") + + if action_type == "approve": + await self.handle_approval(turn_context, action_data) + elif action_type == "reject": + await self.handle_rejection(turn_context, action_data) + + # Return updated card + updated_card = self.create_updated_card(action_data) + return InvokeResponse( + status=200, + body={ + "task": { + "type": "continue", + "value": updated_card + } + } + ) + + def create_approval_card(self, request_id: str): + return { + "type": "AdaptiveCard", + "version": "1.2", + "body": [ + { + "type": "TextBlock", + "text": "Approval Request", + "weight": "Bolder" + }, + { + "type": "TextBlock", + "text": "Please review and approve this request." + } + ], + "actions": [ + { + "type": "Action.Submit", + "title": "Approve", + "data": {"action": "approve", "requestId": request_id} + }, + { + "type": "Action.Submit", + "title": "Reject", + "data": {"action": "reject", "requestId": request_id} + } + ] + } +``` + +## Meeting Integration + +Handle Teams meeting events and interactions: + +```python +class MeetingAgent(TeamsActivityHandler): + async def on_teams_meeting_start( + self, meeting_details, turn_context: TurnContext + ): + # Meeting started - send welcome message + await turn_context.send_activity( + MessageFactory.text("Meeting started! I'm here to help.") + ) + + # Log meeting start + await self.log_meeting_event("start", meeting_details) + + async def on_teams_meeting_participants_join( + self, participants_event, turn_context: TurnContext + ): + # New participants joined + participant_names = [p.user.name for p in participants_event.members] + message = f"Welcome {', '.join(participant_names)} to the meeting!" + + await turn_context.send_activity(MessageFactory.text(message)) + + async def on_teams_meeting_participants_leave( + self, participants_event, turn_context: TurnContext + ): + # Participants left + participant_names = [p.user.name for p in participants_event.members] + message = f"{', '.join(participant_names)} left the meeting" + + await turn_context.send_activity(MessageFactory.text(message)) + + async def get_meeting_info(self, turn_context: TurnContext): + # Get current meeting details + meeting_info = await TeamsInfo.get_meeting_info(turn_context) + + return { + "id": meeting_info.details.id, + "title": meeting_info.details.title, + "organizer": meeting_info.organizer.name, + "start_time": meeting_info.details.scheduled_start_time + } +``` + +## File Handling + +Handle file uploads and downloads in Teams: + +```python +class FileAgent(TeamsActivityHandler): + async def on_teams_file_consent( + self, turn_context: TurnContext, file_consent_response + ): + if file_consent_response.action == "accept": + await self.handle_file_upload(turn_context, file_consent_response) + else: + await turn_context.send_activity("File upload cancelled") + + async def on_teams_file_consent_accept( + self, turn_context: TurnContext, file_consent_response + ): + # File was accepted - handle upload + file_info = file_consent_response.upload_info + + # Upload file content + with open("local_file.pdf", "rb") as file: + await self.upload_file_to_teams(file_info.upload_url, file.read()) + + # Send confirmation + await turn_context.send_activity( + MessageFactory.text("File uploaded successfully!") + ) +``` + +## Configuration & Deployment + +### Environment Setup + +```python +import os +from microsoft_agents.hosting.aiohttp import CloudAdapter +from microsoft_agents.hosting.teams import TeamsActivityHandler +from microsoft_agents.authentication.msal import MsalConnectionManager + +# Load Teams-specific configuration +teams_config = { + "TEAMS_APP_ID": os.getenv("TEAMS_APP_ID"), + "TEAMS_APP_PASSWORD": os.getenv("TEAMS_APP_PASSWORD"), + "TENANT_ID": os.getenv("TENANT_ID") +} + +# Create connection manager +connection_manager = MsalConnectionManager(**teams_config) + +# Create adapter +adapter = CloudAdapter(connection_manager=connection_manager) +``` + +### Teams App Manifest + +Your Teams app manifest should include appropriate permissions: + +```json +{ + "manifestVersion": "1.16", + "version": "1.0.0", + "id": "your-app-id", + "bots": [ + { + "botId": "your-bot-id", + "scopes": ["personal", "team", "groupchat"], + "commandLists": [ + { + "scopes": ["personal", "team", "groupchat"], + "commands": [ + { + "title": "Help", + "description": "Get help with the agent" + } + ] + } + ] + } + ], + "composeExtensions": [ + { + "botId": "your-bot-id", + "commands": [ + { + "id": "search", + "type": "query", + "title": "Search", + "description": "Search for information", + "parameters": [ + { + "name": "searchKeyword", + "title": "Search", + "description": "Enter search term" + } + ] + } + ] + } + ] +} +``` + +## Advanced Patterns + +### Multi-Feature Teams Agent + +```python +class ComprehensiveTeamsAgent(TeamsActivityHandler): + def __init__(self): + super().__init__() + self.commands = { + "help": self.show_help, + "team": self.show_team_info, + "search": self.handle_search, + "approve": self.show_approval_card + } + + async def on_message_activity(self, turn_context: TurnContext): + text = turn_context.activity.text.lower().strip() + + # Handle command-based interactions + for command, handler in self.commands.items(): + if command in text: + await handler(turn_context) + return + + # Default response + await turn_context.send_activity("Try 'help' for available commands") + + async def show_help(self, turn_context: TurnContext): + help_card = MessageFactory.attachment( + self.create_help_card() + ) + await turn_context.send_activity(help_card) + + async def show_team_info(self, turn_context: TurnContext): + team_id = turn_context.activity.channel_data.get("team", {}).get("id") + if team_id: + members = await TeamsInfo.get_team_members(turn_context, team_id) + member_list = "\n".join([f"- {m.name}" for m in members]) + await turn_context.send_activity(f"Team members:\n{member_list}") + else: + await turn_context.send_activity("This command works only in team channels") +``` + +### Error Handling + +```python +class RobustTeamsAgent(TeamsActivityHandler): + async def on_message_activity(self, turn_context: TurnContext): + try: + await self.process_message(turn_context) + except Exception as e: + await self.handle_error(turn_context, e) + + async def handle_error(self, turn_context: TurnContext, error: Exception): + error_message = "Sorry, something went wrong. Please try again." + + if "not found" in str(error).lower(): + error_message = "The requested resource was not found." + elif "permission" in str(error).lower(): + error_message = "You don't have permission for this action." + + await turn_context.send_activity(MessageFactory.text(error_message)) +``` + +## Testing Teams Agents + +### Unit Testing + +```python +import pytest +from microsoft_agents.hosting.teams import TeamsActivityHandler +from microsoft_agents.activity import Activity, ChannelAccount + +@pytest.mark.asyncio +async def test_teams_member_added(): + agent = MyTeamsAgent() + + # Create Teams member added activity + activity = Activity( + type="conversationUpdate", + channel_id="msteams", + members_added=[ChannelAccount(id="user123", name="John Doe")], + recipient=ChannelAccount(id="bot456", name="Test Bot"), + channel_data={"team": {"id": "team789"}} + ) + + context = create_test_context(activity) + await agent.on_conversation_update_activity(context) + + # Verify welcome message was sent + assert len(context.sent_activities) == 1 + assert "welcome" in context.sent_activities[0].text.lower() +``` + +## Key Classes Reference + +- **`TeamsActivityHandler`** - Main handler class with Teams-specific event methods +- **`TeamsInfo`** - Utility class for Teams operations (members, meetings, channels) +- **`MessagingExtensionQuery/Response`** - Handle search and messaging extensions +- **`TaskModuleRequest/Response`** - Interactive dialogs and forms +- **`TabRequest/Response`** - Tab application interactions + +## Features Supported + +✅ **Messaging Extensions** - Search and action-based extensions +✅ **Task Modules** - Interactive dialogs and forms +✅ **Adaptive Cards** - Rich card interactions +✅ **Meeting Events** - Start, end, participant changes +✅ **Team Management** - Member operations, channel messaging +✅ **File Handling** - Upload/download with consent flow +✅ **Tab Apps** - Personal and team tab interactions +✅ **Proactive Messaging** - Send messages to channels/users + +## Migration from Bot Framework + +| Bot Framework Teams | Microsoft Agents Teams | +|-------------------|------------------------| +| `TeamsActivityHandler` | `TeamsActivityHandler` | +| `TeamsInfo` | `TeamsInfo` | +| `on_teams_members_added` | `on_teams_members_added_activity` | +| `MessagingExtensionQuery` | `MessagingExtensionQuery` | +| `TaskModuleRequest` | `TaskModuleRequest` | + +## Need Help? + +- 📖 [Teams Platform Documentation](https://docs.microsoft.com/microsoftteams/platform/) +- 🏗️ [Teams App Development](https://docs.microsoft.com/microsoftteams/platform/bots/what-are-bots) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) +- 💡 [Teams Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples/teams_agent) +- 📋 [Teams App Manifest Reference](https://docs.microsoft.com/microsoftteams/platform/resources/schema/manifest-schema) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-teams/setup.py b/libraries/microsoft-agents-hosting-teams/setup.py index 426735ba..bf3f31df 100644 --- a/libraries/microsoft-agents-hosting-teams/setup.py +++ b/libraries/microsoft-agents-hosting-teams/setup.py @@ -1,10 +1,15 @@ from os import environ from setuptools import setup +from pathlib import Path +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, + long_description=long_description, + long_description_content_type='text/markdown', install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "aiohttp>=3.11.11", diff --git a/libraries/microsoft-agents-storage-blob/readme.md b/libraries/microsoft-agents-storage-blob/readme.md new file mode 100644 index 00000000..ac7cc8e8 --- /dev/null +++ b/libraries/microsoft-agents-storage-blob/readme.md @@ -0,0 +1,529 @@ +# Microsoft Agents Storage - Blob + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) + +Azure Blob Storage integration for Microsoft 365 Agents SDK. This library provides persistent storage for conversation state, user data, and custom agent information using Azure Blob Storage. + +## What is this? + +This library implements the storage interface for the Microsoft 365 Agents SDK using Azure Blob Storage as the backend. It enables your agents to persist conversation state, user preferences, and custom data across sessions. Perfect for production deployments where you need reliable, scalable cloud storage. + +## Installation + +```bash +pip install microsoft-agents-storage-blob +``` + +## Quick Start + +### Basic Configuration with Connection String + +```python +from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig + +# Configure using connection string +config = BlobStorageConfig( + container_name="agent-storage", + connection_string="DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=..." +) + +# Create storage instance +storage = BlobStorage(config) + +# Initialize the container (creates if doesn't exist) +await storage.initialize() +``` + +### Using with AgentApplication + +```python +from microsoft_agents.hosting.core import AgentApplication, TurnState +from microsoft_agents.hosting.aiohttp import CloudAdapter +from microsoft_agents.authentication.msal import MsalConnectionManager +from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig + +# Configure blob storage +blob_config = BlobStorageConfig( + container_name="my-agent-data", + connection_string=os.getenv("AZURE_STORAGE_CONNECTION_STRING") +) +storage = BlobStorage(blob_config) +await storage.initialize() + +# Create agent with blob storage +app = AgentApplication[TurnState]( + storage=storage, + adapter=adapter, + authorization=authorization +) +``` + +## Configuration Options + +### Connection String Authentication + +The simplest way to connect - uses a storage account connection string: + +```python +config = BlobStorageConfig( + container_name="agent-storage", + connection_string="DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=mykey;EndpointSuffix=core.windows.net" +) +``` + +**Get your connection string:** +- Azure Portal → Storage Account → Access Keys → Connection String + +### Token-Based Authentication (Recommended for Production) + +Uses Azure Active Directory credentials for secure, keyless authentication: + +```python +from azure.identity import DefaultAzureCredential + +config = BlobStorageConfig( + container_name="agent-storage", + url="https://myaccount.blob.core.windows.net", + credential=DefaultAzureCredential() +) +``` + +**Benefits:** +- ✅ No secrets in code +- ✅ Managed Identity support +- ✅ Automatic token renewal +- ✅ Fine-grained access control via Azure RBAC + +### Configuration Parameters + +| Parameter | Type | Required | Description | +|-----------|------|----------|-------------| +| `container_name` | `str` | Yes | Name of the blob container to use | +| `connection_string` | `str` | No* | Storage account connection string | +| `url` | `str` | No* | Blob service URL (e.g., `https://account.blob.core.windows.net`) | +| `credential` | `TokenCredential` | No** | Azure credential for authentication | + +*Either `connection_string` OR (`url` + `credential`) must be provided +**Required when using `url` + +## Core Operations + +### Read Data + +```python +from microsoft_agents.hosting.core.storage import StoreItem + +# Define your data model +class UserPreferences(StoreItem): + def __init__(self, user_id: str, theme: str = "light", language: str = "en"): + self.id = user_id + self.theme = theme + self.language = language + +# Read single item +result = await storage.read(["user123"], target_cls=UserPreferences) +if "user123" in result: + prefs = result["user123"] + print(f"Theme: {prefs.theme}, Language: {prefs.language}") + +# Read multiple items +results = await storage.read( + ["user123", "user456", "user789"], + target_cls=UserPreferences +) +``` + +### Write Data + +```python +# Create or update items +user_prefs = UserPreferences("user123", theme="dark", language="es") + +await storage.write({ + "user123": user_prefs +}) + +# Write multiple items at once +await storage.write({ + "user123": UserPreferences("user123", theme="dark"), + "user456": UserPreferences("user456", theme="light"), + "user789": UserPreferences("user789", language="fr") +}) +``` + +### Delete Data + +```python +# Delete single item +await storage.delete(["user123"]) + +# Delete multiple items +await storage.delete(["user123", "user456", "user789"]) +``` + +## Common Patterns + +### Conversation State Storage + +```python +from microsoft_agents.hosting.core import ConversationState, TurnContext + +class MyAgent(ActivityHandler): + def __init__(self, storage: BlobStorage): + self.conversation_state = ConversationState(storage) + + async def on_message_activity(self, turn_context: TurnContext): + # Access conversation state + state_accessor = self.conversation_state.create_property("conversation_data") + conv_data = await state_accessor.get(turn_context, {}) + + # Update message count + conv_data["message_count"] = conv_data.get("message_count", 0) + 1 + + await turn_context.send_activity( + f"Message #{conv_data['message_count']} in this conversation" + ) + + # Save changes + await self.conversation_state.save_changes(turn_context) +``` + +### User State Storage + +```python +from microsoft_agents.hosting.core import UserState + +class MyAgent(ActivityHandler): + def __init__(self, storage: BlobStorage): + self.user_state = UserState(storage) + + async def on_message_activity(self, turn_context: TurnContext): + # Access user state + user_accessor = self.user_state.create_property("user_profile") + profile = await user_accessor.get(turn_context, {}) + + # Track user preferences + if not profile.get("name"): + profile["name"] = turn_context.activity.from_property.name + profile["first_interaction"] = turn_context.activity.timestamp + + profile["last_interaction"] = turn_context.activity.timestamp + profile["total_messages"] = profile.get("total_messages", 0) + 1 + + # Save changes + await self.user_state.save_changes(turn_context) +``` + +### Custom Data Models + +```python +from microsoft_agents.hosting.core.storage import StoreItem +from datetime import datetime + +class TaskItem(StoreItem): + def __init__(self, task_id: str, title: str, status: str = "pending"): + self.id = task_id + self.title = title + self.status = status + self.created_at = datetime.utcnow().isoformat() + self.updated_at = datetime.utcnow().isoformat() + +class TaskManager: + def __init__(self, storage: BlobStorage): + self.storage = storage + + async def create_task(self, task_id: str, title: str): + task = TaskItem(task_id, title) + await self.storage.write({task_id: task}) + return task + + async def get_task(self, task_id: str): + result = await self.storage.read([task_id], target_cls=TaskItem) + return result.get(task_id) + + async def update_task_status(self, task_id: str, status: str): + task = await self.get_task(task_id) + if task: + task.status = status + task.updated_at = datetime.utcnow().isoformat() + await self.storage.write({task_id: task}) + return task + + async def delete_task(self, task_id: str): + await self.storage.delete([task_id]) +``` + +## Environment Setup + +### Local Development with Azurite + +Use the Azure Storage Emulator for local testing: + +```bash +# Install Azurite +npm install -g azurite + +# Start the emulator +azurite --silent --location c:\azurite --debug c:\azurite\debug.log +``` + +```python +# Use emulator connection string +config = BlobStorageConfig( + container_name="local-test", + connection_string=( + "AccountName=devstoreaccount1;" + "AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;" + "DefaultEndpointsProtocol=http;" + "BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1" + ) +) +``` + +### Production Configuration + +```python +import os +from azure.identity import DefaultAzureCredential + +# Using environment variables +config = BlobStorageConfig( + container_name=os.getenv("STORAGE_CONTAINER_NAME", "agent-production"), + url=os.getenv("AZURE_STORAGE_URL"), + credential=DefaultAzureCredential() +) + +storage = BlobStorage(config) +await storage.initialize() +``` + +**Environment Variables:** +```bash +AZURE_STORAGE_URL=https://myaccount.blob.core.windows.net +STORAGE_CONTAINER_NAME=agent-production +``` + +### Azure Managed Identity + +When running in Azure (App Service, Functions, Container Apps), use Managed Identity: + +```python +from azure.identity import ManagedIdentityCredential + +config = BlobStorageConfig( + container_name="agent-storage", + url="https://myaccount.blob.core.windows.net", + credential=ManagedIdentityCredential() +) +``` + +**Azure RBAC Roles Required:** +- `Storage Blob Data Contributor` - For read/write access +- `Storage Blob Data Reader` - For read-only access + +## Advanced Usage + +### Batch Operations + +```python +# Efficient batch reads +user_ids = [f"user{i}" for i in range(100)] +results = await storage.read(user_ids, target_cls=UserPreferences) + +# Batch writes +batch_data = { + f"user{i}": UserPreferences(f"user{i}", theme="dark") + for i in range(100) +} +await storage.write(batch_data) + +# Batch deletes +await storage.delete([f"user{i}" for i in range(100)]) +``` + +### Error Handling + +```python +from azure.core.exceptions import ResourceNotFoundError + +async def safe_read_user(storage: BlobStorage, user_id: str): + try: + result = await storage.read([user_id], target_cls=UserPreferences) + return result.get(user_id) + except ResourceNotFoundError: + # Container doesn't exist + await storage.initialize() + return None + except Exception as e: + # Log error and return default + print(f"Error reading user data: {e}") + return UserPreferences(user_id) +``` + +### Container Management + +```python +# Initialize ensures container exists +await storage.initialize() + +# Safe to call multiple times (idempotent) +await storage.initialize() +await storage.initialize() # No-op if already initialized + +# Manual container operations (if needed) +container_client = storage._container_client +exists = await container_client.exists() +if not exists: + await container_client.create_container() +``` + +### Performance Optimization + +```python +# Use async context manager for cleanup +async with BlobServiceClient.from_connection_string(conn_str) as client: + container = client.get_container_client("agent-storage") + # Operations here + pass + +# Reuse storage instance (singleton pattern) +class StorageManager: + _instance = None + + @classmethod + async def get_instance(cls): + if cls._instance is None: + config = BlobStorageConfig(...) + cls._instance = BlobStorage(config) + await cls._instance.initialize() + return cls._instance +``` + +## Testing + +### Unit Testing with Mocks + +```python +import pytest +from unittest.mock import AsyncMock, MagicMock + +@pytest.fixture +def mock_storage(): + storage = AsyncMock(spec=BlobStorage) + storage.read.return_value = {} + storage.write.return_value = None + storage.delete.return_value = None + return storage + +@pytest.mark.asyncio +async def test_agent_with_storage(mock_storage): + agent = MyAgent(mock_storage) + # Test agent logic + await agent.process_message(...) + + # Verify storage was called + mock_storage.write.assert_called_once() +``` + +### Integration Testing with Azurite + +```python +@pytest.mark.integration +@pytest.mark.asyncio +async def test_storage_integration(): + config = BlobStorageConfig( + container_name="test-container", + connection_string="UseDevelopmentStorage=true" + ) + storage = BlobStorage(config) + await storage.initialize() + + # Test real operations + await storage.write({"key": TestItem("key", "value")}) + result = await storage.read(["key"], target_cls=TestItem) + assert result["key"].value == "value" + + # Cleanup + await storage.delete(["key"]) +``` + +## Migration from MemoryStorage + +Switching from `MemoryStorage` to `BlobStorage` is straightforward: + +```python +# Before (development) +from microsoft_agents.hosting.core import MemoryStorage +storage = MemoryStorage() + +# After (production) +from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig +config = BlobStorageConfig( + container_name="agent-storage", + connection_string=os.getenv("AZURE_STORAGE_CONNECTION_STRING") +) +storage = BlobStorage(config) +await storage.initialize() +``` + +**Benefits of switching to BlobStorage:** +- ✅ Data persists across restarts +- ✅ Scalable to millions of items +- ✅ Multi-instance support (load balancing) +- ✅ Automatic backups and geo-replication +- ✅ Built-in monitoring and diagnostics + +## Troubleshooting + +### Common Issues + +**Container not found error:** +```python +# Solution: Initialize the storage +await storage.initialize() +``` + +**Authentication failures:** +```python +# Verify credential works +from azure.identity import DefaultAzureCredential +credential = DefaultAzureCredential() +token = credential.get_token("https://storage.azure.com/.default") +print(f"Token acquired: {token.token[:20]}...") +``` + +**Connection string issues:** +```python +# Verify connection string format +from azure.storage.blob import BlobServiceClient +try: + client = BlobServiceClient.from_connection_string(connection_string) + print("Connection string valid") +except Exception as e: + print(f"Invalid connection string: {e}") +``` + +## Best Practices + +1. **Use Token Authentication in Production** - Avoid storing connection strings; use Managed Identity or DefaultAzureCredential +2. **Initialize Once** - Call `storage.initialize()` during app startup, not on every request +3. **Implement Retry Logic** - Handle transient failures with exponential backoff +4. **Monitor Performance** - Use Azure Monitor to track storage operations +5. **Set Lifecycle Policies** - Configure automatic cleanup of old data in Azure Portal +6. **Use Consistent Naming** - Establish key naming conventions (e.g., `user:{id}`, `conversation:{id}`) +7. **Batch Operations** - Read/write multiple items together when possible + +## Key Classes Reference + +- **`BlobStorage`** - Main storage implementation using Azure Blob Storage +- **`BlobStorageConfig`** - Configuration settings for connection and authentication +- **`StoreItem`** - Base class for data models (inherit to create custom types) + +## Need Help? + +- 📖 [Azure Blob Storage Documentation](https://docs.microsoft.com/azure/storage/blobs/) +- 🔐 [Azure Identity Documentation](https://docs.microsoft.com/python/api/azure-identity/) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) +- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. diff --git a/libraries/microsoft-agents-storage-cosmos/readme.md b/libraries/microsoft-agents-storage-cosmos/readme.md new file mode 100644 index 00000000..217c698e --- /dev/null +++ b/libraries/microsoft-agents-storage-cosmos/readme.md @@ -0,0 +1,774 @@ +# Microsoft Agents Storage - Cosmos DB + +[![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) + +Azure Cosmos DB storage integration for Microsoft 365 Agents SDK. This library provides enterprise-grade persistent storage for conversation state, user data, and custom agent information using Azure Cosmos DB's globally distributed, multi-model database service. + +## What is this? + +This library implements the storage interface for the Microsoft 365 Agents SDK using Azure Cosmos DB as the backend. It provides automatic partitioning, global distribution, and low-latency access to your agent data. Perfect for production deployments requiring high availability, scalability, and multi-region support. + +**Why Cosmos DB?** +- 🌍 Global distribution with multi-region writes +- ⚡ Single-digit millisecond latency +- 📈 Automatic and instant scalability +- 🔄 Multiple consistency models +- 💪 99.999% availability SLA + +## Installation + +```bash +pip install microsoft-agents-storage-cosmos +``` + +## Quick Start + +### Basic Configuration with Auth Key + +```python +from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig + +# Configure using endpoint and auth key +config = CosmosDBStorageConfig( + cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", + auth_key="your-auth-key-here", + database_id="agent-database", + container_id="agent-storage" +) + +# Create storage instance +storage = CosmosDBStorage(config) + +# Initialize (creates database and container if needed) +await storage.initialize() +``` + +### Using with AgentApplication + +```python +from microsoft_agents.hosting.core import AgentApplication, TurnState +from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig +import os + +# Configure Cosmos DB storage +cosmos_config = CosmosDBStorageConfig( + cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), + auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), + database_id="agents", + container_id="conversations" +) + +storage = CosmosDBStorage(cosmos_config) +await storage.initialize() + +# Create agent with Cosmos DB storage +app = AgentApplication[TurnState]( + storage=storage, + adapter=adapter, + authorization=authorization +) +``` + +## Configuration Options + +### Auth Key Authentication (Simple) + +```python +config = CosmosDBStorageConfig( + cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", + auth_key="your-primary-or-secondary-key", + database_id="agent-db", + container_id="bot-storage" +) +``` + +**Get your endpoint and key:** +- Azure Portal → Cosmos DB Account → Keys → URI and Primary Key + +### Token-Based Authentication (Recommended) + +Uses Azure Active Directory credentials for secure, keyless authentication: + +```python +from azure.identity import DefaultAzureCredential + +config = CosmosDBStorageConfig( + url="https://myaccount.documents.azure.com:443/", + credential=DefaultAzureCredential(), + database_id="agent-db", + container_id="bot-storage" +) +``` + +**Note:** When using `url` with `credential`, do not provide `cosmos_db_endpoint` or `auth_key`. + +### Configuration from JSON File + +```python +# Create config.json +{ + "cosmos_db_endpoint": "https://myaccount.documents.azure.com:443/", + "auth_key": "your-key", + "database_id": "agent-db", + "container_id": "bot-storage", + "container_throughput": 800, + "compatibility_mode": false +} + +# Load from file +config = CosmosDBStorageConfig(filename="config.json") +``` + +### All Configuration Parameters + +| Parameter | Type | Required | Default | Description | +|-----------|------|----------|---------|-------------| +| `cosmos_db_endpoint` | `str` | Yes* | `""` | Cosmos DB account endpoint URL | +| `auth_key` | `str` | Yes* | `""` | Primary or secondary authentication key | +| `database_id` | `str` | Yes | `""` | Database identifier | +| `container_id` | `str` | Yes | `""` | Container identifier | +| `url` | `str` | No** | `""` | Alternative to `cosmos_db_endpoint` for token auth | +| `credential` | `TokenCredential` | No** | `None` | Azure credential for token-based auth | +| `container_throughput` | `int` | No | `400` | RU/s when creating container (400-unlimited) | +| `cosmos_client_options` | `dict` | No | `{}` | Additional client options (connection_policy, consistency_level) | +| `key_suffix` | `str` | No | `""` | Suffix added to all keys for multi-tenancy | +| `compatibility_mode` | `bool` | No | `False` | Truncate keys to 255 chars for legacy support | + +*Either (`cosmos_db_endpoint` + `auth_key`) OR (`url` + `credential`) required +**Required when using `url` + +## Core Operations + +### Read Data + +```python +from microsoft_agents.hosting.core.storage import StoreItem + +# Define your data model +class ConversationData(StoreItem): + def __init__(self, conv_id: str, topic: str = "", message_count: int = 0): + self.id = conv_id + self.topic = topic + self.message_count = message_count + +# Read single item +result = await storage.read(["conv123"], target_cls=ConversationData) +if "conv123" in result: + conv = result["conv123"] + print(f"Topic: {conv.topic}, Messages: {conv.message_count}") + +# Read multiple items +results = await storage.read( + ["conv123", "conv456", "conv789"], + target_cls=ConversationData +) + +# Handle missing items +for key in ["conv123", "conv456"]: + if key in results: + print(f"Found: {results[key].topic}") + else: + print(f"Not found: {key}") +``` + +### Write Data + +```python +# Create or update items +conv_data = ConversationData("conv123", topic="Weather", message_count=5) + +await storage.write({ + "conv123": conv_data +}) + +# Batch writes (more efficient) +await storage.write({ + "conv123": ConversationData("conv123", topic="Weather", message_count=5), + "conv456": ConversationData("conv456", topic="Sports", message_count=12), + "conv789": ConversationData("conv789", topic="News", message_count=3) +}) +``` + +### Delete Data + +```python +# Delete single item +await storage.delete(["conv123"]) + +# Delete multiple items +await storage.delete(["conv123", "conv456", "conv789"]) + +# Delete is idempotent (no error if item doesn't exist) +await storage.delete(["nonexistent-key"]) # No error +``` + +## Common Patterns + +### Conversation State Storage + +```python +from microsoft_agents.hosting.core import ConversationState, ActivityHandler, TurnContext + +class MyAgent(ActivityHandler): + def __init__(self, storage: CosmosDBStorage): + self.conversation_state = ConversationState(storage) + + async def on_message_activity(self, turn_context: TurnContext): + # Access conversation-scoped state + state_accessor = self.conversation_state.create_property("conversation_data") + conv_data = await state_accessor.get(turn_context, {}) + + # Track conversation metadata + if not conv_data.get("started_at"): + conv_data["started_at"] = turn_context.activity.timestamp + conv_data["topic"] = "general" + + conv_data["message_count"] = conv_data.get("message_count", 0) + 1 + conv_data["last_activity"] = turn_context.activity.timestamp + + # Save changes + await self.conversation_state.save_changes(turn_context) +``` + +### User State Storage + +```python +from microsoft_agents.hosting.core import UserState + +class MyAgent(ActivityHandler): + def __init__(self, storage: CosmosDBStorage): + self.user_state = UserState(storage) + + async def on_message_activity(self, turn_context: TurnContext): + # Access user-scoped state (persists across conversations) + user_accessor = self.user_state.create_property("user_profile") + profile = await user_accessor.get(turn_context, {}) + + # Track user preferences and history + if not profile.get("user_id"): + profile["user_id"] = turn_context.activity.from_property.id + profile["name"] = turn_context.activity.from_property.name + profile["first_seen"] = turn_context.activity.timestamp + profile["preferences"] = {"language": "en", "timezone": "UTC"} + + profile["last_seen"] = turn_context.activity.timestamp + profile["total_interactions"] = profile.get("total_interactions", 0) + 1 + + # Save changes + await self.user_state.save_changes(turn_context) +``` + +### Custom Data Models + +```python +from microsoft_agents.hosting.core.storage import StoreItem +from datetime import datetime +from typing import List + +class OrderItem(StoreItem): + def __init__(self, order_id: str, customer_id: str, items: List[dict], status: str = "pending"): + self.id = order_id + self.customer_id = customer_id + self.items = items + self.status = status + self.created_at = datetime.utcnow().isoformat() + self.updated_at = datetime.utcnow().isoformat() + self.total_amount = sum(item.get("price", 0) for item in items) + +class OrderManager: + def __init__(self, storage: CosmosDBStorage): + self.storage = storage + + async def create_order(self, order_id: str, customer_id: str, items: List[dict]): + order = OrderItem(order_id, customer_id, items) + await self.storage.write({order_id: order}) + return order + + async def get_order(self, order_id: str): + result = await self.storage.read([order_id], target_cls=OrderItem) + return result.get(order_id) + + async def update_order_status(self, order_id: str, new_status: str): + order = await self.get_order(order_id) + if order: + order.status = new_status + order.updated_at = datetime.utcnow().isoformat() + await self.storage.write({order_id: order}) + return order + + async def cancel_order(self, order_id: str): + await self.storage.delete([order_id]) +``` + +## Advanced Features + +### Key Suffix for Multi-Tenancy + +Use key suffixes to isolate data for different tenants or environments: + +```python +# Production environment +prod_config = CosmosDBStorageConfig( + cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", + auth_key="your-key", + database_id="shared-db", + container_id="shared-container", + key_suffix="_prod" +) + +# Development environment (same database/container) +dev_config = CosmosDBStorageConfig( + cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", + auth_key="your-key", + database_id="shared-db", + container_id="shared-container", + key_suffix="_dev" +) + +# Keys are automatically suffixed +# "user123" becomes "user123_prod" or "user123_dev" +``` + +### Compatibility Mode + +Enable compatibility mode for legacy containers with 255-character key limits: + +```python +config = CosmosDBStorageConfig( + cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", + auth_key="your-key", + database_id="legacy-db", + container_id="old-container", + compatibility_mode=True +) + +# Long keys are automatically truncated with SHA-256 hash appended +# to prevent collisions +``` + +**Note:** Cannot use `key_suffix` with `compatibility_mode=True`. + +### Custom Throughput Settings + +```python +# Standard throughput (400 RU/s) +config = CosmosDBStorageConfig( + cosmos_db_endpoint="...", + auth_key="...", + database_id="agent-db", + container_id="storage", + container_throughput=400 # Default +) + +# High-performance throughput +config = CosmosDBStorageConfig( + cosmos_db_endpoint="...", + auth_key="...", + database_id="agent-db", + container_id="storage", + container_throughput=1000 # Higher RU/s for more traffic +) + +# Auto-scale throughput (specify in Azure Portal) +# Set to 0 to use existing container throughput +config = CosmosDBStorageConfig( + cosmos_db_endpoint="...", + auth_key="...", + database_id="agent-db", + container_id="existing-container", + container_throughput=0 # Don't override existing +) +``` + +### Custom Client Options + +```python +from azure.cosmos import documents + +# Configure connection policy and consistency +connection_policy = documents.ConnectionPolicy() +connection_policy.RequestTimeout = 30000 # 30 seconds +connection_policy.PreferredLocations = ["West US", "East US"] + +config = CosmosDBStorageConfig( + cosmos_db_endpoint="...", + auth_key="...", + database_id="agent-db", + container_id="storage", + cosmos_client_options={ + "connection_policy": connection_policy, + "consistency_level": "Session" # Session, Eventual, Strong, etc. + } +) +``` + +## Environment Setup + +### Local Development with Cosmos DB Emulator + +Install and run the Azure Cosmos DB Emulator for local testing: + +**Download:** [Azure Cosmos DB Emulator](https://docs.microsoft.com/azure/cosmos-db/local-emulator) + +```python +# Use emulator connection details +config = CosmosDBStorageConfig( + cosmos_db_endpoint="https://localhost:8081", + auth_key="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", + database_id="local-test-db", + container_id="bot-storage" +) + +storage = CosmosDBStorage(config) +await storage.initialize() +``` + +**Note:** The auth key above is the well-known emulator key (safe to use locally). + +### Production Configuration + +```python +import os +from azure.identity import DefaultAzureCredential + +# Using environment variables +config = CosmosDBStorageConfig( + cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), + auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), + database_id=os.getenv("COSMOS_DATABASE_ID", "agents"), + container_id=os.getenv("COSMOS_CONTAINER_ID", "production"), + container_throughput=int(os.getenv("COSMOS_THROUGHPUT", "400")) +) + +# Or use Managed Identity (recommended) +config = CosmosDBStorageConfig( + url=os.getenv("COSMOS_DB_ENDPOINT"), + credential=DefaultAzureCredential(), + database_id="agents", + container_id="production" +) + +storage = CosmosDBStorage(config) +await storage.initialize() +``` + +**Environment Variables:** +```bash +COSMOS_DB_ENDPOINT=https://myaccount.documents.azure.com:443/ +COSMOS_DB_AUTH_KEY=your-primary-key-here +COSMOS_DATABASE_ID=agents +COSMOS_CONTAINER_ID=production +COSMOS_THROUGHPUT=800 +``` + +### Azure Managed Identity + +When running in Azure (App Service, Functions, Container Apps): + +```python +from azure.identity import ManagedIdentityCredential + +config = CosmosDBStorageConfig( + url="https://myaccount.documents.azure.com:443/", + credential=ManagedIdentityCredential(), + database_id="agents", + container_id="production" +) +``` + +**Azure RBAC Roles Required:** +- `Cosmos DB Built-in Data Contributor` - For read/write access +- `Cosmos DB Built-in Data Reader` - For read-only access + +## Key Sanitization + +Cosmos DB has restrictions on certain characters in keys. The library automatically sanitizes keys: + +```python +# Forbidden characters: \ ? / # \t \n \r * +# These are replaced with *{unicode-code-point} + +# Example: +original_key = "user/123?test" +# Becomes: "user*47123*63test" + +# Special handling for keys with forbidden characters +await storage.write({ + "user/123": user_data, # Automatically sanitized + "conversation?active": conv_data # Automatically sanitized +}) + +# Read using original keys (sanitization is automatic) +result = await storage.read(["user/123", "conversation?active"], target_cls=UserData) +``` + +**Internal behavior:** +- Keys are sanitized before storage operations +- Original keys are preserved in the document as `realId` +- Reads return data mapped to original keys + +## Error Handling + +```python +from azure.cosmos.exceptions import CosmosResourceNotFoundError, CosmosHttpResponseError + +async def safe_storage_operation(storage: CosmosDBStorage, key: str): + try: + # Initialize if needed + await storage.initialize() + + # Perform operation + result = await storage.read([key], target_cls=UserData) + return result.get(key) + + except CosmosResourceNotFoundError as e: + # Container or database doesn't exist + print(f"Resource not found: {e}") + await storage.initialize() + return None + + except CosmosHttpResponseError as e: + # Network issues, throttling, etc. + if e.status_code == 429: # Too Many Requests + print("Rate limited, implement retry with backoff") + elif e.status_code == 503: # Service Unavailable + print("Service temporarily unavailable") + else: + print(f"Cosmos error: {e.status_code} - {e.message}") + raise + + except ValueError as e: + # Configuration or validation errors + print(f"Configuration error: {e}") + raise +``` + +## Performance Optimization + +### Batch Operations + +```python +# Efficient: Single request with multiple keys +user_ids = [f"user{i}" for i in range(100)] +results = await storage.read(user_ids, target_cls=UserData) + +# Efficient: Batch write +batch_data = { + f"user{i}": UserData(f"user{i}", name=f"User {i}") + for i in range(100) +} +await storage.write(batch_data) +``` + +### Singleton Pattern + +```python +# Reuse storage instance across requests +class StorageManager: + _instance: CosmosDBStorage = None + + @classmethod + async def get_instance(cls) -> CosmosDBStorage: + if cls._instance is None: + config = CosmosDBStorageConfig( + cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), + auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), + database_id="agents", + container_id="production" + ) + cls._instance = CosmosDBStorage(config) + await cls._instance.initialize() + return cls._instance + +# Use in your agent +storage = await StorageManager.get_instance() +``` + +### Indexing Strategy + +Configure indexing in Azure Portal for better query performance: + +```json +{ + "indexingMode": "consistent", + "automatic": true, + "includedPaths": [ + { + "path": "/document/user_id/*" + }, + { + "path": "/document/timestamp/*" + } + ], + "excludedPaths": [ + { + "path": "/document/large_text/*" + } + ] +} +``` + +## Testing + +### Unit Testing with Mocks + +```python +import pytest +from unittest.mock import AsyncMock + +@pytest.fixture +def mock_storage(): + storage = AsyncMock(spec=CosmosDBStorage) + storage.read.return_value = {} + storage.write.return_value = None + storage.delete.return_value = None + storage.initialize.return_value = None + return storage + +@pytest.mark.asyncio +async def test_agent_with_storage(mock_storage): + agent = MyAgent(mock_storage) + + # Test agent logic + await agent.process_message(...) + + # Verify storage interactions + mock_storage.initialize.assert_called_once() + mock_storage.write.assert_called() +``` + +### Integration Testing with Emulator + +```python +@pytest.mark.integration +@pytest.mark.asyncio +async def test_cosmos_storage_integration(): + config = CosmosDBStorageConfig( + cosmos_db_endpoint="https://localhost:8081", + auth_key="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", + database_id="test-db", + container_id="test-container" + ) + + storage = CosmosDBStorage(config) + await storage.initialize() + + # Test CRUD operations + test_item = TestData("key1", "value1") + await storage.write({"key1": test_item}) + + result = await storage.read(["key1"], target_cls=TestData) + assert result["key1"].value == "value1" + + await storage.delete(["key1"]) + result = await storage.read(["key1"], target_cls=TestData) + assert "key1" not in result +``` + +## Migration Guide + +### From MemoryStorage + +```python +# Before (development) +from microsoft_agents.hosting.core import MemoryStorage +storage = MemoryStorage() + +# After (production) +from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig +import os + +config = CosmosDBStorageConfig( + cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), + auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), + database_id="agents", + container_id="production" +) +storage = CosmosDBStorage(config) +await storage.initialize() +``` + +### From BlobStorage + +Both storage implementations use the same interface: + +```python +# BlobStorage +from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig + +# CosmosDBStorage (drop-in replacement) +from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig + +# Same usage pattern +storage = CosmosDBStorage(config) +await storage.initialize() +``` + +**Key Differences:** +- Cosmos DB: Better for high-throughput, low-latency scenarios +- Blob Storage: Better for large objects, archival, cost-sensitive scenarios + +## Troubleshooting + +### Common Issues + +**"Database/Container not found"** +```python +# Solution: Initialize storage to create resources +await storage.initialize() +``` + +**"Invalid key characters"** +```python +# Solution: Keys are automatically sanitized +# Avoid using \ ? / # \t \n \r * if possible +# Library handles sanitization transparently +``` + +**"Request rate too large (429)"** +```python +# Solution: Increase container throughput or implement retry logic +config = CosmosDBStorageConfig( + # ... other settings ... + container_throughput=1000 # Increase RU/s +) +``` + +**Authentication failures** +```python +# Verify credentials work +from azure.cosmos.aio import CosmosClient + +client = CosmosClient(endpoint, auth_key) +database_list = await client.list_databases() +print(f"Connected! Databases: {len(list(database_list))}") +``` + +## Best Practices + +1. **Use Managed Identity in Production** - Avoid storing auth keys in code or environment variables +2. **Initialize Once** - Call `storage.initialize()` during app startup, not per request +3. **Batch Operations** - Read/write multiple items together when possible +4. **Monitor RU Consumption** - Use Azure Monitor to track Request Units usage +5. **Set Appropriate Throughput** - Start with 400 RU/s, scale up based on metrics +6. **Use Session Consistency** - Default consistency level for most scenarios +7. **Implement Retry Logic** - Handle transient failures with exponential backoff +8. **Partition Wisely** - Current implementation uses `/id` partitioning (automatic) +9. **Enable Diagnostics** - Configure Azure diagnostic logs for troubleshooting +10. **Test with Emulator** - Use local emulator for development and testing + +## Key Classes Reference + +- **`CosmosDBStorage`** - Main storage implementation using Azure Cosmos DB +- **`CosmosDBStorageConfig`** - Configuration settings for connection and behavior +- **`StoreItem`** - Base class for data models (inherit to create custom types) + +## Need Help? + +- 📖 [Azure Cosmos DB Documentation](https://docs.microsoft.com/azure/cosmos-db/) +- 🔐 [Azure Identity Documentation](https://docs.microsoft.com/python/api/azure-identity/) +- ⚡ [Cosmos DB Best Practices](https://docs.microsoft.com/azure/cosmos-db/performance-tips) +- 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) +- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) + +Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. diff --git a/libraries/microsoft-agents-storage-cosmos/setup.py b/libraries/microsoft-agents-storage-cosmos/setup.py index 8f09a0d2..61a04939 100644 --- a/libraries/microsoft-agents-storage-cosmos/setup.py +++ b/libraries/microsoft-agents-storage-cosmos/setup.py @@ -1,10 +1,15 @@ from os import environ from setuptools import setup +from pathlib import Path +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, + long_description=long_description, + long_description_content_type='text/markdown', install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "azure-core", From f3b79af427fd67e3194a9fb150a1dbb517dba67a Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Wed, 8 Oct 2025 16:20:21 -0700 Subject: [PATCH 02/10] Add long description to setup.py for PyPi compatibility --- libraries/microsoft-agents-storage-blob/setup.py | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/libraries/microsoft-agents-storage-blob/setup.py b/libraries/microsoft-agents-storage-blob/setup.py index d38c25bb..7d413978 100644 --- a/libraries/microsoft-agents-storage-blob/setup.py +++ b/libraries/microsoft-agents-storage-blob/setup.py @@ -1,10 +1,15 @@ from os import environ from setuptools import setup +from pathlib import Path +this_directory = Path(__file__).parent +long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, + long_description=long_description, + long_description_content_type='text/markdown', install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "azure-core", From e93e0340d8eb885e05fca555244db929c4f92be9 Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Wed, 8 Oct 2025 16:39:24 -0700 Subject: [PATCH 03/10] Fix formatting in setup.py by removing unnecessary line breaks in setup parameters --- libraries/microsoft-agents-activity/setup.py | 4 ++-- libraries/microsoft-agents-authentication-msal/setup.py | 2 +- libraries/microsoft-agents-copilotstudio-client/setup.py | 4 ++-- libraries/microsoft-agents-hosting-aiohttp/setup.py | 2 +- libraries/microsoft-agents-hosting-core/setup.py | 2 +- libraries/microsoft-agents-hosting-teams/setup.py | 2 +- libraries/microsoft-agents-storage-blob/setup.py | 2 +- libraries/microsoft-agents-storage-cosmos/setup.py | 2 +- 8 files changed, 10 insertions(+), 10 deletions(-) diff --git a/libraries/microsoft-agents-activity/setup.py b/libraries/microsoft-agents-activity/setup.py index fdc62fbe..81d0d780 100644 --- a/libraries/microsoft-agents-activity/setup.py +++ b/libraries/microsoft-agents-activity/setup.py @@ -10,5 +10,5 @@ setup( version=package_version, long_description=long_description, - long_description_content_type='text/markdown' -) \ No newline at end of file + long_description_content_type="text/markdown", +) diff --git a/libraries/microsoft-agents-authentication-msal/setup.py b/libraries/microsoft-agents-authentication-msal/setup.py index 63a68f83..432f24a9 100644 --- a/libraries/microsoft-agents-authentication-msal/setup.py +++ b/libraries/microsoft-agents-authentication-msal/setup.py @@ -9,7 +9,7 @@ setup( version=package_version, long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "msal>=1.31.1", diff --git a/libraries/microsoft-agents-copilotstudio-client/setup.py b/libraries/microsoft-agents-copilotstudio-client/setup.py index 93fb6897..5aa2826c 100644 --- a/libraries/microsoft-agents-copilotstudio-client/setup.py +++ b/libraries/microsoft-agents-copilotstudio-client/setup.py @@ -11,7 +11,7 @@ version=package_version, install_requires=[ f"microsoft-agents-hosting-core=={package_version}", - ], + ], long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", ) diff --git a/libraries/microsoft-agents-hosting-aiohttp/setup.py b/libraries/microsoft-agents-hosting-aiohttp/setup.py index bdb3a65c..8308fd4c 100644 --- a/libraries/microsoft-agents-hosting-aiohttp/setup.py +++ b/libraries/microsoft-agents-hosting-aiohttp/setup.py @@ -10,7 +10,7 @@ setup( version=package_version, long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "aiohttp>=3.11.11", diff --git a/libraries/microsoft-agents-hosting-core/setup.py b/libraries/microsoft-agents-hosting-core/setup.py index a58ff8f7..f1f17e14 100644 --- a/libraries/microsoft-agents-hosting-core/setup.py +++ b/libraries/microsoft-agents-hosting-core/setup.py @@ -10,7 +10,7 @@ setup( version=package_version, long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-activity=={package_version}", "pyjwt>=2.10.1", diff --git a/libraries/microsoft-agents-hosting-teams/setup.py b/libraries/microsoft-agents-hosting-teams/setup.py index bf3f31df..27359ae7 100644 --- a/libraries/microsoft-agents-hosting-teams/setup.py +++ b/libraries/microsoft-agents-hosting-teams/setup.py @@ -9,7 +9,7 @@ setup( version=package_version, long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "aiohttp>=3.11.11", diff --git a/libraries/microsoft-agents-storage-blob/setup.py b/libraries/microsoft-agents-storage-blob/setup.py index 7d413978..d58e3d90 100644 --- a/libraries/microsoft-agents-storage-blob/setup.py +++ b/libraries/microsoft-agents-storage-blob/setup.py @@ -9,7 +9,7 @@ setup( version=package_version, long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "azure-core", diff --git a/libraries/microsoft-agents-storage-cosmos/setup.py b/libraries/microsoft-agents-storage-cosmos/setup.py index 61a04939..70878876 100644 --- a/libraries/microsoft-agents-storage-cosmos/setup.py +++ b/libraries/microsoft-agents-storage-cosmos/setup.py @@ -9,7 +9,7 @@ setup( version=package_version, long_description=long_description, - long_description_content_type='text/markdown', + long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "azure-core", From 476b49c27914a307a23a18c8108596f923ef1006 Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Thu, 9 Oct 2025 10:30:07 -0700 Subject: [PATCH 04/10] Remove long description from setup.py and ensure readme is specified in pyproject.toml for PyPi compatibility Lean into the newer PEP 621 metadata approach we're already using, rather than using the older long_description approach. --- libraries/microsoft-agents-activity/pyproject.toml | 1 + libraries/microsoft-agents-activity/setup.py | 6 ------ 2 files changed, 1 insertion(+), 6 deletions(-) diff --git a/libraries/microsoft-agents-activity/pyproject.toml b/libraries/microsoft-agents-activity/pyproject.toml index 2369332e..8f0de204 100644 --- a/libraries/microsoft-agents-activity/pyproject.toml +++ b/libraries/microsoft-agents-activity/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-activity" dynamic = ["version"] description = "A protocol library for Microsoft Agents" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-activity/setup.py b/libraries/microsoft-agents-activity/setup.py index 81d0d780..9b6de9af 100644 --- a/libraries/microsoft-agents-activity/setup.py +++ b/libraries/microsoft-agents-activity/setup.py @@ -1,14 +1,8 @@ from os import environ from setuptools import setup -from pathlib import Path - -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, - long_description=long_description, - long_description_content_type="text/markdown", ) From d3c2e421d2a62b88fac83195cec8fd95f2ff5586 Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Thu, 9 Oct 2025 10:43:13 -0700 Subject: [PATCH 05/10] Add readme specification to pyproject.toml for all libraries and remove long description from setup.py --- .../microsoft-agents-authentication-msal/pyproject.toml | 1 + libraries/microsoft-agents-authentication-msal/setup.py | 5 ----- .../microsoft-agents-copilotstudio-client/pyproject.toml | 1 + libraries/microsoft-agents-copilotstudio-client/setup.py | 5 ----- libraries/microsoft-agents-hosting-aiohttp/pyproject.toml | 1 + libraries/microsoft-agents-hosting-aiohttp/setup.py | 6 ------ libraries/microsoft-agents-hosting-core/pyproject.toml | 1 + libraries/microsoft-agents-hosting-core/setup.py | 6 ------ libraries/microsoft-agents-hosting-teams/pyproject.toml | 1 + libraries/microsoft-agents-hosting-teams/setup.py | 5 ----- libraries/microsoft-agents-storage-blob/pyproject.toml | 1 + libraries/microsoft-agents-storage-blob/setup.py | 5 ----- libraries/microsoft-agents-storage-cosmos/pyproject.toml | 1 + libraries/microsoft-agents-storage-cosmos/setup.py | 5 ----- 14 files changed, 7 insertions(+), 37 deletions(-) diff --git a/libraries/microsoft-agents-authentication-msal/pyproject.toml b/libraries/microsoft-agents-authentication-msal/pyproject.toml index 40bf4054..975acd23 100644 --- a/libraries/microsoft-agents-authentication-msal/pyproject.toml +++ b/libraries/microsoft-agents-authentication-msal/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-authentication-msal" dynamic = ["version", "dependencies"] description = "A msal-based authentication library for Microsoft Agents" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-authentication-msal/setup.py b/libraries/microsoft-agents-authentication-msal/setup.py index 432f24a9..67315bcf 100644 --- a/libraries/microsoft-agents-authentication-msal/setup.py +++ b/libraries/microsoft-agents-authentication-msal/setup.py @@ -1,15 +1,10 @@ from os import environ from setuptools import setup -from pathlib import Path -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, - long_description=long_description, - long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "msal>=1.31.1", diff --git a/libraries/microsoft-agents-copilotstudio-client/pyproject.toml b/libraries/microsoft-agents-copilotstudio-client/pyproject.toml index 4f08f714..f8b58794 100644 --- a/libraries/microsoft-agents-copilotstudio-client/pyproject.toml +++ b/libraries/microsoft-agents-copilotstudio-client/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-copilotstudio-client" dynamic = ["version", "dependencies"] description = "A client library for Microsoft Agents" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-copilotstudio-client/setup.py b/libraries/microsoft-agents-copilotstudio-client/setup.py index 5aa2826c..ee35a48c 100644 --- a/libraries/microsoft-agents-copilotstudio-client/setup.py +++ b/libraries/microsoft-agents-copilotstudio-client/setup.py @@ -1,9 +1,6 @@ from os import environ from setuptools import setup -from pathlib import Path -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") @@ -12,6 +9,4 @@ install_requires=[ f"microsoft-agents-hosting-core=={package_version}", ], - long_description=long_description, - long_description_content_type="text/markdown", ) diff --git a/libraries/microsoft-agents-hosting-aiohttp/pyproject.toml b/libraries/microsoft-agents-hosting-aiohttp/pyproject.toml index 7b93ca9e..c03beb28 100644 --- a/libraries/microsoft-agents-hosting-aiohttp/pyproject.toml +++ b/libraries/microsoft-agents-hosting-aiohttp/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-hosting-aiohttp" dynamic = ["version", "dependencies"] description = "Integration library for Microsoft Agents with aiohttp" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-hosting-aiohttp/setup.py b/libraries/microsoft-agents-hosting-aiohttp/setup.py index 8308fd4c..426735ba 100644 --- a/libraries/microsoft-agents-hosting-aiohttp/setup.py +++ b/libraries/microsoft-agents-hosting-aiohttp/setup.py @@ -1,16 +1,10 @@ from os import environ from setuptools import setup -from pathlib import Path - -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, - long_description=long_description, - long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "aiohttp>=3.11.11", diff --git a/libraries/microsoft-agents-hosting-core/pyproject.toml b/libraries/microsoft-agents-hosting-core/pyproject.toml index ba13b835..de440380 100644 --- a/libraries/microsoft-agents-hosting-core/pyproject.toml +++ b/libraries/microsoft-agents-hosting-core/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-hosting-core" dynamic = ["version", "dependencies"] description = "Core library for Microsoft Agents" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-hosting-core/setup.py b/libraries/microsoft-agents-hosting-core/setup.py index f1f17e14..e7604116 100644 --- a/libraries/microsoft-agents-hosting-core/setup.py +++ b/libraries/microsoft-agents-hosting-core/setup.py @@ -1,16 +1,10 @@ from os import environ from setuptools import setup -from pathlib import Path - -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, - long_description=long_description, - long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-activity=={package_version}", "pyjwt>=2.10.1", diff --git a/libraries/microsoft-agents-hosting-teams/pyproject.toml b/libraries/microsoft-agents-hosting-teams/pyproject.toml index 5009cf7b..37226745 100644 --- a/libraries/microsoft-agents-hosting-teams/pyproject.toml +++ b/libraries/microsoft-agents-hosting-teams/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-hosting-teams" dynamic = ["version", "dependencies"] description = "Integration library for Microsoft Agents with Teams" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-hosting-teams/setup.py b/libraries/microsoft-agents-hosting-teams/setup.py index 27359ae7..426735ba 100644 --- a/libraries/microsoft-agents-hosting-teams/setup.py +++ b/libraries/microsoft-agents-hosting-teams/setup.py @@ -1,15 +1,10 @@ from os import environ from setuptools import setup -from pathlib import Path -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, - long_description=long_description, - long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "aiohttp>=3.11.11", diff --git a/libraries/microsoft-agents-storage-blob/pyproject.toml b/libraries/microsoft-agents-storage-blob/pyproject.toml index efac60ec..a9427e03 100644 --- a/libraries/microsoft-agents-storage-blob/pyproject.toml +++ b/libraries/microsoft-agents-storage-blob/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-storage-blob" dynamic = ["version", "dependencies"] description = "A blob storage library for Microsoft Agents" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-storage-blob/setup.py b/libraries/microsoft-agents-storage-blob/setup.py index d58e3d90..d38c25bb 100644 --- a/libraries/microsoft-agents-storage-blob/setup.py +++ b/libraries/microsoft-agents-storage-blob/setup.py @@ -1,15 +1,10 @@ from os import environ from setuptools import setup -from pathlib import Path -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, - long_description=long_description, - long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "azure-core", diff --git a/libraries/microsoft-agents-storage-cosmos/pyproject.toml b/libraries/microsoft-agents-storage-cosmos/pyproject.toml index 59d22815..692a6c82 100644 --- a/libraries/microsoft-agents-storage-cosmos/pyproject.toml +++ b/libraries/microsoft-agents-storage-cosmos/pyproject.toml @@ -6,6 +6,7 @@ build-backend = "setuptools.build_meta" name = "microsoft-agents-storage-cosmos" dynamic = ["version", "dependencies"] description = "A Cosmos DB storage library for Microsoft Agents" +readme = {file = "readme.md", content-type = "text/markdown"} authors = [{name = "Microsoft Corporation"}] requires-python = ">=3.9" classifiers = [ diff --git a/libraries/microsoft-agents-storage-cosmos/setup.py b/libraries/microsoft-agents-storage-cosmos/setup.py index 70878876..8f09a0d2 100644 --- a/libraries/microsoft-agents-storage-cosmos/setup.py +++ b/libraries/microsoft-agents-storage-cosmos/setup.py @@ -1,15 +1,10 @@ from os import environ from setuptools import setup -from pathlib import Path -this_directory = Path(__file__).parent -long_description = (this_directory / "readme.md").read_text() package_version = environ.get("PackageVersion", "0.0.0") setup( version=package_version, - long_description=long_description, - long_description_content_type="text/markdown", install_requires=[ f"microsoft-agents-hosting-core=={package_version}", "azure-core", From be73c21025d5955a94146542335b799b36d4335a Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Thu, 9 Oct 2025 10:55:54 -0700 Subject: [PATCH 06/10] Revert setup.py changes - restore to main branch state --- libraries/microsoft-agents-copilotstudio-client/setup.py | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/microsoft-agents-copilotstudio-client/setup.py b/libraries/microsoft-agents-copilotstudio-client/setup.py index ee35a48c..41a7da0d 100644 --- a/libraries/microsoft-agents-copilotstudio-client/setup.py +++ b/libraries/microsoft-agents-copilotstudio-client/setup.py @@ -1,7 +1,6 @@ from os import environ from setuptools import setup - package_version = environ.get("PackageVersion", "0.0.0") setup( From 7e912aef9ca89444f45e7a9396068a84b2313f7f Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Thu, 9 Oct 2025 11:54:37 -0700 Subject: [PATCH 07/10] Enhance README files across multiple libraries to include comprehensive SDK descriptions, package overviews, and sample application links for improved developer guidance. --- libraries/microsoft-agents-activity/readme.md | 50 ++++++++++++++++--- .../readme.md | 43 +++++++++++++--- .../readme.md | 44 +++++++++++++--- .../readme.md | 44 +++++++++++++--- .../microsoft-agents-hosting-core/readme.md | 45 ++++++++++++++--- .../microsoft-agents-hosting-teams/readme.md | 45 ++++++++++++++--- .../microsoft-agents-storage-blob/readme.md | 43 +++++++++++++--- .../microsoft-agents-storage-cosmos/readme.md | 44 +++++++++++++--- 8 files changed, 300 insertions(+), 58 deletions(-) diff --git a/libraries/microsoft-agents-activity/readme.md b/libraries/microsoft-agents-activity/readme.md index c30149c7..2b3facc2 100644 --- a/libraries/microsoft-agents-activity/readme.md +++ b/libraries/microsoft-agents-activity/readme.md @@ -4,9 +4,38 @@ Core types and schemas for building conversational AI agents that work across Microsoft 365 platforms like Teams, Copilot Studio, and Webchat. -## What is this? +# What is this? -This library provides the fundamental building blocks for agent communication - think of it as the "language" that agents use to talk to each other and to users. An Activity is basically a message, event, or action in a conversation. +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | + +## Architecture + +The SDK follows a modular architecture: +- **Activity Layer**: Protocol definitions for cross-platform messaging +- **Hosting Layer**: Core agent lifecycle, middleware, and web hosting +- **Storage Layer**: Persistent state management with Azure backends +- **Authentication Layer**: Secure identity and token management +- **Integration Layer**: Platform-specific adapters (Teams, Copilot Studio) ## Installation @@ -104,10 +133,19 @@ The library supports different types of communication: ✅ **Cross-platform** - Works across all Microsoft 365 chat platforms ✅ **Migration friendly** - Easy upgrade from Bot Framework -## Need Help? +# Quick Links -- 📖 [Full SDK Documentation](https://aka.ms/agents) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -- 💬 [Ask Questions](https://stackoverflow.com/questions/tagged/microsoft-agents) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file diff --git a/libraries/microsoft-agents-authentication-msal/readme.md b/libraries/microsoft-agents-authentication-msal/readme.md index 5d585e1d..61f800ff 100644 --- a/libraries/microsoft-agents-authentication-msal/readme.md +++ b/libraries/microsoft-agents-authentication-msal/readme.md @@ -2,11 +2,31 @@ [![PyPI version](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) -MSAL-based authentication library for Microsoft 365 Agents SDK. Handles Azure AD authentication with support for client secrets, certificates, and managed identities. +Provides secure authentication for your agents using Microsoft Authentication Library (MSAL). It handles getting tokens from Azure AD so your agent can securely communicate with Microsoft services like Teams, Graph API, and other Azure resources. -## What is this? +# What is this? -This library provides secure authentication for your agents using Microsoft Authentication Library (MSAL). It handles getting tokens from Azure AD so your agent can securely communicate with Microsoft services like Teams, Graph API, and other Azure resources. +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | ## Installation @@ -218,10 +238,19 @@ token = await auth.get_access_token("https://graph.microsoft.com") - Regularly rotate client secrets and certificates - Use least-privilege principle for scopes and permissions -## Need Help? +# Quick Links -- 📖 [Full SDK Documentation](https://aka.ms/agents) -- 🔐 [Azure AD App Registration Guide](https://docs.microsoft.com/azure/active-directory/develop/quickstart-register-app) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file diff --git a/libraries/microsoft-agents-copilotstudio-client/readme.md b/libraries/microsoft-agents-copilotstudio-client/readme.md index 9a0c4806..73afbfa9 100644 --- a/libraries/microsoft-agents-copilotstudio-client/readme.md +++ b/libraries/microsoft-agents-copilotstudio-client/readme.md @@ -4,10 +4,32 @@ The Copilot Studio Client is for connecting to and interacting with agents created in Microsoft Copilot Studio. This library allows you to integrate Copilot Studio agents into your Python applications. -## What is this? - This client library provides a direct connection to Copilot Studio agents, bypassing traditional chat channels. It's perfect for integrating AI conversations into your applications, building custom UIs, or creating agent-to-agent communication flows. +# What is this? +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | + + ## Installation ```bash @@ -300,11 +322,19 @@ async def console_chat(): - Access to Microsoft Power Platform environment - Published Copilot Studio agent -## Need Help? +# Quick Links -- 📖 [Copilot Studio Documentation](https://docs.microsoft.com/power-virtual-agents/) -- 🔧 [Power Platform Admin Center](https://admin.powerplatform.microsoft.com/) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -- 💬 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples/copilot_studio_client_sample) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-aiohttp/readme.md b/libraries/microsoft-agents-hosting-aiohttp/readme.md index 7ddb5d75..9a0c5d98 100644 --- a/libraries/microsoft-agents-hosting-aiohttp/readme.md +++ b/libraries/microsoft-agents-hosting-aiohttp/readme.md @@ -4,10 +4,32 @@ Integration library for hosting Microsoft 365 Agents using aiohttp. This library provides HTTP adapters, middleware, and utilities for building web-based agent applications with the popular aiohttp framework. -## What is this? - This library bridges the Microsoft 365 Agents SDK with aiohttp, allowing you to create HTTP endpoints that handle agent conversations. It provides everything you need to host agents as web services, including request processing, authentication, and routing. +# What is this? + +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | + ## Installation ```bash @@ -345,11 +367,19 @@ asyncio.run(test_agent()) 4. **Structure routes** logically for agent communication 5. **Test thoroughly** with both unit and integration tests -## Need Help? +# Quick Links -- 📖 [Full SDK Documentation](https://aka.ms/agents) -- 🌐 [aiohttp Documentation](https://docs.aiohttp.org/) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-core/readme.md b/libraries/microsoft-agents-hosting-core/readme.md index 96214294..3d8fc4f9 100644 --- a/libraries/microsoft-agents-hosting-core/readme.md +++ b/libraries/microsoft-agents-hosting-core/readme.md @@ -4,10 +4,32 @@ The core hosting library for Microsoft 365 Agents SDK. This library provides the fundamental building blocks for creating conversational AI agents, including activity processing, state management, authentication, and channel communication. -## What is this? - This is the heart of the Microsoft 365 Agents SDK - think of it as the engine that powers your conversational agents. It handles the complex orchestration of conversations, manages state across turns, and provides the infrastructure needed to build production-ready agents that work across Microsoft 365 platforms. +# What is this? +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | + + ## Installation ```bash @@ -562,12 +584,19 @@ async def resilient_handler(context: TurnContext, state: TurnState): | `MemoryStorage` | `MemoryStorage` | | `MessageFactory` | `MessageFactory` | -## Need Help? +# Quick Links -- 📖 [Full SDK Documentation](https://aka.ms/agents) -- 🏗️ [Architecture Guide](https://docs.microsoft.com/azure/bot-service/bot-builder-concept-activity-processing) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) -- 📚 [Bot Framework Migration Guide](https://docs.microsoft.com/azure/bot-service/migration/) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-teams/readme.md b/libraries/microsoft-agents-hosting-teams/readme.md index 64f312a0..9c1a66e0 100644 --- a/libraries/microsoft-agents-hosting-teams/readme.md +++ b/libraries/microsoft-agents-hosting-teams/readme.md @@ -4,10 +4,32 @@ Integration library for building Microsoft Teams agents using the Microsoft 365 Agents SDK. This library provides specialized handlers and utilities for Teams-specific functionality like messaging extensions, task modules, adaptive cards, and meeting events. -## What is this? - This library extends the core hosting capabilities with Teams-specific features. It handles Teams' unique interaction patterns like messaging extensions, tab applications, task modules, and meeting integrations. Think of it as the bridge that makes your agent "Teams-native" rather than just a generic chatbot. +# What is this? +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | + + ## Installation ```bash @@ -579,12 +601,19 @@ async def test_teams_member_added(): | `MessagingExtensionQuery` | `MessagingExtensionQuery` | | `TaskModuleRequest` | `TaskModuleRequest` | -## Need Help? +# Quick Links -- 📖 [Teams Platform Documentation](https://docs.microsoft.com/microsoftteams/platform/) -- 🏗️ [Teams App Development](https://docs.microsoft.com/microsoftteams/platform/bots/what-are-bots) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -- 💡 [Teams Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples/teams_agent) -- 📋 [Teams App Manifest Reference](https://docs.microsoft.com/microsoftteams/platform/resources/schema/manifest-schema) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. \ No newline at end of file +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file diff --git a/libraries/microsoft-agents-storage-blob/readme.md b/libraries/microsoft-agents-storage-blob/readme.md index ac7cc8e8..2aa60f25 100644 --- a/libraries/microsoft-agents-storage-blob/readme.md +++ b/libraries/microsoft-agents-storage-blob/readme.md @@ -4,10 +4,31 @@ Azure Blob Storage integration for Microsoft 365 Agents SDK. This library provides persistent storage for conversation state, user data, and custom agent information using Azure Blob Storage. -## What is this? - This library implements the storage interface for the Microsoft 365 Agents SDK using Azure Blob Storage as the backend. It enables your agents to persist conversation state, user preferences, and custom data across sessions. Perfect for production deployments where you need reliable, scalable cloud storage. +# What is this? +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | + ## Installation ```bash @@ -519,11 +540,19 @@ except Exception as e: - **`BlobStorageConfig`** - Configuration settings for connection and authentication - **`StoreItem`** - Base class for data models (inherit to create custom types) -## Need Help? +# Quick Links -- 📖 [Azure Blob Storage Documentation](https://docs.microsoft.com/azure/storage/blobs/) -- 🔐 [Azure Identity Documentation](https://docs.microsoft.com/python/api/azure-identity/) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations diff --git a/libraries/microsoft-agents-storage-cosmos/readme.md b/libraries/microsoft-agents-storage-cosmos/readme.md index 217c698e..a04e34cb 100644 --- a/libraries/microsoft-agents-storage-cosmos/readme.md +++ b/libraries/microsoft-agents-storage-cosmos/readme.md @@ -4,10 +4,31 @@ Azure Cosmos DB storage integration for Microsoft 365 Agents SDK. This library provides enterprise-grade persistent storage for conversation state, user data, and custom agent information using Azure Cosmos DB's globally distributed, multi-model database service. -## What is this? - This library implements the storage interface for the Microsoft 365 Agents SDK using Azure Cosmos DB as the backend. It provides automatic partitioning, global distribution, and low-latency access to your agent data. Perfect for production deployments requiring high availability, scalability, and multi-region support. +# What is this? +This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio. + +## Packages Overview + +We offer the following PyPI packages to create conversational experiences based on Agents: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-activity` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-activity)](https://pypi.org/project/microsoft-agents-activity/) | Types and validators implementing the Activity protocol spec. | +| `microsoft-agents-hosting-core` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-core)](https://pypi.org/project/microsoft-agents-hosting-core/) | Core library for Microsoft Agents hosting. | +| `microsoft-agents-hosting-aiohttp` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-aiohttp)](https://pypi.org/project/microsoft-agents-hosting-aiohttp/) | Configures aiohttp to run the Agent. | +| `microsoft-agents-hosting-teams` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-hosting-teams)](https://pypi.org/project/microsoft-agents-hosting-teams/) | Provides classes to host an Agent for Teams. | +| `microsoft-agents-storage-blob` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-blob)](https://pypi.org/project/microsoft-agents-storage-blob/) | Extension to use Azure Blob as storage. | +| `microsoft-agents-storage-cosmos` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-storage-cosmos)](https://pypi.org/project/microsoft-agents-storage-cosmos/) | Extension to use CosmosDB as storage. | +| `microsoft-agents-authentication-msal` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-authentication-msal)](https://pypi.org/project/microsoft-agents-authentication-msal/) | MSAL-based authentication for Microsoft Agents. | + +Additionally we provide a Copilot Studio Client, to interact with Agents created in CopilotStudio: + +| Package Name | PyPI Version | Description | +|--------------|-------------|-------------| +| `microsoft-agents-copilotstudio-client` | [![PyPI](https://img.shields.io/pypi/v/microsoft-agents-copilotstudio-client)](https://pypi.org/project/microsoft-agents-copilotstudio-client/) | Direct to Engine client to interact with Agents created in CopilotStudio | + **Why Cosmos DB?** - 🌍 Global distribution with multi-region writes - ⚡ Single-digit millisecond latency @@ -763,12 +784,19 @@ print(f"Connected! Databases: {len(list(database_list))}") - **`CosmosDBStorageConfig`** - Configuration settings for connection and behavior - **`StoreItem`** - Base class for data models (inherit to create custom types) -## Need Help? +# Quick Links -- 📖 [Azure Cosmos DB Documentation](https://docs.microsoft.com/azure/cosmos-db/) -- 🔐 [Azure Identity Documentation](https://docs.microsoft.com/python/api/azure-identity/) -- ⚡ [Cosmos DB Best Practices](https://docs.microsoft.com/azure/cosmos-db/performance-tips) +- 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) +- 📖 [Complete Documentation](https://aka.ms/agents) +- 💡 [Python Samples Repository](https://github.com/microsoft/Agents/tree/main/samples/python) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) -- 💡 [Sample Applications](https://github.com/microsoft/Agents-for-python/tree/main/test_samples) -Part of the [Microsoft 365 Agents SDK](https://github.com/microsoft/Agents-for-python) family. +# Sample Applications + +Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): +- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards +- **Copilot Studio Integration**: Connect to Copilot Studio agents +- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms +- **Authentication Flows**: OAuth, MSAL, and token management examples +- **State Management**: Conversation and user state with Azure storage +- **Streaming Responses**: Real-time agent responses with citations From c224b72fbd04a535188a2b4a44c38ee92f941d89 Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Fri, 10 Oct 2025 13:54:45 -0700 Subject: [PATCH 08/10] Review all sample code, remove AI written sample code. Simplify readme's for maintainability. --- libraries/microsoft-agents-activity/readme.md | 110 ++- .../readme.md | 204 +---- .../readme.md | 261 +------ .../readme.md | 331 +------- .../microsoft-agents-hosting-core/readme.md | 530 +------------ .../microsoft-agents-hosting-teams/readme.md | 552 +------------ .../microsoft-agents-storage-blob/readme.md | 461 +---------- .../microsoft-agents-storage-cosmos/readme.md | 729 +----------------- 8 files changed, 211 insertions(+), 2967 deletions(-) diff --git a/libraries/microsoft-agents-activity/readme.md b/libraries/microsoft-agents-activity/readme.md index 2b3facc2..393c582c 100644 --- a/libraries/microsoft-agents-activity/readme.md +++ b/libraries/microsoft-agents-activity/readme.md @@ -44,74 +44,55 @@ pip install microsoft-agents-activity ``` ## Quick Start - +Code below taken from the [Quick Start](https://github.com/microsoft/Agents/tree/main/samples/python/quickstart) sample. ```python -from microsoft_agents.activity import Activity, ActivityTypes +@AGENT_APP.conversation_update("membersAdded") +async def on_members_added(context: TurnContext, _state: TurnState): + await context.send_activity( + "Welcome to the empty agent! " + "This agent is designed to be a starting point for your own agent development." + ) + return True + -# Send a simple message -activity = Activity( - type=ActivityTypes.message, - text="Hello! How can I help you today?" -) +@AGENT_APP.message(re.compile(r"^hello$")) +async def on_hello(context: TurnContext, _state: TurnState): + await context.send_activity("Hello!") -# Create a reply -reply = activity.create_reply("Thanks for reaching out!") -# Show typing indicator -typing = Activity.create_typing_activity() +@AGENT_APP.activity("message") +async def on_message(context: TurnContext, _state: TurnState): + await context.send_activity(f"you said: {context.activity.text}") ``` ## Common Use Cases ### Rich Messages with Cards +Code below taken from the [Cards](https://github.com/microsoft/Agents/tree/main/samples/python/cards) sample. -```python -from microsoft_agents.activity import HeroCard, CardAction, Attachment - -# Create a welcome card -card = HeroCard( - title="Welcome to our service!", - subtitle="Let's get you started", - text="Choose an option below to continue", - actions=[ - CardAction(type="imBack", title="Get Help", value="help"), - CardAction(type="imBack", title="View Profile", value="profile") - ] -) - -activity = Activity( - type=ActivityTypes.message, - text="Welcome!", - attachments=[Attachment( - content_type="application/vnd.microsoft.card.hero", - content=card - )] -) -``` - -### Handle @Mentions - -```python -from microsoft_agents.activity import Mention, ChannelAccount - -# Check for mentions in incoming messages -mentions = activity.get_mentions() -for mention in mentions: - mentioned_user = mention.mentioned - print(f"User {mentioned_user.name} was mentioned") -``` - -### Teams Integration ```python -from microsoft_agents.activity.teams import TeamsChannelAccount - -# Work with Teams-specific user info -teams_user = TeamsChannelAccount( - id="user123", - name="John Doe", - user_principal_name="john.doe@company.com" -) + @staticmethod + async def send_animation_card(context: TurnContext): + card = CardFactory.animation_card( + AnimationCard( + title="Microsoft Agents Framework", + image=ThumbnailUrl( + url="https://i.giphy.com/Ki55RUbOV5njy.gif", alt="Cute Robot" + ), + media=[MediaUrl(url="https://i.giphy.com/Ki55RUbOV5njy.gif")], + subtitle="Animation Card", + text="This is an example of an animation card using a gif.", + aspect="16:9", + duration="PT2M", + ) + ) + await CardMessages.send_activity(context, card) + + @staticmethod + async def send_activity(context: TurnContext, card: Attachment): + activity = Activity(type=ActivityTypes.message, attachments=[card]) + await context.send_activity(activity) ``` ## Activity Types @@ -141,11 +122,14 @@ The library supports different types of communication: - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) # Sample Applications - Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file + +|Name|Description|README| +|----|----|----| +|Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)| +|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)| +|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)| +|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)| +|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)| +|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)| +|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)| \ No newline at end of file diff --git a/libraries/microsoft-agents-authentication-msal/readme.md b/libraries/microsoft-agents-authentication-msal/readme.md index 61f800ff..9395a26e 100644 --- a/libraries/microsoft-agents-authentication-msal/readme.md +++ b/libraries/microsoft-agents-authentication-msal/readme.md @@ -38,169 +38,49 @@ pip install microsoft-agents-authentication-msal ### Basic Setup with Client Secret +Define your client secrets in the ENV file ```python -from microsoft_agents.authentication.msal import MsalAuth -from microsoft_agents.hosting.core import AgentAuthConfiguration, AuthTypes - -# Configure authentication -config = AgentAuthConfiguration( - AUTH_TYPE=AuthTypes.client_secret, - TENANT_ID="your-tenant-id", - CLIENT_ID="your-client-id", - CLIENT_SECRET="your-client-secret" -) - -# Create auth provider -auth = MsalAuth(config) - -# Get access token -token = await auth.get_access_token( - resource_url="https://graph.microsoft.com", - scopes=["https://graph.microsoft.com/.default"] -) +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=client-id +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=client-secret +CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=tenant-id ``` -### Using with Connection Manager +Load the Configuration (Code from [main.py Quickstart Sample](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/src/main.py)) ```python -from microsoft_agents.authentication.msal import MsalConnectionManager - -# Load from environment variables -connection_manager = MsalConnectionManager(**agents_sdk_config) - -# Use with hosting adapter -from microsoft_agents.hosting.aiohttp import CloudAdapter -adapter = CloudAdapter(connection_manager=connection_manager) -``` +from .start_server import start_server -## Authentication Types - -### 1. Client Secret (Most Common) -```python -config = AgentAuthConfiguration( - AUTH_TYPE=AuthTypes.client_secret, - TENANT_ID="your-tenant-id", - CLIENT_ID="your-app-id", - CLIENT_SECRET="your-secret" +start_server( + agent_application=AGENT_APP, + auth_configuration=CONNECTION_MANAGER.get_default_connection_configuration(), ) ``` +Then start the Agent (code snipped from (start_server.py Quickstart Sample](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/src/start_server.py)): -### 2. Certificate-based ```python -config = AgentAuthConfiguration( - AUTH_TYPE=AuthTypes.client_certificate, - TENANT_ID="your-tenant-id", - CLIENT_ID="your-app-id", - CLIENT_CERTIFICATE_PATH="/path/to/cert.pem", - CLIENT_CERTIFICATE_PRIVATE_KEY_PATH="/path/to/key.pem" -) +def start_server( + agent_application: AgentApplication, auth_configuration: AgentAuthConfiguration +): + async def entry_point(req: Request) -> Response: + agent: AgentApplication = req.app["agent_app"] + adapter: CloudAdapter = req.app["adapter"] + return await start_agent_process( + req, + agent, + adapter, + ) +[...] ``` -### 3. Managed Identity (Azure hosting) -```python -# System-assigned managed identity -config = AgentAuthConfiguration( - AUTH_TYPE=AuthTypes.system_managed_identity -) - -# User-assigned managed identity -config = AgentAuthConfiguration( - AUTH_TYPE=AuthTypes.user_managed_identity, - CLIENT_ID="managed-identity-client-id" -) -``` - -## Environment Configuration - -Set up your `.env` file: - -```bash -# Basic authentication -CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=your-tenant-id -CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=your-client-id -CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=your-client-secret - -# Multiple connections -CONNECTIONS__GRAPH__SETTINGS__TENANTID=your-tenant-id -CONNECTIONS__GRAPH__SETTINGS__CLIENTID=your-graph-app-id -CONNECTIONS__GRAPH__SETTINGS__CLIENTSECRET=your-graph-secret -``` - -Load configuration: -```python -from microsoft_agents.activity import load_configuration_from_env -from os import environ - -agents_sdk_config = load_configuration_from_env(environ) -connection_manager = MsalConnectionManager(**agents_sdk_config) -``` - -## Advanced Features - -### On-Behalf-Of (OBO) Flow -```python -# Get token on behalf of user -user_token = await auth.acquire_token_on_behalf_of( - scopes=["https://graph.microsoft.com/User.Read"], - user_assertion="user-jwt-token" -) -``` - -### Agentic Authentication -```python -# For multi-agent scenarios -app_token = await auth.get_agentic_application_token("agent-instance-id") -user_token = await auth.get_agentic_user_token( - "agent-instance-id", - "user@company.com", - ["User.Read"] -) -``` - -### Connection Mapping -```python -# Map different connections to different services -connection_manager = MsalConnectionManager( - connections_map=[ - {"CONNECTION": "GRAPH", "SERVICEURL": "graph.microsoft.com"}, - {"CONNECTION": "TEAMS", "SERVICEURL": "teams.microsoft.com"} - ] -) -``` - -## Common Use Cases - -### Teams Agent -```python -from microsoft_agents.hosting.aiohttp import CloudAdapter - -CONFIG = AgentAuthConfiguration( - AUTH_TYPE=AuthTypes.client_secret, - TENANT_ID=environ.get("TENANT_ID"), - CLIENT_ID=environ.get("CLIENT_ID"), - CLIENT_SECRET=environ.get("CLIENT_SECRET") -) - -connection_manager = MsalConnectionManager( - connections_configurations={"SERVICE_CONNECTION": CONFIG} -) - -adapter = CloudAdapter(connection_manager=connection_manager) -``` - -### Graph API Access +## Authentication Types +The M365 Agents SDK in Python supports the following Auth types: ```python -# Configure for Microsoft Graph -graph_config = AgentAuthConfiguration( - AUTH_TYPE=AuthTypes.client_secret, - TENANT_ID="your-tenant-id", - CLIENT_ID="your-app-id", - CLIENT_SECRET="your-secret", - SCOPES=["https://graph.microsoft.com/.default"] -) - -auth = MsalAuth(graph_config) -token = await auth.get_access_token("https://graph.microsoft.com") +class AuthTypes(str, Enum): + certificate = "certificate" + certificate_subject_name = "CertificateSubjectName" + client_secret = "ClientSecret" + user_managed_identity = "UserManagedIdentity" + system_managed_identity = "SystemManagedIdentity" ``` ## Key Classes @@ -216,22 +96,8 @@ token = await auth.get_access_token("https://graph.microsoft.com") ✅ **Multi-tenant** - Support for different Azure AD tenants ✅ **Agent-to-agent** - Secure communication between agents ✅ **On-behalf-of** - Act on behalf of users -✅ **Scope resolution** - Dynamic scope handling with placeholders - -## Troubleshooting - -### Common Issues - -**Token acquisition failed** -- Verify your client ID, secret, and tenant ID -- Check that your app has the required permissions -- Ensure scopes are correctly formatted - -**Managed Identity not working** -- Verify you're running on Azure with managed identity enabled -- Check that the identity has required permissions -## Security Best Practices +# Security Best Practices - Store secrets in Azure Key Vault or environment variables - Use managed identities when possible (no secrets to manage) @@ -247,10 +113,4 @@ token = await auth.get_access_token("https://graph.microsoft.com") # Sample Applications -Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file +w \ No newline at end of file diff --git a/libraries/microsoft-agents-copilotstudio-client/readme.md b/libraries/microsoft-agents-copilotstudio-client/readme.md index 73afbfa9..1ce21edf 100644 --- a/libraries/microsoft-agents-copilotstudio-client/readme.md +++ b/libraries/microsoft-agents-copilotstudio-client/readme.md @@ -40,126 +40,41 @@ pip install microsoft-agents-copilotstudio-client ### Basic Setup +Code below from the [main.py in the Copilot Studio Client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/src/main.py) ```python -from microsoft_agents.copilotstudio.client import ( - CopilotClient, - ConnectionSettings, - PowerPlatformCloud, - AgentType -) - -# Configure connection to your Copilot Studio agent -settings = ConnectionSettings( - environment_id="your-environment-id", - agent_identifier="your-agent-id", - cloud=PowerPlatformCloud.PROD, - copilot_agent_type=AgentType.PUBLISHED -) - -# Create client with access token -client = CopilotClient(settings, "your-access-token") +def create_client(): + settings = ConnectionSettings( + environment_id=environ.get("COPILOTSTUDIOAGENT__ENVIRONMENTID"), + agent_identifier=environ.get("COPILOTSTUDIOAGENT__SCHEMANAME"), + cloud=None, + copilot_agent_type=None, + custom_power_platform_cloud=None, + ) + token = acquire_token( + settings, + app_client_id=environ.get("COPILOTSTUDIOAGENT__AGENTAPPID"), + tenant_id=environ.get("COPILOTSTUDIOAGENT__TENANTID"), + ) + copilot_client = CopilotClient(settings, token) + return copilot_client ``` ### Start a Conversation +The code below is summarized from the [main.py in the Copilot Studio Client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/src/main.py). See that sample for complete & working code. ```python -import asyncio - -async def chat_with_agent(): - # Initialize conversation - async for activity in client.start_conversation(): - if activity.type == "message": - print(f"Agent: {activity.text}") - - # Send a question - async for activity in client.ask_question("Hello, how can you help me?"): - if activity.type == "message": - print(f"Agent: {activity.text}") - -# Run the conversation -asyncio.run(chat_with_agent()) -``` - -## Configuration Options - -### Connection Settings - -```python -settings = ConnectionSettings( - environment_id="12345678-1234-1234-1234-123456789012", # Required - agent_identifier="your-agent-name", # Required - cloud=PowerPlatformCloud.PROD, # Environment - copilot_agent_type=AgentType.PUBLISHED # Agent version -) -``` - -### Power Platform Clouds - -Choose the appropriate cloud environment: - -```python -from microsoft_agents.copilotstudio.client import PowerPlatformCloud - -# Production (default) -cloud = PowerPlatformCloud.PROD - -# Government clouds -cloud = PowerPlatformCloud.GOV -cloud = PowerPlatformCloud.HIGH -cloud = PowerPlatformCloud.DOD - -# Testing environments -cloud = PowerPlatformCloud.DEV -cloud = PowerPlatformCloud.TEST - -# Custom cloud -settings = ConnectionSettings( - environment_id="your-env-id", - agent_identifier="your-agent-id", - cloud=PowerPlatformCloud.OTHER, - custom_power_platform_cloud="your-custom-cloud.powerplatform.com" -) -``` - -### Agent Types - -```python -from microsoft_agents.copilotstudio.client import AgentType - -# Published agent (default) -agent_type = AgentType.PUBLISHED - -# Prebuilt agent -agent_type = AgentType.PREBUILT -``` - -## Authentication - -### Get Access Token + copilot_client = create_client() + act = copilot_client.start_conversation(True) -You need a valid access token for the Power Platform API: + ... -```python -from msal import PublicClientApplication - -# MSAL configuration -app = PublicClientApplication( - client_id="your-app-client-id", - authority="https://login.microsoftonline.com/your-tenant-id" -) - -# Get token -result = app.acquire_token_interactive( - scopes=["https://api.powerplatform.com/.default"] -) - -if "access_token" in result: - token = result["access_token"] - client = CopilotClient(settings, token) + replies = copilot_client.ask_question("Who are you?", conversation_id) + async for reply in replies: + if reply.type == ActivityTypes.message: + print(f"\n{reply.text}") ``` ### Environment Variables - Set up your `.env` file: ```bash @@ -175,118 +90,6 @@ COPILOT_AGENT_TYPE=PUBLISHED CUSTOM_POWER_PLATFORM_CLOUD=your-custom-cloud.com ``` -## Advanced Usage - -### Send Custom Activities - -```python -from microsoft_agents.activity import Activity, ActivityTypes, ConversationAccount - -# Create custom activity -activity = Activity( - type=ActivityTypes.message, - text="Custom message with metadata", - conversation=ConversationAccount(id="conversation-123"), - value={"customData": "example"} -) - -# Send to agent -async for response in client.ask_question_with_activity(activity): - print(f"Response: {response.text}") -``` - -### Handle Different Activity Types - -```python -async def handle_conversation(): - async for activity in client.start_conversation(): - if activity.type == "message": - print(f"Message: {activity.text}") - - # Check for suggested actions - if activity.suggested_actions: - print("Available actions:") - for action in activity.suggested_actions.actions: - print(f" - {action.title}") - - elif activity.type == "typing": - print("Agent is typing...") - - elif activity.type == "event": - print(f"Event: {activity.name}") -``` - -### Conversation Management - -```python -class ConversationManager: - def __init__(self, client: CopilotClient): - self.client = client - self.conversation_id = None - - async def start_new_conversation(self): - async for activity in self.client.start_conversation(): - if activity.conversation: - self.conversation_id = activity.conversation.id - yield activity - - async def send_message(self, text: str): - async for activity in self.client.ask_question(text, self.conversation_id): - yield activity -``` - -## Integration with Microsoft 365 Agents SDK - -```python -from microsoft_agents.hosting.core import TurnContext, MessageFactory -from microsoft_agents.authentication.msal import MsalAuth - -# In your agent handler -async def handle_copilot_studio_query(context: TurnContext): - # Get OBO token - auth = MsalAuth(your_auth_config) - token = await auth.acquire_token_on_behalf_of( - scopes=["https://api.powerplatform.com/.default"], - user_assertion="user-token" - ) - - # Create Copilot Studio client - client = CopilotClient(settings, token) - - # Forward user message to Copilot Studio - user_message = context.activity.text - async for activity in client.ask_question(user_message): - await context.send_activity(MessageFactory.text(activity.text)) -``` - -## Console Chat Example - -```python -async def console_chat(): - # Start conversation - print("Starting conversation with Copilot Studio agent...") - async for activity in client.start_conversation(): - if activity.type == "message": - print(f"Agent: {activity.text}") - - # Chat loop - while True: - user_input = input("You: ") - if user_input.lower() in ['quit', 'exit']: - break - - async for activity in client.ask_question(user_input): - if activity.type == "message": - print(f"Agent: {activity.text}") -``` - -## Key Classes - -- **`CopilotClient`** - Main client for communicating with Copilot Studio agents -- **`ConnectionSettings`** - Configuration for connecting to your agent -- **`PowerPlatformCloud`** - Enum for different Power Platform environments -- **`AgentType`** - Enum for agent version types (published/prebuilt) - ## Features ✅ **Real-time streaming** - Server-sent events for live responses @@ -331,10 +134,12 @@ async def console_chat(): # Sample Applications -Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file +|Name|Description|README| +|----|----|----| +|Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)| +|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)| +|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)| +|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)| +|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)| +|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)| +|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)| \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-aiohttp/readme.md b/libraries/microsoft-agents-hosting-aiohttp/readme.md index 9a0c5d98..2d397e4c 100644 --- a/libraries/microsoft-agents-hosting-aiohttp/readme.md +++ b/libraries/microsoft-agents-hosting-aiohttp/readme.md @@ -36,314 +36,51 @@ Additionally we provide a Copilot Studio Client, to interact with Agents created pip install microsoft-agents-hosting-aiohttp ``` -## Quick Start - -### Basic Agent Server +## Simple Echo Agent +See the [Quickstart sample](https://github.com/microsoft/Agents/tree/main/samples/python/quickstart) for full working code. ```python -from aiohttp.web import Application, Request, Response, run_app -from microsoft_agents.hosting.aiohttp import ( - CloudAdapter, - jwt_authorization_middleware, - start_agent_process -) -from microsoft_agents.hosting.core import AgentApplication, TurnState, MemoryStorage -from microsoft_agents.authentication.msal import MsalConnectionManager - -# Create your agent application -storage = MemoryStorage() -connection_manager = MsalConnectionManager(**config) -adapter = CloudAdapter(connection_manager=connection_manager) -agent_app = AgentApplication[TurnState]( - storage=storage, - adapter=adapter, - **config -) +agents_sdk_config = load_configuration_from_env(environ) -# Set up message handler -async def messages(req: Request) -> Response: - return await start_agent_process(req, agent_app, adapter) - -# Create aiohttp application -app = Application(middlewares=[jwt_authorization_middleware]) -app.router.add_post("/api/messages", messages) -app["agent_configuration"] = auth_config -app["agent_app"] = agent_app -app["adapter"] = adapter - -# Run the server -run_app(app, host="localhost", port=3978) -``` +STORAGE = MemoryStorage() +CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config) +ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER) +AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config) -### Simple Echo Agent - -```python -from microsoft_agents.hosting.core import AgentApplication, TurnState, TurnContext -from microsoft_agents.hosting.aiohttp import CloudAdapter - -# Create minimal agent -agent_app = AgentApplication[TurnState]( - storage=MemoryStorage(), - adapter=CloudAdapter() +AGENT_APP = AgentApplication[TurnState]( + storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config ) -@agent_app.activity("message") +@AGENT_APP.activity("message") async def on_message(context: TurnContext, state: TurnState): await context.send_activity(f"You said: {context.activity.text}") -# Use the shared start_server helper -from shared import start_server -start_server(agent_app, auth_configuration=None) -``` - -## Core Components - -### CloudAdapter - -The main adapter for processing HTTP requests and converting them to agent activities: - -```python -from microsoft_agents.hosting.aiohttp import CloudAdapter -from microsoft_agents.authentication.msal import MsalConnectionManager - -# Basic setup -adapter = CloudAdapter(connection_manager=connection_manager) - -# With custom error handling -async def custom_error_handler(context, error): - print(f"Error: {error}") - await context.send_activity("Sorry, something went wrong.") - -adapter.on_turn_error = custom_error_handler - -# Process incoming requests -async def handle_messages(request: Request) -> Response: - return await adapter.process(request, your_agent) -``` - -### JWT Authorization Middleware +... -Automatic JWT token validation for secure agent endpoints: - -```python -from microsoft_agents.hosting.aiohttp import ( - jwt_authorization_middleware, - jwt_authorization_decorator -) - -# As middleware (recommended) -app = Application(middlewares=[jwt_authorization_middleware]) - -# As decorator for specific routes -@jwt_authorization_decorator -async def protected_endpoint(request): - claims = request["claims_identity"] - return Response(text=f"Hello {claims.name}") -``` - -### Channel Service Routing - -For agent-to-agent communication: - -```python -from microsoft_agents.hosting.aiohttp import channel_service_route_table - -# Add agent-to-agent routes -app.router.add_routes( - channel_service_route_table(your_agent, "/api/botresponse") +start_server( + agent_application=AGENT_APP, + auth_configuration=CONNECTION_MANAGER.get_default_connection_configuration(), ) ``` -## Authentication Setup - -### Environment Configuration - -```bash -# Required for authentication -CONNECTIONS__SERVICE_CONNECTION__SETTINGS__TENANTID=your-tenant-id -CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTID=your-client-id -CONNECTIONS__SERVICE_CONNECTION__SETTINGS__CLIENTSECRET=your-client-secret -``` - -### Full Setup with Authentication - -```python -import os -from dotenv import load_dotenv -from microsoft_agents.activity import load_configuration_from_env -from microsoft_agents.authentication.msal import MsalConnectionManager -from microsoft_agents.hosting.aiohttp import CloudAdapter -from microsoft_agents.hosting.core import AgentApplication, Authorization - -load_dotenv() -config = load_configuration_from_env(os.environ) - -# Set up authentication and storage -storage = MemoryStorage() -connection_manager = MsalConnectionManager(**config) -adapter = CloudAdapter(connection_manager=connection_manager) -authorization = Authorization(storage, connection_manager, **config) - -# Create authenticated agent application -agent_app = AgentApplication[TurnState]( - storage=storage, - adapter=adapter, - authorization=authorization, - **config -) -``` - -## Advanced Features - -### Streaming Responses - -Support for real-time streaming responses: - -```python -from microsoft_agents.hosting.aiohttp import StreamingResponse, Citation - -# Create streaming response with citations -citations = [Citation(content="Source info", title="Reference")] -response = StreamingResponse(citations=citations) - -# Use in your agent logic -async def stream_response(context: TurnContext): - # Stream data to user in real-time - await context.send_activity("Starting stream...") - # Implementation depends on your streaming needs -``` - -### Custom Middleware - -Add your own middleware for logging, analytics, etc: - -```python -from aiohttp.web import middleware - -@middleware -async def logging_middleware(request, handler): - print(f"Incoming request: {request.method} {request.path}") - response = await handler(request) - print(f"Response status: {response.status}") - return response - -app = Application(middlewares=[ - logging_middleware, - jwt_authorization_middleware -]) -``` ### Error Handling -Customize error responses: - -```python -from microsoft_agents.hosting.core import MessageFactory - -async def custom_error_handler(context: TurnContext, error: Exception): - if isinstance(error, PermissionError): - await context.send_activity( - MessageFactory.text("You don't have permission for this action") - ) - else: - await context.send_activity( - MessageFactory.text("An unexpected error occurred") - ) - - # Send trace for debugging - await context.send_trace_activity( - "Error", str(error), "error", "OnTurnError" - ) - -adapter.on_turn_error = custom_error_handler -``` - -## Integration Patterns - -### Teams Integration - -```python -from microsoft_agents.hosting.teams import TeamsActivityHandler - -class MyTeamsAgent(TeamsActivityHandler): - async def on_message_activity(self, turn_context: TurnContext): - await turn_context.send_activity("Hello from Teams!") - -# Use with CloudAdapter -agent = MyTeamsAgent() -adapter = CloudAdapter(connection_manager=connection_manager) -``` - -### Multi-Agent Systems - -```python -# Agent 1 - Initiator -app.router.add_routes( - channel_service_route_table(agent1, "/api/agent1") -) - -# Agent 2 - Responder -app.router.add_routes( - channel_service_route_table(agent2, "/api/agent2") -) - -# Configure agent communication -from microsoft_agents.hosting.core import HttpAgentChannelFactory - -channel_factory = HttpAgentChannelFactory() -# Set up agent discovery and routing -``` - -### Development vs Production - -```python -# Development - simpler setup -if os.getenv("ENVIRONMENT") == "development": - adapter = CloudAdapter() # Anonymous mode -else: - # Production - full authentication - adapter = CloudAdapter(connection_manager=connection_manager) -``` - -## Testing Your Agent +Customize error responses. Code take from the [Quickstart sample](https://github.com/microsoft/Agents/tree/main/samples/python/quickstart). ```python -import aiohttp -import asyncio - -async def test_agent(): - async with aiohttp.ClientSession() as session: - # Test message - activity = { - "type": "message", - "text": "Hello agent!", - "conversation": {"id": "test-conversation"}, - "from": {"id": "test-user"}, - "recipient": {"id": "test-agent"} - } - - async with session.post( - "http://localhost:3978/api/messages", - json=activity, - headers={"Content-Type": "application/json"} - ) as response: - print(f"Status: {response.status}") - if response.status == 200: - result = await response.json() - print(f"Response: {result}") - -# Run test -asyncio.run(test_agent()) +@AGENT_APP.error +async def on_error(context: TurnContext, error: Exception): + # This check writes out errors to console log + # NOTE: In production environment, you should consider logging this to Azure + # application insights. + print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr) + traceback.print_exc() + + # Send a message to the user + await context.send_activity("The bot encountered an error or bug.") ``` -## Key Classes - -- **`CloudAdapter`** - Main HTTP adapter for processing agent requests -- **`start_agent_process`** - Helper function to start agent processing -- **`jwt_authorization_middleware`** - JWT authentication middleware -- **`channel_service_route_table`** - Routes for agent-to-agent communication -- **`StreamingResponse`** - Support for streaming responses - ## Features ✅ **HTTP hosting** - Full aiohttp integration for web hosting @@ -365,7 +102,6 @@ asyncio.run(test_agent()) 2. **Handle errors gracefully** with custom error handlers 3. **Secure your endpoints** with JWT middleware in production 4. **Structure routes** logically for agent communication -5. **Test thoroughly** with both unit and integration tests # Quick Links @@ -375,11 +111,12 @@ asyncio.run(test_agent()) - 🐛 [Report Issues](https://github.com/microsoft/Agents-for-python/issues) # Sample Applications - -Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file +|Name|Description|README| +|----|----|----| +|Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)| +|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)| +|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)| +|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)| +|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)| +|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)| +|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)| \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-core/readme.md b/libraries/microsoft-agents-hosting-core/readme.md index 3d8fc4f9..86730baa 100644 --- a/libraries/microsoft-agents-hosting-core/readme.md +++ b/libraries/microsoft-agents-hosting-core/readme.md @@ -35,43 +35,31 @@ Additionally we provide a Copilot Studio Client, to interact with Agents created ```bash pip install microsoft-agents-hosting-core ``` +## Simple Echo Agent +See the [Quickstart sample](https://github.com/microsoft/Agents/tree/main/samples/python/quickstart) for full working code. -## Quick Start +```python +agents_sdk_config = load_configuration_from_env(environ) -### Simple Echo Agent +STORAGE = MemoryStorage() +CONNECTION_MANAGER = MsalConnectionManager(**agents_sdk_config) +ADAPTER = CloudAdapter(connection_manager=CONNECTION_MANAGER) +AUTHORIZATION = Authorization(STORAGE, CONNECTION_MANAGER, **agents_sdk_config) -```python -from microsoft_agents.hosting.core import ( - AgentApplication, - TurnState, - TurnContext, - MemoryStorage +AGENT_APP = AgentApplication[TurnState]( + storage=STORAGE, adapter=ADAPTER, authorization=AUTHORIZATION, **agents_sdk_config ) -# Create your agent application -storage = MemoryStorage() -agent_app = AgentApplication[TurnState](storage=storage) - -@agent_app.activity("message") +@AGENT_APP.activity("message") async def on_message(context: TurnContext, state: TurnState): await context.send_activity(f"You said: {context.activity.text}") -# Agent is ready to process messages! -``` - -### Activity Handler Style +... -```python -from microsoft_agents.hosting.core import ActivityHandler, TurnContext - -class MyAgent(ActivityHandler): - async def on_message_activity(self, turn_context: TurnContext): - await turn_context.send_activity("Hello from ActivityHandler!") - - async def on_conversation_update_activity(self, turn_context: TurnContext): - await turn_context.send_activity("Welcome to the conversation!") - -agent = MyAgent() +start_server( + agent_application=AGENT_APP, + auth_configuration=CONNECTION_MANAGER.get_default_connection_configuration(), +) ``` ## Core Concepts @@ -89,465 +77,33 @@ agent = MyAgent() - More familiar to Bot Framework developers - Lower-level control over activity processing -### Turn Context - -Every conversation "turn" (message exchange) gets a `TurnContext` containing: - -```python -async def handle_message(context: TurnContext, state: TurnState): - # The incoming activity (message, event, etc.) - user_message = context.activity.text - - # Send responses - await context.send_activity("Simple text response") - - # Send rich content - from microsoft_agents.hosting.core import MessageFactory - card_activity = MessageFactory.attachment(hero_card) - await context.send_activity(card_activity) - - # Access conversation metadata - conversation_id = context.activity.conversation.id - user_id = context.activity.from_property.id -``` - -### State Management - -Agents need to remember information across conversation turns: - -```python -from microsoft_agents.hosting.core import ( - TurnState, - ConversationState, - UserState, - MemoryStorage -) - -storage = MemoryStorage() -agent_app = AgentApplication[TurnState](storage=storage) - -@agent_app.activity("message") -async def on_message(context: TurnContext, state: TurnState): - # Conversation-scoped data (shared by all users in conversation) - conversation_data = state.conversation - conversation_data.set_value("topic", "weather") - - # User-scoped data (specific to this user across conversations) - user_data = state.user - user_data.set_value("name", "John") - - # Temporary data (only for this turn) - state.temp.set_value("processing_step", "validation") - - # State is automatically saved after the turn -``` - -#### Built-in State Scopes - -- **ConversationState** - Shared across all users in a conversation -- **UserState** - Specific to individual users across all conversations -- **TempState** - Temporary data for the current turn only - -## Advanced Features - ### Route-based Message Handling ```python -# Handle all messages -@agent_app.activity("message") -async def handle_all_messages(context: TurnContext, state: TurnState): - await context.send_activity("Default handler") - -# Handle specific patterns -@agent_app.message(r"/weather (\w+)") -async def handle_weather(context: TurnContext, state: TurnState): - city = context.matches[1] # Extract from regex - await context.send_activity(f"Weather for {city}") - -# Handle conversation updates -@agent_app.conversation_update("membersAdded") -async def welcome_user(context: TurnContext, state: TurnState): - await context.send_activity("Welcome to the agent!") - -# Handle different activity types -@agent_app.activity("event") -async def handle_events(context: TurnContext, state: TurnState): - event_name = context.activity.name - await context.send_activity(f"Received event: {event_name}") -``` - -### Authentication & Authorization - -```python -from microsoft_agents.hosting.core import Authorization -from microsoft_agents.authentication.msal import MsalConnectionManager - -# Set up authentication -connection_manager = MsalConnectionManager(**config) -authorization = Authorization(storage, connection_manager, **config) - -agent_app = AgentApplication[TurnState]( - storage=storage, - authorization=authorization, - **config -) - -@agent_app.activity("message") -async def authenticated_handler(context: TurnContext, state: TurnState): - # Check if user is authenticated - if not agent_app.auth.is_signed_in(context): - await agent_app.auth.sign_in(context) - return - - # Get user token for Microsoft Graph - token = await agent_app.auth.get_token(context, ["User.Read"]) - # Use token to call Microsoft Graph APIs -``` - -### Middleware & Logging - -```python -from microsoft_agents.hosting.core.storage import ( - TranscriptLoggerMiddleware, - ConsoleTranscriptLogger -) - -# Add transcript logging -adapter.use(TranscriptLoggerMiddleware(ConsoleTranscriptLogger())) +@AGENT_APP.message(re.compile(r"^hello$")) +async def on_hello(context: TurnContext, _state: TurnState): + await context.send_activity("Hello!") -# Custom middleware -class CustomMiddleware: - async def on_turn(self, context: TurnContext, next_handler): - print(f"Before: {context.activity.text}") - await next_handler() - print("After: Turn completed") -adapter.use(CustomMiddleware()) -``` - -### File Handling - -```python -from microsoft_agents.hosting.core import InputFile - -@agent_app.activity("message") -async def handle_files(context: TurnContext, state: TurnState): - # Check for file attachments - files = state.temp.input_files - if files: - for file in files: - if file.content_type.startswith("image/"): - # Process image file - content = await file.download() - await context.send_activity(f"Received image: {file.name}") +@AGENT_APP.activity("message") +async def on_message(context: TurnContext, _state: TurnState): + await context.send_activity(f"you said: {context.activity.text}") ``` ### Error Handling ```python -@agent_app.error +@AGENT_APP.error async def on_error(context: TurnContext, error: Exception): - print(f"Error occurred: {error}") - await context.send_activity("Sorry, something went wrong.") - -# Custom error types -from microsoft_agents.hosting.core import ApplicationError - -async def risky_operation(): - if something_wrong: - raise ApplicationError("Custom error message") -``` - -## Storage Options - -### Memory Storage (Development) - -```python -from microsoft_agents.hosting.core import MemoryStorage + # NOTE: In production environment, you should consider logging this to Azure + # application insights. + print(f"\n [on_turn_error] unhandled error: {error}", file=sys.stderr) + traceback.print_exc() -storage = MemoryStorage() # Data lost when app restarts + # Send a message to the user + await context.send_activity("The bot encountered an error or bug.") ``` -### Persistent Storage (Production) - -```python -# Azure Blob Storage -from microsoft_agents.storage.blob import BlobStorage -storage = BlobStorage("connection_string", "container_name") - -# Azure Cosmos DB -from microsoft_agents.storage.cosmos import CosmosDbStorage -storage = CosmosDbStorage("connection_string", "database", "container") -``` - -## Channel Communication - -### Agent-to-Agent Communication - -```python -from microsoft_agents.hosting.core import ( - HttpAgentChannelFactory, - ConfigurationChannelHost -) - -# Set up agent communication -channel_factory = HttpAgentChannelFactory() -channel_host = ConfigurationChannelHost( - channel_factory, connection_manager, config, "HttpAgentClient" -) - -# Send message to another agent -await channel_host.send_to_agent( - agent_id="other-agent-id", - activity=message_activity -) -``` - -### Teams Integration - -```python -from microsoft_agents.hosting.teams import TeamsActivityHandler - -class TeamsAgent(TeamsActivityHandler): - async def on_teams_message_activity(self, turn_context: TurnContext): - # Teams-specific message handling - await turn_context.send_activity("Hello from Teams!") - - async def on_teams_members_added_activity(self, turn_context: TurnContext): - # Handle new team members - await turn_context.send_activity("Welcome to the team!") -``` - -## Rich Content Creation - -### Message Factory - -```python -from microsoft_agents.hosting.core import MessageFactory - -# Simple text -message = MessageFactory.text("Hello world!") - -# Text with suggested actions -message = MessageFactory.suggested_actions( - ["Option 1", "Option 2", "Option 3"], - "Choose an option:" -) - -# Attachment (cards, files, etc.) -message = MessageFactory.attachment(hero_card_attachment) -``` - -### Card Factory - -```python -from microsoft_agents.hosting.core import CardFactory - -# Hero card -hero_card = CardFactory.hero_card( - title="Card Title", - subtitle="Card Subtitle", - text="Card description text", - images=["https://example.com/image.jpg"], - buttons=[ - {"type": "imBack", "title": "Click Me", "value": "button_clicked"} - ] -) - -# Adaptive card -adaptive_card = CardFactory.adaptive_card({ - "type": "AdaptiveCard", - "version": "1.2", - "body": [ - { - "type": "TextBlock", - "text": "Hello Adaptive Cards!" - } - ] -}) -``` - -## Configuration & Deployment - -### Environment Setup - -```python -import os -from microsoft_agents.activity import load_configuration_from_env -from microsoft_agents.hosting.core import AgentApplication, TurnState - -# Load from environment variables -config = load_configuration_from_env(os.environ) - -# Create fully configured agent -agent_app = AgentApplication[TurnState]( - storage=storage, - adapter=adapter, - authorization=authorization, - **config -) -``` - -### Application Options - -```python -from microsoft_agents.hosting.core import ApplicationOptions - -options = ApplicationOptions( - agent_id="my-agent", - storage=storage, - authentication=auth_config, - logging_level="DEBUG" -) - -agent_app = AgentApplication[TurnState](options=options) -``` - -## Architecture Patterns - -### Layered Architecture - -```python -# Domain layer - business logic -class WeatherService: - async def get_weather(self, city: str) -> str: - return f"Weather in {city}: Sunny, 72°F" - -# Application layer - agent handlers -@agent_app.message(r"/weather (\w+)") -async def weather_handler(context: TurnContext, state: TurnState): - city = context.matches[1] - weather_service = WeatherService() - weather = await weather_service.get_weather(city) - await context.send_activity(weather) -``` - -### State Management Patterns - -```python -# Custom state scope -class OrderState(AgentState): - def get_storage_key(self, turn_context: TurnContext): - return f"order:{turn_context.activity.from_property.id}" - -# Use custom state -custom_state = OrderState(storage, "OrderState") -turn_state = TurnState.with_storage(storage, custom_state) -``` - -### Plugin Architecture - -```python -# Create reusable components -class GreetingPlugin: - @staticmethod - def register(app: AgentApplication): - @app.conversation_update("membersAdded") - async def greet(context: TurnContext, state: TurnState): - await context.send_activity("Welcome!") - -# Register plugins -GreetingPlugin.register(agent_app) -``` - -## Testing Your Agents - -### Unit Testing - -```python -import pytest -from microsoft_agents.hosting.core import TurnContext, MemoryStorage -from microsoft_agents.activity import Activity, ActivityTypes - -@pytest.mark.asyncio -async def test_echo_handler(): - # Create test activity - activity = Activity( - type=ActivityTypes.message, - text="test message", - conversation={"id": "test"}, - from_property={"id": "user"} - ) - - # Create test context - context = TurnContext(mock_adapter, activity) - state = TurnState.with_storage(MemoryStorage()) - - # Test your handler - await echo_handler(context, state) - - # Verify response - assert len(context.responses) == 1 - assert "test message" in context.responses[0].text -``` - -### Integration Testing - -```python -# Test with real storage and authentication -async def test_authenticated_flow(): - storage = MemoryStorage() - agent_app = AgentApplication[TurnState]( - storage=storage, - authorization=test_auth - ) - - # Simulate authenticated conversation - context = create_test_context(authenticated_user_activity) - await agent_app.on_turn(context) -``` - -## Performance & Best Practices - -### Efficient State Management - -```python -# Load only needed state scopes -minimal_state = TurnState() -minimal_state.add(ConversationState(storage)) # Only add what you need - -# Batch state operations -async def batch_operations(context: TurnContext, state: TurnState): - state.conversation.set_value("key1", "value1") - state.conversation.set_value("key2", "value2") - state.user.set_value("preference", "dark_mode") - # All saved together at end of turn -``` - -### Async Best Practices - -```python -@agent_app.activity("message") -async def efficient_handler(context: TurnContext, state: TurnState): - # Good: Use async/await for I/O operations - api_result = await external_api_call() - - # Good: Process concurrently when possible - results = await asyncio.gather( - call_service_a(), - call_service_b(), - call_service_c() - ) - - await context.send_activity(f"Results: {results}") -``` - -### Error Recovery - -```python -@agent_app.activity("message") -async def resilient_handler(context: TurnContext, state: TurnState): - try: - result = await unreliable_service() - await context.send_activity(f"Success: {result}") - except ServiceError: - # Graceful degradation - await context.send_activity("Service temporarily unavailable, try again later") - except Exception as e: - # Log and provide user-friendly message - logger.error(f"Unexpected error: {e}") - await context.send_activity("Something went wrong, please try again") -``` ## Key Classes Reference @@ -572,18 +128,6 @@ async def resilient_handler(context: TurnContext, state: TurnState): - **`Authorization`** - Authentication and authorization manager - **`ClaimsIdentity`** - User identity and claims -## Migration from Bot Framework - -| Bot Framework | Microsoft Agents Core | -|---------------|----------------------| -| `BotFrameworkAdapter` | `CloudAdapter` | -| `ActivityHandler` | `ActivityHandler` or `AgentApplication` | -| `TurnContext` | `TurnContext` | -| `ConversationState` | `ConversationState` | -| `UserState` | `UserState` | -| `MemoryStorage` | `MemoryStorage` | -| `MessageFactory` | `MessageFactory` | - # Quick Links - 📦 [All SDK Packages on PyPI](https://pypi.org/search/?q=microsoft-agents) @@ -593,10 +137,12 @@ async def resilient_handler(context: TurnContext, state: TurnState): # Sample Applications -Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file +|Name|Description|README| +|----|----|----| +|Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)| +|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)| +|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)| +|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)| +|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)| +|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)| +|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)| \ No newline at end of file diff --git a/libraries/microsoft-agents-hosting-teams/readme.md b/libraries/microsoft-agents-hosting-teams/readme.md index 9c1a66e0..1d84f23c 100644 --- a/libraries/microsoft-agents-hosting-teams/readme.md +++ b/libraries/microsoft-agents-hosting-teams/readme.md @@ -36,542 +36,6 @@ Additionally we provide a Copilot Studio Client, to interact with Agents created pip install microsoft-agents-hosting-teams ``` -## Quick Start - -### Basic Teams Agent - -```python -from microsoft_agents.hosting.teams import TeamsActivityHandler -from microsoft_agents.hosting.core import TurnContext, MessageFactory - -class MyTeamsAgent(TeamsActivityHandler): - async def on_message_activity(self, turn_context: TurnContext): - await turn_context.send_activity( - MessageFactory.text("Hello from Teams!") - ) - - async def on_teams_members_added_activity(self, turn_context: TurnContext): - for member in turn_context.activity.members_added: - if member.id != turn_context.activity.recipient.id: - await turn_context.send_activity( - MessageFactory.text(f"Welcome to Teams, {member.name}!") - ) - -# Use with hosting adapter -agent = MyTeamsAgent() -``` - -### Teams-Specific Features - -```python -from microsoft_agents.hosting.teams import TeamsInfo - -class TeamsAgent(TeamsActivityHandler): - async def on_message_activity(self, turn_context: TurnContext): - text = turn_context.activity.text.lower() - - if "team info" in text: - # Get team member information - team_id = turn_context.activity.channel_data.get("team", {}).get("id") - member = await TeamsInfo.get_team_member( - turn_context, team_id, turn_context.activity.from_property.id - ) - await turn_context.send_activity(f"Member info: {member.name}") - - elif "send to channel" in text: - # Send message to specific channel - channel_id = "19:channel-id@thread.teams" - activity = MessageFactory.text("Broadcasting to channel!") - - await TeamsInfo.send_message_to_teams_channel( - turn_context, activity, channel_id - ) -``` - -## Core Components - -### TeamsActivityHandler - -The main class that extends `ActivityHandler` with Teams-specific event handling: - -```python -class MyTeamsAgent(TeamsActivityHandler): - # Standard message handling - async def on_message_activity(self, turn_context: TurnContext): - await turn_context.send_activity("Got your message!") - - # Teams conversation events - async def on_teams_members_added_activity(self, turn_context: TurnContext): - await turn_context.send_activity("Welcome to the team!") - - async def on_teams_members_removed_activity(self, turn_context: TurnContext): - await turn_context.send_activity("Someone left the team") - - # Meeting events - async def on_teams_meeting_start(self, meeting_details, turn_context: TurnContext): - await turn_context.send_activity("Meeting started!") - - async def on_teams_meeting_end(self, meeting_details, turn_context: TurnContext): - await turn_context.send_activity("Meeting ended!") -``` - -### TeamsInfo Utilities - -Helper class for Teams-specific operations: - -```python -from microsoft_agents.hosting.teams import TeamsInfo - -# Get team member information -member = await TeamsInfo.get_team_member(context, team_id, user_id) - -# Get meeting participant details -participant = await TeamsInfo.get_meeting_participant( - context, meeting_id, participant_id, tenant_id -) - -# Get meeting information -meeting_info = await TeamsInfo.get_meeting_info(context, meeting_id) - -# Send message to Teams channel -await TeamsInfo.send_message_to_teams_channel( - context, activity, channel_id, app_id -) -``` - -## Messaging Extensions - -Handle search and action-based messaging extensions: - -```python -class MessagingExtensionAgent(TeamsActivityHandler): - async def on_teams_messaging_extension_query( - self, turn_context: TurnContext, query - ): - # Handle search queries - search_term = query.parameters[0].value if query.parameters else "" - - results = [ - { - "type": "result", - "attachmentLayout": "list", - "attachments": [ - { - "contentType": "application/vnd.microsoft.card.thumbnail", - "content": { - "title": f"Search result for: {search_term}", - "text": "Found relevant information", - "tap": { - "type": "invoke", - "value": {"query": search_term} - } - } - } - ] - } - ] - - return MessagingExtensionResponse(compose_extension=results[0]) - - async def on_teams_messaging_extension_submit_action( - self, turn_context: TurnContext, action - ): - # Handle action submissions - user_input = action.data - - # Process the action and return response - card = { - "type": "AdaptiveCard", - "version": "1.2", - "body": [ - { - "type": "TextBlock", - "text": f"Processed: {user_input}" - } - ] - } - - return MessagingExtensionActionResponse( - task={ - "type": "continue", - "value": { - "card": card - } - } - ) -``` - -## Task Modules - -Interactive dialogs and forms in Teams: - -```python -class TaskModuleAgent(TeamsActivityHandler): - async def on_teams_task_module_fetch( - self, turn_context: TurnContext, task_module_request - ): - # Show a form to the user - adaptive_card = { - "type": "AdaptiveCard", - "version": "1.2", - "body": [ - { - "type": "TextBlock", - "text": "Please enter your information:" - }, - { - "type": "Input.Text", - "id": "name", - "placeholder": "Your name" - }, - { - "type": "Input.Text", - "id": "email", - "placeholder": "Your email" - } - ], - "actions": [ - { - "type": "Action.Submit", - "title": "Submit", - "data": {"action": "submit"} - } - ] - } - - return TaskModuleResponse( - task={ - "type": "continue", - "value": { - "card": adaptive_card, - "height": 250, - "width": 400, - "title": "User Information" - } - } - ) - - async def on_teams_task_module_submit( - self, turn_context: TurnContext, task_module_request - ): - # Process form submission - data = task_module_request.data - name = data.get("name", "") - email = data.get("email", "") - - # Save data and close task module - await self.save_user_info(name, email) - - return TaskModuleResponse( - task={ - "type": "message", - "value": f"Thank you {name}! Information saved." - } - ) -``` - -## Adaptive Cards & Actions - -Handle card interactions and button clicks: - -```python -class CardAgent(TeamsActivityHandler): - async def on_teams_card_action_invoke(self, turn_context: TurnContext): - # Handle adaptive card button clicks - action_data = turn_context.activity.value - action_type = action_data.get("action") - - if action_type == "approve": - await self.handle_approval(turn_context, action_data) - elif action_type == "reject": - await self.handle_rejection(turn_context, action_data) - - # Return updated card - updated_card = self.create_updated_card(action_data) - return InvokeResponse( - status=200, - body={ - "task": { - "type": "continue", - "value": updated_card - } - } - ) - - def create_approval_card(self, request_id: str): - return { - "type": "AdaptiveCard", - "version": "1.2", - "body": [ - { - "type": "TextBlock", - "text": "Approval Request", - "weight": "Bolder" - }, - { - "type": "TextBlock", - "text": "Please review and approve this request." - } - ], - "actions": [ - { - "type": "Action.Submit", - "title": "Approve", - "data": {"action": "approve", "requestId": request_id} - }, - { - "type": "Action.Submit", - "title": "Reject", - "data": {"action": "reject", "requestId": request_id} - } - ] - } -``` - -## Meeting Integration - -Handle Teams meeting events and interactions: - -```python -class MeetingAgent(TeamsActivityHandler): - async def on_teams_meeting_start( - self, meeting_details, turn_context: TurnContext - ): - # Meeting started - send welcome message - await turn_context.send_activity( - MessageFactory.text("Meeting started! I'm here to help.") - ) - - # Log meeting start - await self.log_meeting_event("start", meeting_details) - - async def on_teams_meeting_participants_join( - self, participants_event, turn_context: TurnContext - ): - # New participants joined - participant_names = [p.user.name for p in participants_event.members] - message = f"Welcome {', '.join(participant_names)} to the meeting!" - - await turn_context.send_activity(MessageFactory.text(message)) - - async def on_teams_meeting_participants_leave( - self, participants_event, turn_context: TurnContext - ): - # Participants left - participant_names = [p.user.name for p in participants_event.members] - message = f"{', '.join(participant_names)} left the meeting" - - await turn_context.send_activity(MessageFactory.text(message)) - - async def get_meeting_info(self, turn_context: TurnContext): - # Get current meeting details - meeting_info = await TeamsInfo.get_meeting_info(turn_context) - - return { - "id": meeting_info.details.id, - "title": meeting_info.details.title, - "organizer": meeting_info.organizer.name, - "start_time": meeting_info.details.scheduled_start_time - } -``` - -## File Handling - -Handle file uploads and downloads in Teams: - -```python -class FileAgent(TeamsActivityHandler): - async def on_teams_file_consent( - self, turn_context: TurnContext, file_consent_response - ): - if file_consent_response.action == "accept": - await self.handle_file_upload(turn_context, file_consent_response) - else: - await turn_context.send_activity("File upload cancelled") - - async def on_teams_file_consent_accept( - self, turn_context: TurnContext, file_consent_response - ): - # File was accepted - handle upload - file_info = file_consent_response.upload_info - - # Upload file content - with open("local_file.pdf", "rb") as file: - await self.upload_file_to_teams(file_info.upload_url, file.read()) - - # Send confirmation - await turn_context.send_activity( - MessageFactory.text("File uploaded successfully!") - ) -``` - -## Configuration & Deployment - -### Environment Setup - -```python -import os -from microsoft_agents.hosting.aiohttp import CloudAdapter -from microsoft_agents.hosting.teams import TeamsActivityHandler -from microsoft_agents.authentication.msal import MsalConnectionManager - -# Load Teams-specific configuration -teams_config = { - "TEAMS_APP_ID": os.getenv("TEAMS_APP_ID"), - "TEAMS_APP_PASSWORD": os.getenv("TEAMS_APP_PASSWORD"), - "TENANT_ID": os.getenv("TENANT_ID") -} - -# Create connection manager -connection_manager = MsalConnectionManager(**teams_config) - -# Create adapter -adapter = CloudAdapter(connection_manager=connection_manager) -``` - -### Teams App Manifest - -Your Teams app manifest should include appropriate permissions: - -```json -{ - "manifestVersion": "1.16", - "version": "1.0.0", - "id": "your-app-id", - "bots": [ - { - "botId": "your-bot-id", - "scopes": ["personal", "team", "groupchat"], - "commandLists": [ - { - "scopes": ["personal", "team", "groupchat"], - "commands": [ - { - "title": "Help", - "description": "Get help with the agent" - } - ] - } - ] - } - ], - "composeExtensions": [ - { - "botId": "your-bot-id", - "commands": [ - { - "id": "search", - "type": "query", - "title": "Search", - "description": "Search for information", - "parameters": [ - { - "name": "searchKeyword", - "title": "Search", - "description": "Enter search term" - } - ] - } - ] - } - ] -} -``` - -## Advanced Patterns - -### Multi-Feature Teams Agent - -```python -class ComprehensiveTeamsAgent(TeamsActivityHandler): - def __init__(self): - super().__init__() - self.commands = { - "help": self.show_help, - "team": self.show_team_info, - "search": self.handle_search, - "approve": self.show_approval_card - } - - async def on_message_activity(self, turn_context: TurnContext): - text = turn_context.activity.text.lower().strip() - - # Handle command-based interactions - for command, handler in self.commands.items(): - if command in text: - await handler(turn_context) - return - - # Default response - await turn_context.send_activity("Try 'help' for available commands") - - async def show_help(self, turn_context: TurnContext): - help_card = MessageFactory.attachment( - self.create_help_card() - ) - await turn_context.send_activity(help_card) - - async def show_team_info(self, turn_context: TurnContext): - team_id = turn_context.activity.channel_data.get("team", {}).get("id") - if team_id: - members = await TeamsInfo.get_team_members(turn_context, team_id) - member_list = "\n".join([f"- {m.name}" for m in members]) - await turn_context.send_activity(f"Team members:\n{member_list}") - else: - await turn_context.send_activity("This command works only in team channels") -``` - -### Error Handling - -```python -class RobustTeamsAgent(TeamsActivityHandler): - async def on_message_activity(self, turn_context: TurnContext): - try: - await self.process_message(turn_context) - except Exception as e: - await self.handle_error(turn_context, e) - - async def handle_error(self, turn_context: TurnContext, error: Exception): - error_message = "Sorry, something went wrong. Please try again." - - if "not found" in str(error).lower(): - error_message = "The requested resource was not found." - elif "permission" in str(error).lower(): - error_message = "You don't have permission for this action." - - await turn_context.send_activity(MessageFactory.text(error_message)) -``` - -## Testing Teams Agents - -### Unit Testing - -```python -import pytest -from microsoft_agents.hosting.teams import TeamsActivityHandler -from microsoft_agents.activity import Activity, ChannelAccount - -@pytest.mark.asyncio -async def test_teams_member_added(): - agent = MyTeamsAgent() - - # Create Teams member added activity - activity = Activity( - type="conversationUpdate", - channel_id="msteams", - members_added=[ChannelAccount(id="user123", name="John Doe")], - recipient=ChannelAccount(id="bot456", name="Test Bot"), - channel_data={"team": {"id": "team789"}} - ) - - context = create_test_context(activity) - await agent.on_conversation_update_activity(context) - - # Verify welcome message was sent - assert len(context.sent_activities) == 1 - assert "welcome" in context.sent_activities[0].text.lower() -``` - ## Key Classes Reference - **`TeamsActivityHandler`** - Main handler class with Teams-specific event methods @@ -610,10 +74,12 @@ async def test_teams_member_added(): # Sample Applications -Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations \ No newline at end of file +|Name|Description|README| +|----|----|----| +|Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)| +|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)| +|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)| +|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)| +|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)| +|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)| +|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)| \ No newline at end of file diff --git a/libraries/microsoft-agents-storage-blob/readme.md b/libraries/microsoft-agents-storage-blob/readme.md index 2aa60f25..4e9e1ceb 100644 --- a/libraries/microsoft-agents-storage-blob/readme.md +++ b/libraries/microsoft-agents-storage-blob/readme.md @@ -35,80 +35,6 @@ Additionally we provide a Copilot Studio Client, to interact with Agents created pip install microsoft-agents-storage-blob ``` -## Quick Start - -### Basic Configuration with Connection String - -```python -from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig - -# Configure using connection string -config = BlobStorageConfig( - container_name="agent-storage", - connection_string="DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=..." -) - -# Create storage instance -storage = BlobStorage(config) - -# Initialize the container (creates if doesn't exist) -await storage.initialize() -``` - -### Using with AgentApplication - -```python -from microsoft_agents.hosting.core import AgentApplication, TurnState -from microsoft_agents.hosting.aiohttp import CloudAdapter -from microsoft_agents.authentication.msal import MsalConnectionManager -from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig - -# Configure blob storage -blob_config = BlobStorageConfig( - container_name="my-agent-data", - connection_string=os.getenv("AZURE_STORAGE_CONNECTION_STRING") -) -storage = BlobStorage(blob_config) -await storage.initialize() - -# Create agent with blob storage -app = AgentApplication[TurnState]( - storage=storage, - adapter=adapter, - authorization=authorization -) -``` - -## Configuration Options - -### Connection String Authentication - -The simplest way to connect - uses a storage account connection string: - -```python -config = BlobStorageConfig( - container_name="agent-storage", - connection_string="DefaultEndpointsProtocol=https;AccountName=myaccount;AccountKey=mykey;EndpointSuffix=core.windows.net" -) -``` - -**Get your connection string:** -- Azure Portal → Storage Account → Access Keys → Connection String - -### Token-Based Authentication (Recommended for Production) - -Uses Azure Active Directory credentials for secure, keyless authentication: - -```python -from azure.identity import DefaultAzureCredential - -config = BlobStorageConfig( - container_name="agent-storage", - url="https://myaccount.blob.core.windows.net", - credential=DefaultAzureCredential() -) -``` - **Benefits:** - ✅ No secrets in code - ✅ Managed Identity support @@ -127,202 +53,6 @@ config = BlobStorageConfig( *Either `connection_string` OR (`url` + `credential`) must be provided **Required when using `url` -## Core Operations - -### Read Data - -```python -from microsoft_agents.hosting.core.storage import StoreItem - -# Define your data model -class UserPreferences(StoreItem): - def __init__(self, user_id: str, theme: str = "light", language: str = "en"): - self.id = user_id - self.theme = theme - self.language = language - -# Read single item -result = await storage.read(["user123"], target_cls=UserPreferences) -if "user123" in result: - prefs = result["user123"] - print(f"Theme: {prefs.theme}, Language: {prefs.language}") - -# Read multiple items -results = await storage.read( - ["user123", "user456", "user789"], - target_cls=UserPreferences -) -``` - -### Write Data - -```python -# Create or update items -user_prefs = UserPreferences("user123", theme="dark", language="es") - -await storage.write({ - "user123": user_prefs -}) - -# Write multiple items at once -await storage.write({ - "user123": UserPreferences("user123", theme="dark"), - "user456": UserPreferences("user456", theme="light"), - "user789": UserPreferences("user789", language="fr") -}) -``` - -### Delete Data - -```python -# Delete single item -await storage.delete(["user123"]) - -# Delete multiple items -await storage.delete(["user123", "user456", "user789"]) -``` - -## Common Patterns - -### Conversation State Storage - -```python -from microsoft_agents.hosting.core import ConversationState, TurnContext - -class MyAgent(ActivityHandler): - def __init__(self, storage: BlobStorage): - self.conversation_state = ConversationState(storage) - - async def on_message_activity(self, turn_context: TurnContext): - # Access conversation state - state_accessor = self.conversation_state.create_property("conversation_data") - conv_data = await state_accessor.get(turn_context, {}) - - # Update message count - conv_data["message_count"] = conv_data.get("message_count", 0) + 1 - - await turn_context.send_activity( - f"Message #{conv_data['message_count']} in this conversation" - ) - - # Save changes - await self.conversation_state.save_changes(turn_context) -``` - -### User State Storage - -```python -from microsoft_agents.hosting.core import UserState - -class MyAgent(ActivityHandler): - def __init__(self, storage: BlobStorage): - self.user_state = UserState(storage) - - async def on_message_activity(self, turn_context: TurnContext): - # Access user state - user_accessor = self.user_state.create_property("user_profile") - profile = await user_accessor.get(turn_context, {}) - - # Track user preferences - if not profile.get("name"): - profile["name"] = turn_context.activity.from_property.name - profile["first_interaction"] = turn_context.activity.timestamp - - profile["last_interaction"] = turn_context.activity.timestamp - profile["total_messages"] = profile.get("total_messages", 0) + 1 - - # Save changes - await self.user_state.save_changes(turn_context) -``` - -### Custom Data Models - -```python -from microsoft_agents.hosting.core.storage import StoreItem -from datetime import datetime - -class TaskItem(StoreItem): - def __init__(self, task_id: str, title: str, status: str = "pending"): - self.id = task_id - self.title = title - self.status = status - self.created_at = datetime.utcnow().isoformat() - self.updated_at = datetime.utcnow().isoformat() - -class TaskManager: - def __init__(self, storage: BlobStorage): - self.storage = storage - - async def create_task(self, task_id: str, title: str): - task = TaskItem(task_id, title) - await self.storage.write({task_id: task}) - return task - - async def get_task(self, task_id: str): - result = await self.storage.read([task_id], target_cls=TaskItem) - return result.get(task_id) - - async def update_task_status(self, task_id: str, status: str): - task = await self.get_task(task_id) - if task: - task.status = status - task.updated_at = datetime.utcnow().isoformat() - await self.storage.write({task_id: task}) - return task - - async def delete_task(self, task_id: str): - await self.storage.delete([task_id]) -``` - -## Environment Setup - -### Local Development with Azurite - -Use the Azure Storage Emulator for local testing: - -```bash -# Install Azurite -npm install -g azurite - -# Start the emulator -azurite --silent --location c:\azurite --debug c:\azurite\debug.log -``` - -```python -# Use emulator connection string -config = BlobStorageConfig( - container_name="local-test", - connection_string=( - "AccountName=devstoreaccount1;" - "AccountKey=Eby8vdM02xNOcqFlqUwJPLlmEtlCDXJ1OUzFT50uSRZ6IFsuFq2UVErCz4I6tq/K1SZFPTOtr/KBHBeksoGMGw==;" - "DefaultEndpointsProtocol=http;" - "BlobEndpoint=http://127.0.0.1:10000/devstoreaccount1" - ) -) -``` - -### Production Configuration - -```python -import os -from azure.identity import DefaultAzureCredential - -# Using environment variables -config = BlobStorageConfig( - container_name=os.getenv("STORAGE_CONTAINER_NAME", "agent-production"), - url=os.getenv("AZURE_STORAGE_URL"), - credential=DefaultAzureCredential() -) - -storage = BlobStorage(config) -await storage.initialize() -``` - -**Environment Variables:** -```bash -AZURE_STORAGE_URL=https://myaccount.blob.core.windows.net -STORAGE_CONTAINER_NAME=agent-production -``` ### Azure Managed Identity @@ -342,151 +72,6 @@ config = BlobStorageConfig( - `Storage Blob Data Contributor` - For read/write access - `Storage Blob Data Reader` - For read-only access -## Advanced Usage - -### Batch Operations - -```python -# Efficient batch reads -user_ids = [f"user{i}" for i in range(100)] -results = await storage.read(user_ids, target_cls=UserPreferences) - -# Batch writes -batch_data = { - f"user{i}": UserPreferences(f"user{i}", theme="dark") - for i in range(100) -} -await storage.write(batch_data) - -# Batch deletes -await storage.delete([f"user{i}" for i in range(100)]) -``` - -### Error Handling - -```python -from azure.core.exceptions import ResourceNotFoundError - -async def safe_read_user(storage: BlobStorage, user_id: str): - try: - result = await storage.read([user_id], target_cls=UserPreferences) - return result.get(user_id) - except ResourceNotFoundError: - # Container doesn't exist - await storage.initialize() - return None - except Exception as e: - # Log error and return default - print(f"Error reading user data: {e}") - return UserPreferences(user_id) -``` - -### Container Management - -```python -# Initialize ensures container exists -await storage.initialize() - -# Safe to call multiple times (idempotent) -await storage.initialize() -await storage.initialize() # No-op if already initialized - -# Manual container operations (if needed) -container_client = storage._container_client -exists = await container_client.exists() -if not exists: - await container_client.create_container() -``` - -### Performance Optimization - -```python -# Use async context manager for cleanup -async with BlobServiceClient.from_connection_string(conn_str) as client: - container = client.get_container_client("agent-storage") - # Operations here - pass - -# Reuse storage instance (singleton pattern) -class StorageManager: - _instance = None - - @classmethod - async def get_instance(cls): - if cls._instance is None: - config = BlobStorageConfig(...) - cls._instance = BlobStorage(config) - await cls._instance.initialize() - return cls._instance -``` - -## Testing - -### Unit Testing with Mocks - -```python -import pytest -from unittest.mock import AsyncMock, MagicMock - -@pytest.fixture -def mock_storage(): - storage = AsyncMock(spec=BlobStorage) - storage.read.return_value = {} - storage.write.return_value = None - storage.delete.return_value = None - return storage - -@pytest.mark.asyncio -async def test_agent_with_storage(mock_storage): - agent = MyAgent(mock_storage) - # Test agent logic - await agent.process_message(...) - - # Verify storage was called - mock_storage.write.assert_called_once() -``` - -### Integration Testing with Azurite - -```python -@pytest.mark.integration -@pytest.mark.asyncio -async def test_storage_integration(): - config = BlobStorageConfig( - container_name="test-container", - connection_string="UseDevelopmentStorage=true" - ) - storage = BlobStorage(config) - await storage.initialize() - - # Test real operations - await storage.write({"key": TestItem("key", "value")}) - result = await storage.read(["key"], target_cls=TestItem) - assert result["key"].value == "value" - - # Cleanup - await storage.delete(["key"]) -``` - -## Migration from MemoryStorage - -Switching from `MemoryStorage` to `BlobStorage` is straightforward: - -```python -# Before (development) -from microsoft_agents.hosting.core import MemoryStorage -storage = MemoryStorage() - -# After (production) -from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig -config = BlobStorageConfig( - container_name="agent-storage", - connection_string=os.getenv("AZURE_STORAGE_CONNECTION_STRING") -) -storage = BlobStorage(config) -await storage.initialize() -``` - **Benefits of switching to BlobStorage:** - ✅ Data persists across restarts - ✅ Scalable to millions of items @@ -494,36 +79,6 @@ await storage.initialize() - ✅ Automatic backups and geo-replication - ✅ Built-in monitoring and diagnostics -## Troubleshooting - -### Common Issues - -**Container not found error:** -```python -# Solution: Initialize the storage -await storage.initialize() -``` - -**Authentication failures:** -```python -# Verify credential works -from azure.identity import DefaultAzureCredential -credential = DefaultAzureCredential() -token = credential.get_token("https://storage.azure.com/.default") -print(f"Token acquired: {token.token[:20]}...") -``` - -**Connection string issues:** -```python -# Verify connection string format -from azure.storage.blob import BlobServiceClient -try: - client = BlobServiceClient.from_connection_string(connection_string) - print("Connection string valid") -except Exception as e: - print(f"Invalid connection string: {e}") -``` - ## Best Practices 1. **Use Token Authentication in Production** - Avoid storing connection strings; use Managed Identity or DefaultAzureCredential @@ -549,10 +104,12 @@ except Exception as e: # Sample Applications -Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations +|Name|Description|README| +|----|----|----| +|Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)| +|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)| +|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)| +|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)| +|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)| +|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)| +|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)| \ No newline at end of file diff --git a/libraries/microsoft-agents-storage-cosmos/readme.md b/libraries/microsoft-agents-storage-cosmos/readme.md index a04e34cb..847761fc 100644 --- a/libraries/microsoft-agents-storage-cosmos/readme.md +++ b/libraries/microsoft-agents-storage-cosmos/readme.md @@ -42,386 +42,6 @@ Additionally we provide a Copilot Studio Client, to interact with Agents created pip install microsoft-agents-storage-cosmos ``` -## Quick Start - -### Basic Configuration with Auth Key - -```python -from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig - -# Configure using endpoint and auth key -config = CosmosDBStorageConfig( - cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", - auth_key="your-auth-key-here", - database_id="agent-database", - container_id="agent-storage" -) - -# Create storage instance -storage = CosmosDBStorage(config) - -# Initialize (creates database and container if needed) -await storage.initialize() -``` - -### Using with AgentApplication - -```python -from microsoft_agents.hosting.core import AgentApplication, TurnState -from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig -import os - -# Configure Cosmos DB storage -cosmos_config = CosmosDBStorageConfig( - cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), - auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), - database_id="agents", - container_id="conversations" -) - -storage = CosmosDBStorage(cosmos_config) -await storage.initialize() - -# Create agent with Cosmos DB storage -app = AgentApplication[TurnState]( - storage=storage, - adapter=adapter, - authorization=authorization -) -``` - -## Configuration Options - -### Auth Key Authentication (Simple) - -```python -config = CosmosDBStorageConfig( - cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", - auth_key="your-primary-or-secondary-key", - database_id="agent-db", - container_id="bot-storage" -) -``` - -**Get your endpoint and key:** -- Azure Portal → Cosmos DB Account → Keys → URI and Primary Key - -### Token-Based Authentication (Recommended) - -Uses Azure Active Directory credentials for secure, keyless authentication: - -```python -from azure.identity import DefaultAzureCredential - -config = CosmosDBStorageConfig( - url="https://myaccount.documents.azure.com:443/", - credential=DefaultAzureCredential(), - database_id="agent-db", - container_id="bot-storage" -) -``` - -**Note:** When using `url` with `credential`, do not provide `cosmos_db_endpoint` or `auth_key`. - -### Configuration from JSON File - -```python -# Create config.json -{ - "cosmos_db_endpoint": "https://myaccount.documents.azure.com:443/", - "auth_key": "your-key", - "database_id": "agent-db", - "container_id": "bot-storage", - "container_throughput": 800, - "compatibility_mode": false -} - -# Load from file -config = CosmosDBStorageConfig(filename="config.json") -``` - -### All Configuration Parameters - -| Parameter | Type | Required | Default | Description | -|-----------|------|----------|---------|-------------| -| `cosmos_db_endpoint` | `str` | Yes* | `""` | Cosmos DB account endpoint URL | -| `auth_key` | `str` | Yes* | `""` | Primary or secondary authentication key | -| `database_id` | `str` | Yes | `""` | Database identifier | -| `container_id` | `str` | Yes | `""` | Container identifier | -| `url` | `str` | No** | `""` | Alternative to `cosmos_db_endpoint` for token auth | -| `credential` | `TokenCredential` | No** | `None` | Azure credential for token-based auth | -| `container_throughput` | `int` | No | `400` | RU/s when creating container (400-unlimited) | -| `cosmos_client_options` | `dict` | No | `{}` | Additional client options (connection_policy, consistency_level) | -| `key_suffix` | `str` | No | `""` | Suffix added to all keys for multi-tenancy | -| `compatibility_mode` | `bool` | No | `False` | Truncate keys to 255 chars for legacy support | - -*Either (`cosmos_db_endpoint` + `auth_key`) OR (`url` + `credential`) required -**Required when using `url` - -## Core Operations - -### Read Data - -```python -from microsoft_agents.hosting.core.storage import StoreItem - -# Define your data model -class ConversationData(StoreItem): - def __init__(self, conv_id: str, topic: str = "", message_count: int = 0): - self.id = conv_id - self.topic = topic - self.message_count = message_count - -# Read single item -result = await storage.read(["conv123"], target_cls=ConversationData) -if "conv123" in result: - conv = result["conv123"] - print(f"Topic: {conv.topic}, Messages: {conv.message_count}") - -# Read multiple items -results = await storage.read( - ["conv123", "conv456", "conv789"], - target_cls=ConversationData -) - -# Handle missing items -for key in ["conv123", "conv456"]: - if key in results: - print(f"Found: {results[key].topic}") - else: - print(f"Not found: {key}") -``` - -### Write Data - -```python -# Create or update items -conv_data = ConversationData("conv123", topic="Weather", message_count=5) - -await storage.write({ - "conv123": conv_data -}) - -# Batch writes (more efficient) -await storage.write({ - "conv123": ConversationData("conv123", topic="Weather", message_count=5), - "conv456": ConversationData("conv456", topic="Sports", message_count=12), - "conv789": ConversationData("conv789", topic="News", message_count=3) -}) -``` - -### Delete Data - -```python -# Delete single item -await storage.delete(["conv123"]) - -# Delete multiple items -await storage.delete(["conv123", "conv456", "conv789"]) - -# Delete is idempotent (no error if item doesn't exist) -await storage.delete(["nonexistent-key"]) # No error -``` - -## Common Patterns - -### Conversation State Storage - -```python -from microsoft_agents.hosting.core import ConversationState, ActivityHandler, TurnContext - -class MyAgent(ActivityHandler): - def __init__(self, storage: CosmosDBStorage): - self.conversation_state = ConversationState(storage) - - async def on_message_activity(self, turn_context: TurnContext): - # Access conversation-scoped state - state_accessor = self.conversation_state.create_property("conversation_data") - conv_data = await state_accessor.get(turn_context, {}) - - # Track conversation metadata - if not conv_data.get("started_at"): - conv_data["started_at"] = turn_context.activity.timestamp - conv_data["topic"] = "general" - - conv_data["message_count"] = conv_data.get("message_count", 0) + 1 - conv_data["last_activity"] = turn_context.activity.timestamp - - # Save changes - await self.conversation_state.save_changes(turn_context) -``` - -### User State Storage - -```python -from microsoft_agents.hosting.core import UserState - -class MyAgent(ActivityHandler): - def __init__(self, storage: CosmosDBStorage): - self.user_state = UserState(storage) - - async def on_message_activity(self, turn_context: TurnContext): - # Access user-scoped state (persists across conversations) - user_accessor = self.user_state.create_property("user_profile") - profile = await user_accessor.get(turn_context, {}) - - # Track user preferences and history - if not profile.get("user_id"): - profile["user_id"] = turn_context.activity.from_property.id - profile["name"] = turn_context.activity.from_property.name - profile["first_seen"] = turn_context.activity.timestamp - profile["preferences"] = {"language": "en", "timezone": "UTC"} - - profile["last_seen"] = turn_context.activity.timestamp - profile["total_interactions"] = profile.get("total_interactions", 0) + 1 - - # Save changes - await self.user_state.save_changes(turn_context) -``` - -### Custom Data Models - -```python -from microsoft_agents.hosting.core.storage import StoreItem -from datetime import datetime -from typing import List - -class OrderItem(StoreItem): - def __init__(self, order_id: str, customer_id: str, items: List[dict], status: str = "pending"): - self.id = order_id - self.customer_id = customer_id - self.items = items - self.status = status - self.created_at = datetime.utcnow().isoformat() - self.updated_at = datetime.utcnow().isoformat() - self.total_amount = sum(item.get("price", 0) for item in items) - -class OrderManager: - def __init__(self, storage: CosmosDBStorage): - self.storage = storage - - async def create_order(self, order_id: str, customer_id: str, items: List[dict]): - order = OrderItem(order_id, customer_id, items) - await self.storage.write({order_id: order}) - return order - - async def get_order(self, order_id: str): - result = await self.storage.read([order_id], target_cls=OrderItem) - return result.get(order_id) - - async def update_order_status(self, order_id: str, new_status: str): - order = await self.get_order(order_id) - if order: - order.status = new_status - order.updated_at = datetime.utcnow().isoformat() - await self.storage.write({order_id: order}) - return order - - async def cancel_order(self, order_id: str): - await self.storage.delete([order_id]) -``` - -## Advanced Features - -### Key Suffix for Multi-Tenancy - -Use key suffixes to isolate data for different tenants or environments: - -```python -# Production environment -prod_config = CosmosDBStorageConfig( - cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", - auth_key="your-key", - database_id="shared-db", - container_id="shared-container", - key_suffix="_prod" -) - -# Development environment (same database/container) -dev_config = CosmosDBStorageConfig( - cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", - auth_key="your-key", - database_id="shared-db", - container_id="shared-container", - key_suffix="_dev" -) - -# Keys are automatically suffixed -# "user123" becomes "user123_prod" or "user123_dev" -``` - -### Compatibility Mode - -Enable compatibility mode for legacy containers with 255-character key limits: - -```python -config = CosmosDBStorageConfig( - cosmos_db_endpoint="https://myaccount.documents.azure.com:443/", - auth_key="your-key", - database_id="legacy-db", - container_id="old-container", - compatibility_mode=True -) - -# Long keys are automatically truncated with SHA-256 hash appended -# to prevent collisions -``` - -**Note:** Cannot use `key_suffix` with `compatibility_mode=True`. - -### Custom Throughput Settings - -```python -# Standard throughput (400 RU/s) -config = CosmosDBStorageConfig( - cosmos_db_endpoint="...", - auth_key="...", - database_id="agent-db", - container_id="storage", - container_throughput=400 # Default -) - -# High-performance throughput -config = CosmosDBStorageConfig( - cosmos_db_endpoint="...", - auth_key="...", - database_id="agent-db", - container_id="storage", - container_throughput=1000 # Higher RU/s for more traffic -) - -# Auto-scale throughput (specify in Azure Portal) -# Set to 0 to use existing container throughput -config = CosmosDBStorageConfig( - cosmos_db_endpoint="...", - auth_key="...", - database_id="agent-db", - container_id="existing-container", - container_throughput=0 # Don't override existing -) -``` - -### Custom Client Options - -```python -from azure.cosmos import documents - -# Configure connection policy and consistency -connection_policy = documents.ConnectionPolicy() -connection_policy.RequestTimeout = 30000 # 30 seconds -connection_policy.PreferredLocations = ["West US", "East US"] - -config = CosmosDBStorageConfig( - cosmos_db_endpoint="...", - auth_key="...", - database_id="agent-db", - container_id="storage", - cosmos_client_options={ - "connection_policy": connection_policy, - "consistency_level": "Session" # Session, Eventual, Strong, etc. - } -) -``` ## Environment Setup @@ -431,339 +51,6 @@ Install and run the Azure Cosmos DB Emulator for local testing: **Download:** [Azure Cosmos DB Emulator](https://docs.microsoft.com/azure/cosmos-db/local-emulator) -```python -# Use emulator connection details -config = CosmosDBStorageConfig( - cosmos_db_endpoint="https://localhost:8081", - auth_key="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", - database_id="local-test-db", - container_id="bot-storage" -) - -storage = CosmosDBStorage(config) -await storage.initialize() -``` - -**Note:** The auth key above is the well-known emulator key (safe to use locally). - -### Production Configuration - -```python -import os -from azure.identity import DefaultAzureCredential - -# Using environment variables -config = CosmosDBStorageConfig( - cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), - auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), - database_id=os.getenv("COSMOS_DATABASE_ID", "agents"), - container_id=os.getenv("COSMOS_CONTAINER_ID", "production"), - container_throughput=int(os.getenv("COSMOS_THROUGHPUT", "400")) -) - -# Or use Managed Identity (recommended) -config = CosmosDBStorageConfig( - url=os.getenv("COSMOS_DB_ENDPOINT"), - credential=DefaultAzureCredential(), - database_id="agents", - container_id="production" -) - -storage = CosmosDBStorage(config) -await storage.initialize() -``` - -**Environment Variables:** -```bash -COSMOS_DB_ENDPOINT=https://myaccount.documents.azure.com:443/ -COSMOS_DB_AUTH_KEY=your-primary-key-here -COSMOS_DATABASE_ID=agents -COSMOS_CONTAINER_ID=production -COSMOS_THROUGHPUT=800 -``` - -### Azure Managed Identity - -When running in Azure (App Service, Functions, Container Apps): - -```python -from azure.identity import ManagedIdentityCredential - -config = CosmosDBStorageConfig( - url="https://myaccount.documents.azure.com:443/", - credential=ManagedIdentityCredential(), - database_id="agents", - container_id="production" -) -``` - -**Azure RBAC Roles Required:** -- `Cosmos DB Built-in Data Contributor` - For read/write access -- `Cosmos DB Built-in Data Reader` - For read-only access - -## Key Sanitization - -Cosmos DB has restrictions on certain characters in keys. The library automatically sanitizes keys: - -```python -# Forbidden characters: \ ? / # \t \n \r * -# These are replaced with *{unicode-code-point} - -# Example: -original_key = "user/123?test" -# Becomes: "user*47123*63test" - -# Special handling for keys with forbidden characters -await storage.write({ - "user/123": user_data, # Automatically sanitized - "conversation?active": conv_data # Automatically sanitized -}) - -# Read using original keys (sanitization is automatic) -result = await storage.read(["user/123", "conversation?active"], target_cls=UserData) -``` - -**Internal behavior:** -- Keys are sanitized before storage operations -- Original keys are preserved in the document as `realId` -- Reads return data mapped to original keys - -## Error Handling - -```python -from azure.cosmos.exceptions import CosmosResourceNotFoundError, CosmosHttpResponseError - -async def safe_storage_operation(storage: CosmosDBStorage, key: str): - try: - # Initialize if needed - await storage.initialize() - - # Perform operation - result = await storage.read([key], target_cls=UserData) - return result.get(key) - - except CosmosResourceNotFoundError as e: - # Container or database doesn't exist - print(f"Resource not found: {e}") - await storage.initialize() - return None - - except CosmosHttpResponseError as e: - # Network issues, throttling, etc. - if e.status_code == 429: # Too Many Requests - print("Rate limited, implement retry with backoff") - elif e.status_code == 503: # Service Unavailable - print("Service temporarily unavailable") - else: - print(f"Cosmos error: {e.status_code} - {e.message}") - raise - - except ValueError as e: - # Configuration or validation errors - print(f"Configuration error: {e}") - raise -``` - -## Performance Optimization - -### Batch Operations - -```python -# Efficient: Single request with multiple keys -user_ids = [f"user{i}" for i in range(100)] -results = await storage.read(user_ids, target_cls=UserData) - -# Efficient: Batch write -batch_data = { - f"user{i}": UserData(f"user{i}", name=f"User {i}") - for i in range(100) -} -await storage.write(batch_data) -``` - -### Singleton Pattern - -```python -# Reuse storage instance across requests -class StorageManager: - _instance: CosmosDBStorage = None - - @classmethod - async def get_instance(cls) -> CosmosDBStorage: - if cls._instance is None: - config = CosmosDBStorageConfig( - cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), - auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), - database_id="agents", - container_id="production" - ) - cls._instance = CosmosDBStorage(config) - await cls._instance.initialize() - return cls._instance - -# Use in your agent -storage = await StorageManager.get_instance() -``` - -### Indexing Strategy - -Configure indexing in Azure Portal for better query performance: - -```json -{ - "indexingMode": "consistent", - "automatic": true, - "includedPaths": [ - { - "path": "/document/user_id/*" - }, - { - "path": "/document/timestamp/*" - } - ], - "excludedPaths": [ - { - "path": "/document/large_text/*" - } - ] -} -``` - -## Testing - -### Unit Testing with Mocks - -```python -import pytest -from unittest.mock import AsyncMock - -@pytest.fixture -def mock_storage(): - storage = AsyncMock(spec=CosmosDBStorage) - storage.read.return_value = {} - storage.write.return_value = None - storage.delete.return_value = None - storage.initialize.return_value = None - return storage - -@pytest.mark.asyncio -async def test_agent_with_storage(mock_storage): - agent = MyAgent(mock_storage) - - # Test agent logic - await agent.process_message(...) - - # Verify storage interactions - mock_storage.initialize.assert_called_once() - mock_storage.write.assert_called() -``` - -### Integration Testing with Emulator - -```python -@pytest.mark.integration -@pytest.mark.asyncio -async def test_cosmos_storage_integration(): - config = CosmosDBStorageConfig( - cosmos_db_endpoint="https://localhost:8081", - auth_key="C2y6yDjf5/R+ob0N8A7Cgv30VRDJIWEHLM+4QDU5DE2nQ9nDuVTqobD4b8mGGyPMbIZnqyMsEcaGQy67XIw/Jw==", - database_id="test-db", - container_id="test-container" - ) - - storage = CosmosDBStorage(config) - await storage.initialize() - - # Test CRUD operations - test_item = TestData("key1", "value1") - await storage.write({"key1": test_item}) - - result = await storage.read(["key1"], target_cls=TestData) - assert result["key1"].value == "value1" - - await storage.delete(["key1"]) - result = await storage.read(["key1"], target_cls=TestData) - assert "key1" not in result -``` - -## Migration Guide - -### From MemoryStorage - -```python -# Before (development) -from microsoft_agents.hosting.core import MemoryStorage -storage = MemoryStorage() - -# After (production) -from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig -import os - -config = CosmosDBStorageConfig( - cosmos_db_endpoint=os.getenv("COSMOS_DB_ENDPOINT"), - auth_key=os.getenv("COSMOS_DB_AUTH_KEY"), - database_id="agents", - container_id="production" -) -storage = CosmosDBStorage(config) -await storage.initialize() -``` - -### From BlobStorage - -Both storage implementations use the same interface: - -```python -# BlobStorage -from microsoft_agents.storage.blob import BlobStorage, BlobStorageConfig - -# CosmosDBStorage (drop-in replacement) -from microsoft_agents.storage.cosmos import CosmosDBStorage, CosmosDBStorageConfig - -# Same usage pattern -storage = CosmosDBStorage(config) -await storage.initialize() -``` - -**Key Differences:** -- Cosmos DB: Better for high-throughput, low-latency scenarios -- Blob Storage: Better for large objects, archival, cost-sensitive scenarios - -## Troubleshooting - -### Common Issues - -**"Database/Container not found"** -```python -# Solution: Initialize storage to create resources -await storage.initialize() -``` - -**"Invalid key characters"** -```python -# Solution: Keys are automatically sanitized -# Avoid using \ ? / # \t \n \r * if possible -# Library handles sanitization transparently -``` - -**"Request rate too large (429)"** -```python -# Solution: Increase container throughput or implement retry logic -config = CosmosDBStorageConfig( - # ... other settings ... - container_throughput=1000 # Increase RU/s -) -``` - -**Authentication failures** -```python -# Verify credentials work -from azure.cosmos.aio import CosmosClient - -client = CosmosClient(endpoint, auth_key) -database_list = await client.list_databases() -print(f"Connected! Databases: {len(list(database_list))}") -``` ## Best Practices @@ -793,10 +80,12 @@ print(f"Connected! Databases: {len(list(database_list))}") # Sample Applications -Explore working examples in the [Python samples repository](https://github.com/microsoft/Agents/tree/main/samples/python): -- **Teams Agent**: Full-featured Microsoft Teams bot with SSO and adaptive cards -- **Copilot Studio Integration**: Connect to Copilot Studio agents -- **Multi-Channel Agent**: Deploy to Teams, webchat, and third-party platforms -- **Authentication Flows**: OAuth, MSAL, and token management examples -- **State Management**: Conversation and user state with Azure storage -- **Streaming Responses**: Real-time agent responses with citations +|Name|Description|README| +|----|----|----| +|Quickstart|Simplest agent|[Quickstart](https://github.com/microsoft/Agents/blob/main/samples/python/quickstart/README.md)| +|Auto Sign In|Simple OAuth agent using Graph and GitHub|[auto-signin](https://github.com/microsoft/Agents/blob/main/samples/python/auto-signin/README.md)| +|OBO Authorization|OBO flow to access a Copilot Studio Agent|[obo-authorization](https://github.com/microsoft/Agents/blob/main/samples/python/obo-authorization/README.md)| +|Semantic Kernel Integration|A weather agent built with Semantic Kernel|[semantic-kernel-multiturn](https://github.com/microsoft/Agents/blob/main/samples/python/semantic-kernel-multiturn/README.md)| +|Streaming Agent|Streams OpenAI responses|[azure-ai-streaming](https://github.com/microsoft/Agents/blob/main/samples/python/azureai-streaming/README.md)| +|Copilot Studio Client|Console app to consume a Copilot Studio Agent|[copilotstudio-client](https://github.com/microsoft/Agents/blob/main/samples/python/copilotstudio-client/README.md)| +|Cards Agent|Agent that uses rich cards to enhance conversation design |[cards](https://github.com/microsoft/Agents/blob/main/samples/python/cards/README.md)| \ No newline at end of file From 5834e7d90dcac6aaf031172ce43f5d43b3e2576e Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Fri, 10 Oct 2025 14:47:51 -0700 Subject: [PATCH 09/10] Removed erronous class --- libraries/microsoft-agents-authentication-msal/readme.md | 1 - 1 file changed, 1 deletion(-) diff --git a/libraries/microsoft-agents-authentication-msal/readme.md b/libraries/microsoft-agents-authentication-msal/readme.md index 9395a26e..6fce36ab 100644 --- a/libraries/microsoft-agents-authentication-msal/readme.md +++ b/libraries/microsoft-agents-authentication-msal/readme.md @@ -87,7 +87,6 @@ class AuthTypes(str, Enum): - **`MsalAuth`** - Core authentication provider using MSAL - **`MsalConnectionManager`** - Manages multiple authentication connections -- **`AgentAuthConfiguration`** - Configuration settings for auth providers ## Features From 994c6b09b9897c69af32ab0fa9c672b3d7fff1c7 Mon Sep 17 00:00:00 2001 From: Chris Mullins Date: Fri, 10 Oct 2025 14:51:49 -0700 Subject: [PATCH 10/10] Added in a "This library is still evolving" message. --- libraries/microsoft-agents-hosting-teams/readme.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/libraries/microsoft-agents-hosting-teams/readme.md b/libraries/microsoft-agents-hosting-teams/readme.md index 1d84f23c..9ff2e5ab 100644 --- a/libraries/microsoft-agents-hosting-teams/readme.md +++ b/libraries/microsoft-agents-hosting-teams/readme.md @@ -6,6 +6,8 @@ Integration library for building Microsoft Teams agents using the Microsoft 365 This library extends the core hosting capabilities with Teams-specific features. It handles Teams' unique interaction patterns like messaging extensions, tab applications, task modules, and meeting integrations. Think of it as the bridge that makes your agent "Teams-native" rather than just a generic chatbot. +This library is still in flux, as the interfaces to Teams continue to evolve. + # What is this? This library is part of the **Microsoft 365 Agents SDK for Python** - a comprehensive framework for building enterprise-grade conversational AI agents. The SDK enables developers to create intelligent agents that work across multiple platforms including Microsoft Teams, M365 Copilot, Copilot Studio, and web chat, with support for third-party integrations like Slack, Facebook Messenger, and Twilio.