mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Python: Add Purview Middleware (#1142)
* [Py Purview] Purview Python Initial Commit * [Py Purview] Purview Python Minor Fixes * [Py Purview] Purview Python Comment Fixesish * [Py Purview] Purview Python Agent Middleware Done * [Py Purview] Purview Python Agent Middleware Done * [Py Purview] Purview Python Lint Errors * [Py Purview] Purview Python Final Hopefully * [Py Purview] Purview Python Final Hopefully * [Py Purview] Purview Python Fix ReadMe * [Py Purview] Purview Python Fix MyPy * [Py Purview] Purview Python Minor Updates on comments * [Py Purview] Purview Python Fix Build Error --------- Co-authored-by: Dmytro Struk <13853051+dmytrostruk@users.noreply.github.com>
This commit is contained in:
committed by
GitHub
Unverified
parent
76ae0a62ac
commit
59da578902
@@ -0,0 +1,88 @@
|
||||
## Purview Policy Enforcement Sample (Python)
|
||||
|
||||
This getting-started sample shows how to attach Microsoft Purview policy evaluation to an Agent Framework `ChatAgent` using the **middleware** approach.
|
||||
|
||||
1. Configure an Azure OpenAI chat client
|
||||
2. Add Purview policy enforcement middleware (`PurviewPolicyMiddleware`)
|
||||
3. Run a short conversation and observe prompt / response blocking behavior
|
||||
|
||||
---
|
||||
## 1. Setup
|
||||
### Required Environment Variables
|
||||
|
||||
| Variable | Required | Purpose |
|
||||
|----------|----------|---------|
|
||||
| `AZURE_OPENAI_ENDPOINT` | Yes | Azure OpenAI endpoint (https://<name>.openai.azure.com) |
|
||||
| `AZURE_OPENAI_DEPLOYMENT_NAME` | Optional | Model deployment name (defaults inside SDK if omitted) |
|
||||
| `PURVIEW_CLIENT_APP_ID` | Yes* | Client (application) ID used for Purview authentication |
|
||||
| `PURVIEW_USE_CERT_AUTH` | Optional (`true`/`false`) | Switch between certificate and interactive auth |
|
||||
| `PURVIEW_TENANT_ID` | Yes (when cert auth on) | Tenant ID for certificate authentication |
|
||||
| `PURVIEW_CERT_PATH` | Yes (when cert auth on) | Path to your .pfx certificate |
|
||||
| `PURVIEW_CERT_PASSWORD` | Optional | Password for encrypted certs |
|
||||
|
||||
*A demo default exists in code for illustration only—always set your own value.
|
||||
|
||||
### 2. Auth Modes Supported
|
||||
|
||||
#### A. Interactive Browser Authentication (default)
|
||||
Opens a browser on first run to sign in.
|
||||
|
||||
```powershell
|
||||
$env:AZURE_OPENAI_ENDPOINT = "https://your-openai-instance.openai.azure.com"
|
||||
$env:PURVIEW_CLIENT_APP_ID = "00000000-0000-0000-0000-000000000000"
|
||||
```
|
||||
|
||||
#### B. Certificate Authentication
|
||||
For headless / CI scenarios.
|
||||
|
||||
```powershell
|
||||
$env:PURVIEW_USE_CERT_AUTH = "true"
|
||||
$env:PURVIEW_TENANT_ID = "<tenant-guid>"
|
||||
$env:PURVIEW_CERT_PATH = "C:\path\to\cert.pfx"
|
||||
$env:PURVIEW_CERT_PASSWORD = "optional-password"
|
||||
```
|
||||
|
||||
Certificate steps (summary): create / register app, generate certificate, upload public key, export .pfx with private key, grant required Graph / Purview permissions.
|
||||
|
||||
---
|
||||
|
||||
## 3. Run the Sample
|
||||
|
||||
From repo root:
|
||||
|
||||
```powershell
|
||||
cd python/samples/getting_started/purview_agent
|
||||
python sample_purview_agent.py
|
||||
```
|
||||
|
||||
If interactive auth is used, a browser window will appear the first time.
|
||||
|
||||
---
|
||||
|
||||
## 4. How It Works
|
||||
|
||||
1. Builds an Azure OpenAI chat client (using the environment endpoint / deployment)
|
||||
2. Chooses credential mode (certificate vs interactive)
|
||||
3. Creates `PurviewPolicyMiddleware` with `PurviewSettings`
|
||||
4. Injects middleware into the agent at construction
|
||||
5. Sends two user messages sequentially
|
||||
6. Prints results (or policy block messages)
|
||||
|
||||
Prompt blocks set a system-level message: `Prompt blocked by policy` and terminate the run early. Response blocks rewrite the output to `Response blocked by policy`.
|
||||
|
||||
---
|
||||
|
||||
## 5. Code Snippet (Middleware Injection)
|
||||
|
||||
```python
|
||||
agent = ChatAgent(
|
||||
chat_client=chat_client,
|
||||
instructions="You are good at telling jokes.",
|
||||
name="Joker",
|
||||
middleware=[
|
||||
PurviewPolicyMiddleware(credential, PurviewSettings(app_name="Sample App", default_user_id="<guid>"))
|
||||
],
|
||||
)
|
||||
```
|
||||
|
||||
---
|
||||
@@ -0,0 +1,179 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
"""Purview policy enforcement sample (Python).
|
||||
|
||||
Shows:
|
||||
1. Creating a basic chat agent
|
||||
2. Adding Purview policy evaluation via AGENT middleware (agent-level)
|
||||
3. Adding Purview policy evaluation via CHAT middleware (chat-client level)
|
||||
4. Running a threaded conversation and printing results
|
||||
|
||||
Environment variables:
|
||||
- AZURE_OPENAI_ENDPOINT (required)
|
||||
- AZURE_OPENAI_DEPLOYMENT_NAME (optional, defaults to gpt-4o-mini)
|
||||
- PURVIEW_CLIENT_APP_ID (required)
|
||||
- PURVIEW_USE_CERT_AUTH (optional, set to "true" for certificate auth)
|
||||
- PURVIEW_TENANT_ID (required if certificate auth)
|
||||
- PURVIEW_CERT_PATH (required if certificate auth)
|
||||
- PURVIEW_CERT_PASSWORD (optional)
|
||||
- PURVIEW_DEFAULT_USER_ID (optional, user ID for Purview evaluation)
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
import os
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import AgentRunResponse, ChatAgent, ChatMessage, Role
|
||||
from agent_framework.azure import AzureOpenAIChatClient
|
||||
from azure.identity import (
|
||||
AzureCliCredential,
|
||||
CertificateCredential,
|
||||
InteractiveBrowserCredential,
|
||||
)
|
||||
|
||||
# Purview integration pieces
|
||||
from agent_framework.microsoft import (
|
||||
PurviewPolicyMiddleware,
|
||||
PurviewChatPolicyMiddleware,
|
||||
PurviewSettings,
|
||||
)
|
||||
|
||||
JOKER_NAME = "Joker"
|
||||
JOKER_INSTRUCTIONS = "You are good at telling jokes. Keep responses concise."
|
||||
|
||||
|
||||
def _get_env(name: str, *, required: bool = True, default: str | None = None) -> str:
|
||||
val = os.environ.get(name, default)
|
||||
if required and not val:
|
||||
raise RuntimeError(f"Environment variable {name} is required")
|
||||
return val # type: ignore[return-value]
|
||||
|
||||
|
||||
def build_credential() -> Any:
|
||||
"""Select an Azure credential for Purview authentication.
|
||||
|
||||
Supported modes:
|
||||
1. CertificateCredential (if PURVIEW_USE_CERT_AUTH=true)
|
||||
2. InteractiveBrowserCredential (requires PURVIEW_CLIENT_APP_ID)
|
||||
"""
|
||||
client_id = _get_env("PURVIEW_CLIENT_APP_ID", required=True)
|
||||
use_cert_auth = _get_env("PURVIEW_USE_CERT_AUTH", required=False, default="false").lower() == "true"
|
||||
|
||||
if not client_id:
|
||||
raise RuntimeError(
|
||||
"PURVIEW_CLIENT_APP_ID is required for interactive browser authentication; "
|
||||
"set PURVIEW_USE_CERT_AUTH=true for certificate mode instead."
|
||||
)
|
||||
|
||||
if use_cert_auth:
|
||||
tenant_id = _get_env("PURVIEW_TENANT_ID")
|
||||
cert_path = _get_env("PURVIEW_CERT_PATH")
|
||||
cert_password = _get_env("PURVIEW_CERT_PASSWORD", required=False, default=None)
|
||||
print(f"Using Certificate Authentication (tenant: {tenant_id}, cert: {cert_path})")
|
||||
return CertificateCredential(
|
||||
tenant_id=tenant_id,
|
||||
client_id=client_id,
|
||||
certificate_path=cert_path,
|
||||
password=cert_password,
|
||||
)
|
||||
|
||||
print(f"Using Interactive Browser Authentication (client_id: {client_id})")
|
||||
return InteractiveBrowserCredential(client_id=client_id)
|
||||
|
||||
|
||||
async def run_with_agent_middleware() -> None:
|
||||
endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
|
||||
if not endpoint:
|
||||
print("Skipping run: AZURE_OPENAI_ENDPOINT not set")
|
||||
return
|
||||
|
||||
deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME", "gpt-4o-mini")
|
||||
user_id = os.environ.get("PURVIEW_DEFAULT_USER_ID")
|
||||
chat_client = AzureOpenAIChatClient(deployment_name=deployment, endpoint=endpoint, credential=AzureCliCredential())
|
||||
|
||||
purview_agent_middleware = PurviewPolicyMiddleware(
|
||||
build_credential(),
|
||||
PurviewSettings(
|
||||
app_name="Agent Framework Sample App",
|
||||
),
|
||||
)
|
||||
|
||||
agent = ChatAgent(
|
||||
chat_client=chat_client,
|
||||
instructions=JOKER_INSTRUCTIONS,
|
||||
name=JOKER_NAME,
|
||||
middleware=purview_agent_middleware,
|
||||
)
|
||||
|
||||
print("-- Agent Middleware Path --")
|
||||
first: AgentRunResponse = await agent.run(ChatMessage(role=Role.USER, text="Tell me a joke about a pirate.", additional_properties={"user_id": user_id}))
|
||||
print("First response (agent middleware):\n", first)
|
||||
|
||||
second: AgentRunResponse = await agent.run(ChatMessage(role=Role.USER, text="That was funny. Tell me another one.", additional_properties={"user_id": user_id}))
|
||||
print("Second response (agent middleware):\n", second)
|
||||
|
||||
|
||||
async def run_with_chat_middleware() -> None:
|
||||
endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
|
||||
if not endpoint:
|
||||
print("Skipping chat middleware run: AZURE_OPENAI_ENDPOINT not set")
|
||||
return
|
||||
|
||||
deployment = os.environ.get("AZURE_OPENAI_DEPLOYMENT_NAME", default="gpt-4o-mini")
|
||||
user_id = os.environ.get("PURVIEW_DEFAULT_USER_ID")
|
||||
|
||||
chat_client = AzureOpenAIChatClient(
|
||||
deployment_name=deployment,
|
||||
endpoint=endpoint,
|
||||
credential=AzureCliCredential(),
|
||||
middleware=[
|
||||
PurviewChatPolicyMiddleware(
|
||||
build_credential(),
|
||||
PurviewSettings(
|
||||
app_name="Agent Framework Sample App (Chat)",
|
||||
),
|
||||
)
|
||||
],
|
||||
)
|
||||
|
||||
agent = ChatAgent(
|
||||
chat_client=chat_client,
|
||||
instructions=JOKER_INSTRUCTIONS,
|
||||
name=JOKER_NAME,
|
||||
)
|
||||
|
||||
print("-- Chat Middleware Path --")
|
||||
first: AgentRunResponse = await agent.run(
|
||||
ChatMessage(
|
||||
role=Role.USER,
|
||||
text="Give me a short clean joke.",
|
||||
additional_properties={"user_id": user_id},
|
||||
)
|
||||
)
|
||||
print("First response (chat middleware):\n", first)
|
||||
|
||||
second: AgentRunResponse = await agent.run(
|
||||
ChatMessage(
|
||||
role=Role.USER,
|
||||
text="One more please.",
|
||||
additional_properties={"user_id": user_id},
|
||||
)
|
||||
)
|
||||
print("Second response (chat middleware):\n", second)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("== Purview Agent Sample (Agent & Chat Middleware) ==")
|
||||
try:
|
||||
await run_with_agent_middleware()
|
||||
except Exception as ex: # pragma: no cover - demo resilience
|
||||
print(f"Agent middleware path failed: {ex}")
|
||||
|
||||
try:
|
||||
await run_with_chat_middleware()
|
||||
except Exception as ex: # pragma: no cover - demo resilience
|
||||
print(f"Chat middleware path failed: {ex}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user