mirror of
https://github.com/microsoft/agent-framework.git
synced 2026-06-16 21:04:09 +08:00
Merge branch 'main' into copilot/move-workflow-and-agent-samples
This commit is contained in:
@@ -3,7 +3,7 @@
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, AgentSession, BaseContextProvider, SessionContext
|
||||
from agent_framework import Agent, AgentSession, ContextProvider, SessionContext
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
|
||||
@@ -17,7 +17,7 @@ responses — the name persists across turns via the session.
|
||||
|
||||
|
||||
# <context_provider>
|
||||
class UserMemoryProvider(BaseContextProvider):
|
||||
class UserMemoryProvider(ContextProvider):
|
||||
"""A context provider that remembers user info in session state."""
|
||||
|
||||
DEFAULT_SOURCE_ID = "user_memory"
|
||||
|
||||
@@ -9,6 +9,7 @@ This folder contains examples for direct chat client usage patterns.
|
||||
| [`built_in_chat_clients.py`](built_in_chat_clients.py) | Consolidated sample for built-in chat clients. Uses `get_client()` to create the selected client and pass it to `main()`. |
|
||||
| [`chat_response_cancellation.py`](chat_response_cancellation.py) | Demonstrates how to cancel chat responses during streaming, showing proper cancellation handling and cleanup. |
|
||||
| [`custom_chat_client.py`](custom_chat_client.py) | Demonstrates how to create custom chat clients by extending the `BaseChatClient` class. Shows a `EchoingChatClient` implementation and how to integrate it with `Agent` using the `as_agent()` method. |
|
||||
| [`require_per_service_call_history_persistence.py`](require_per_service_call_history_persistence.py) | Compares two otherwise identical `FoundryChatClient` agents with `store=False`; the only difference is whether `require_per_service_call_history_persistence` is enabled, and only the run without it stores the synthesized tool result when middleware terminates the loop early. |
|
||||
|
||||
## Selecting a built-in client
|
||||
|
||||
@@ -35,13 +36,22 @@ Example:
|
||||
uv run samples/02-agents/chat_client/built_in_chat_clients.py
|
||||
```
|
||||
|
||||
The `require_per_service_call_history_persistence.py` sample uses `FoundryChatClient`, so set the usual Foundry settings first and sign in with the Azure CLI:
|
||||
|
||||
```bash
|
||||
export FOUNDRY_PROJECT_ENDPOINT="https://<your-project>.services.ai.azure.com/api/projects/<project-name>"
|
||||
export FOUNDRY_MODEL="<your-model-deployment-name>"
|
||||
az login
|
||||
uv run samples/02-agents/chat_client/require_per_service_call_history_persistence.py
|
||||
```
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Depending on the selected client, set the appropriate environment variables:
|
||||
|
||||
**For Azure OpenAI clients (`azure_openai_responses` and `azure_openai_chat_completion`):**
|
||||
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`: The Azure OpenAI deployment used by the sample
|
||||
- `AZURE_OPENAI_MODEL`: The Azure OpenAI deployment used by the sample
|
||||
- `AZURE_OPENAI_API_VERSION` (optional): Azure OpenAI API version override
|
||||
- `AZURE_OPENAI_API_KEY` (optional): Azure OpenAI API key if you are not using `AzureCliCredential`
|
||||
|
||||
@@ -56,13 +66,13 @@ Depending on the selected client, set the appropriate environment variables:
|
||||
|
||||
**For Anthropic client (`anthropic`):**
|
||||
- `ANTHROPIC_API_KEY`: Your Anthropic API key
|
||||
- `ANTHROPIC_CHAT_MODEL_ID`: The Anthropic model ID (for example, `claude-sonnet-4-5`)
|
||||
- `ANTHROPIC_CHAT_MODEL`: The Anthropic model to use (for example, `claude-sonnet-4-5`)
|
||||
|
||||
**For Ollama client (`ollama`):**
|
||||
- `OLLAMA_HOST`: Ollama server URL (defaults to `http://localhost:11434` if unset)
|
||||
- `OLLAMA_MODEL_ID`: Ollama model name (for example, `mistral`, `qwen2.5:8b`)
|
||||
- `OLLAMA_MODEL`: Ollama model name (for example, `mistral`, `qwen2.5:8b`)
|
||||
|
||||
**For Bedrock client (`bedrock`):**
|
||||
- `BEDROCK_CHAT_MODEL_ID`: Bedrock model ID (for example, `anthropic.claude-3-5-sonnet-20240620-v1:0`)
|
||||
- `BEDROCK_CHAT_MODEL`: Bedrock model ID (for example, `anthropic.claude-3-5-sonnet-20240620-v1:0`)
|
||||
- `BEDROCK_REGION`: AWS region (defaults to `us-east-1` if unset)
|
||||
- AWS credentials via standard environment variables (for example, `AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`)
|
||||
|
||||
@@ -22,31 +22,29 @@ This sample demonstrates how to run the same prompt flow against different built
|
||||
chat clients using a single `get_client` factory.
|
||||
|
||||
Select one of these client names:
|
||||
- openai_responses
|
||||
- openai_chat
|
||||
- openai_chat_completion
|
||||
- anthropic
|
||||
- ollama
|
||||
- bedrock
|
||||
- azure_openai_responses
|
||||
- azure_openai_chat
|
||||
- azure_openai_chat_completion
|
||||
- foundry_chat
|
||||
"""
|
||||
|
||||
ClientName = Literal[
|
||||
"openai_responses",
|
||||
"openai_chat",
|
||||
"openai_chat_completion",
|
||||
"anthropic",
|
||||
"ollama",
|
||||
"bedrock",
|
||||
"azure_openai_responses",
|
||||
"azure_openai_chat",
|
||||
"azure_openai_chat_completion",
|
||||
"foundry_chat",
|
||||
]
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity.
|
||||
# Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py
|
||||
# and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def get_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
@@ -62,7 +60,7 @@ def get_client(client_name: ClientName) -> SupportsChatGetResponse[Any]:
|
||||
from agent_framework.anthropic import AnthropicClient
|
||||
from agent_framework.ollama import OllamaChatClient
|
||||
|
||||
if client_name == "openai_responses":
|
||||
if client_name == "openai_chat":
|
||||
return OpenAIChatClient()
|
||||
if client_name == "openai_chat_completion":
|
||||
return OpenAIChatCompletionClient()
|
||||
@@ -72,7 +70,7 @@ def get_client(client_name: ClientName) -> SupportsChatGetResponse[Any]:
|
||||
return OllamaChatClient()
|
||||
if client_name == "bedrock":
|
||||
return BedrockChatClient()
|
||||
if client_name == "azure_openai_responses":
|
||||
if client_name == "azure_openai_chat":
|
||||
return OpenAIChatClient(credential=AzureCliCredential())
|
||||
if client_name == "azure_openai_chat_completion":
|
||||
return OpenAIChatCompletionClient(credential=AzureCliCredential())
|
||||
@@ -86,7 +84,7 @@ def get_client(client_name: ClientName) -> SupportsChatGetResponse[Any]:
|
||||
raise ValueError(f"Unsupported client name: {client_name}")
|
||||
|
||||
|
||||
async def main(client_name: ClientName = "openai_responses") -> None:
|
||||
async def main(client_name: ClientName = "openai_chat") -> None:
|
||||
"""Run a basic prompt using a selected built-in client."""
|
||||
client = get_client(client_name)
|
||||
|
||||
@@ -122,7 +120,7 @@ async def main(client_name: ClientName = "openai_responses") -> None:
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main("openai_responses"))
|
||||
asyncio.run(main("openai_chat"))
|
||||
|
||||
|
||||
"""
|
||||
|
||||
@@ -24,7 +24,7 @@ async def main() -> None:
|
||||
Creates a task for the chat request, waits briefly, then cancels it to show proper cleanup.
|
||||
|
||||
Configuration:
|
||||
- OpenAI model ID: Use "model_id" parameter or "OPENAI_MODEL" environment variable
|
||||
- OpenAI model ID: Use "model" parameter or "OPENAI_MODEL" environment variable
|
||||
- OpenAI API key: Use "api_key" parameter or "OPENAI_API_KEY" environment variable
|
||||
"""
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
@@ -102,7 +102,7 @@ class EchoingChatClient(BaseChatClient[OptionsT]):
|
||||
|
||||
response = ChatResponse(
|
||||
messages=[response_message],
|
||||
model_id="echo-model-v1",
|
||||
model="echo-model-v1",
|
||||
response_id=f"echo-resp-{random.randint(1000, 9999)}",
|
||||
)
|
||||
|
||||
@@ -120,7 +120,7 @@ class EchoingChatClient(BaseChatClient[OptionsT]):
|
||||
contents=[Content.from_text(char)],
|
||||
role="assistant",
|
||||
response_id=f"echo-stream-resp-{random.randint(1000, 9999)}",
|
||||
model_id="echo-model-v1",
|
||||
model="echo-model-v1",
|
||||
)
|
||||
await asyncio.sleep(stream_delay_seconds)
|
||||
|
||||
|
||||
@@ -0,0 +1,194 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
from __future__ import annotations
|
||||
|
||||
import asyncio
|
||||
from collections.abc import Awaitable, Callable
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import (
|
||||
Agent,
|
||||
FunctionInvocationContext,
|
||||
FunctionMiddleware,
|
||||
InMemoryHistoryProvider,
|
||||
Message,
|
||||
MiddlewareTermination,
|
||||
)
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
"""
|
||||
Compare Foundry agents with and without per-service-call chat history persistence.
|
||||
|
||||
This sample runs two otherwise identical Foundry agents with ``store=False`` so
|
||||
history stays local for both runs.
|
||||
|
||||
The sample adds a function middleware that raises ``MiddlewareTermination``
|
||||
immediately after the tool runs, so the request stops before a second model
|
||||
call.
|
||||
|
||||
That early termination is the important difference:
|
||||
|
||||
- Without per-service-call chat history persistence, the synthesized tool result is
|
||||
still written to local history.
|
||||
- With ``require_per_service_call_history_persistence=True``, that synthesized tool result is
|
||||
not written to local history.
|
||||
|
||||
The per-service-call persistence case matches service-side storage behavior. When a terminated
|
||||
request never sends the tool result back to the service, that result also never
|
||||
becomes part of the service-managed history.
|
||||
"""
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
def lookup_weather(
|
||||
location: Annotated[str, Field(description="The location to get the weather for.")],
|
||||
) -> str:
|
||||
"""Return a deterministic weather result for the requested location."""
|
||||
return f"The weather in {location} is sunny."
|
||||
|
||||
|
||||
class TerminateAfterToolMiddleware(FunctionMiddleware):
|
||||
"""Stop the tool loop after the first tool finishes."""
|
||||
|
||||
async def process(
|
||||
self,
|
||||
context: FunctionInvocationContext,
|
||||
call_next: Callable[[], Awaitable[None]],
|
||||
) -> None:
|
||||
"""Run the tool, then terminate the loop with that tool result."""
|
||||
await call_next()
|
||||
raise MiddlewareTermination(result=context.result)
|
||||
|
||||
|
||||
def _describe_message(message: Message) -> str:
|
||||
"""Render one stored message in a compact, readable format."""
|
||||
parts: list[str] = []
|
||||
for content in message.contents:
|
||||
if content.type == "text" and content.text:
|
||||
parts.append(content.text)
|
||||
elif content.type == "function_call":
|
||||
parts.append(f"function_call -> {content.name}({content.arguments})")
|
||||
elif content.type == "function_result":
|
||||
parts.append(f"function_result -> {content.result}")
|
||||
else:
|
||||
parts.append(content.type)
|
||||
|
||||
return f"{message.role}: {' | '.join(parts)}"
|
||||
|
||||
|
||||
def _includes_tool_result(messages: list[Message]) -> bool:
|
||||
"""Return whether any stored message contains a tool result."""
|
||||
return any(content.type == "function_result" for message in messages for content in message.contents)
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Run both comparison scenarios."""
|
||||
print("=== require_per_service_call_history_persistence when middleware terminates the tool loop ===\n")
|
||||
|
||||
# 1. Create one Foundry chat client that both agents will share.
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
query = "What is the weather in Seattle, and should I bring sunglasses?"
|
||||
|
||||
# 2. Create and run the agent without per-service-call persistence.
|
||||
agent_without_persistence = Agent(
|
||||
client=client,
|
||||
instructions=(
|
||||
"You are a weather assistant. Call lookup_weather exactly once before answering "
|
||||
"any weather question, then summarize the tool result in one short paragraph."
|
||||
),
|
||||
tools=[lookup_weather],
|
||||
context_providers=[InMemoryHistoryProvider()],
|
||||
middleware=[TerminateAfterToolMiddleware()],
|
||||
default_options={"tool_choice": "required", "store": False},
|
||||
)
|
||||
session_without_persistence = agent_without_persistence.create_session()
|
||||
await agent_without_persistence.run(
|
||||
query,
|
||||
session=session_without_persistence,
|
||||
)
|
||||
stored_messages_without_persistence = session_without_persistence.state[InMemoryHistoryProvider.DEFAULT_SOURCE_ID][
|
||||
"messages"
|
||||
]
|
||||
|
||||
print("=== Without per-service-call persistence ===")
|
||||
print("Loop terminated immediately after the tool finished.")
|
||||
print(f"Stored synthesized tool result: {_includes_tool_result(stored_messages_without_persistence)}")
|
||||
print("Stored history:")
|
||||
for index, message in enumerate(stored_messages_without_persistence, start=1):
|
||||
print(f" {index}. {_describe_message(message)}")
|
||||
print()
|
||||
|
||||
# 3. Create and run the agent with per-service-call persistence enabled.
|
||||
agent_with_persistence = Agent(
|
||||
client=client,
|
||||
instructions=(
|
||||
"You are a weather assistant. Call lookup_weather exactly once before answering "
|
||||
"any weather question, then summarize the tool result in one short paragraph."
|
||||
),
|
||||
tools=[lookup_weather],
|
||||
context_providers=[InMemoryHistoryProvider()],
|
||||
middleware=[TerminateAfterToolMiddleware()],
|
||||
require_per_service_call_history_persistence=True,
|
||||
default_options={"tool_choice": "required", "store": False},
|
||||
)
|
||||
session_with_persistence = agent_with_persistence.create_session()
|
||||
await agent_with_persistence.run(
|
||||
query,
|
||||
session=session_with_persistence,
|
||||
)
|
||||
stored_messages_with_persistence = session_with_persistence.state[InMemoryHistoryProvider.DEFAULT_SOURCE_ID][
|
||||
"messages"
|
||||
]
|
||||
|
||||
print("=== With per-service-call persistence ===")
|
||||
print("Loop terminated immediately after the tool finished.")
|
||||
print(f"Stored synthesized tool result: {_includes_tool_result(stored_messages_with_persistence)}")
|
||||
print("Stored history:")
|
||||
for index, message in enumerate(stored_messages_with_persistence, start=1):
|
||||
print(f" {index}. {_describe_message(message)}")
|
||||
print()
|
||||
|
||||
# 4. Summarize the effect of the flag.
|
||||
print(
|
||||
"Both runs used FoundryChatClient with store=False and terminated right after the tool. "
|
||||
"Without per-service-call persistence, local history still stored the synthesized tool result. "
|
||||
"With per-service-call persistence, local history stopped at the assistant function-call message instead, "
|
||||
"which matches service-side storage because the terminated tool result is never sent back to the service."
|
||||
)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
|
||||
|
||||
"""
|
||||
Sample output:
|
||||
=== require_per_service_call_history_persistence when middleware terminates the tool loop ===
|
||||
|
||||
=== Without per-service-call persistence ===
|
||||
Loop terminated immediately after the tool finished.
|
||||
Stored synthesized tool result: True
|
||||
Stored history:
|
||||
1. user: What is the weather in Seattle, and should I bring sunglasses?
|
||||
2. assistant: function_call -> lookup_weather({"location":"Seattle"})
|
||||
3. tool: function_result -> The weather in Seattle is sunny.
|
||||
|
||||
=== With per-service-call persistence ===
|
||||
Loop terminated immediately after the tool finished.
|
||||
Stored synthesized tool result: False
|
||||
Stored history:
|
||||
1. user: What is the weather in Seattle, and should I bring sunglasses?
|
||||
2. assistant: function_call -> lookup_weather({"location":"Seattle"})
|
||||
|
||||
Both runs used FoundryChatClient with store=False and terminated right after
|
||||
the tool. Without per-service-call persistence, local history still stored the
|
||||
synthesized tool result. With per-service-call persistence, local history
|
||||
stopped at the assistant function-call message instead, which matches
|
||||
service-side storage because the terminated tool result is never sent back to
|
||||
the service.
|
||||
"""
|
||||
@@ -11,7 +11,7 @@
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
import tiktoken
|
||||
import tiktoken # type: ignore
|
||||
from agent_framework import (
|
||||
Message,
|
||||
TokenizerProtocol,
|
||||
@@ -33,9 +33,9 @@ Key components:
|
||||
class TiktokenTokenizer(TokenizerProtocol):
|
||||
"""TokenizerProtocol implementation backed by tiktoken's o200k_base (gpt-4.1 and up default) encoding."""
|
||||
|
||||
def __init__(self, *, encoding_name: str = "o200k_base", model_name: str | None = None) -> None:
|
||||
if model_name is not None:
|
||||
self._encoding = tiktoken.encoding_for_model(model_name)
|
||||
def __init__(self, *, encoding_name: str = "o200k_base", model: str | None = None) -> None:
|
||||
if model is not None:
|
||||
self._encoding = tiktoken.encoding_for_model(model)
|
||||
else:
|
||||
self._encoding: Any = tiktoken.get_encoding(encoding_name)
|
||||
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Context Provider Samples
|
||||
|
||||
These samples demonstrate how to use context providers to enrich agent conversations with external knowledge — from custom logic to Azure AI Search (RAG) and memory services.
|
||||
|
||||
## Samples
|
||||
|
||||
| File / Folder | Description |
|
||||
|---------------|-------------|
|
||||
| [`simple_context_provider.py`](simple_context_provider.py) | Implement a custom context provider by extending `BaseContextProvider` to extract and inject structured user information across turns. |
|
||||
| [`azure_ai_foundry_memory.py`](azure_ai_foundry_memory.py) | Use `FoundryMemoryProvider` to add semantic memory — automatically retrieves, searches, and stores memories via Azure AI Foundry. |
|
||||
| [`azure_ai_search/`](azure_ai_search/) | Retrieval Augmented Generation (RAG) with Azure AI Search in semantic and agentic modes. See its own [README](azure_ai_search/README.md). |
|
||||
| [`mem0/`](mem0/) | Memory-powered context using the Mem0 integration (open-source and managed). See its own [README](mem0/README.md). |
|
||||
| [`redis/`](redis/) | Redis-backed context providers for conversation memory and sessions. See its own [README](redis/README.md). |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
**For `simple_context_provider.py`:**
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
- `FOUNDRY_MODEL`: Model deployment name
|
||||
- Azure CLI authentication (`az login`)
|
||||
|
||||
**For `azure_ai_foundry_memory.py`:**
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
- `FOUNDRY_MODEL`: Chat/responses model deployment name
|
||||
- `AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME`: Embedding model deployment name (e.g., `text-embedding-ada-002`)
|
||||
- Azure CLI authentication (`az login`)
|
||||
|
||||
See each subfolder's README for provider-specific prerequisites.
|
||||
@@ -33,7 +33,7 @@ rather than chat history. The memory store is deleted at the end of the run.
|
||||
Prerequisites:
|
||||
1. Set FOUNDRY_PROJECT_ENDPOINT environment variable
|
||||
2. Set FOUNDRY_MODEL for the chat/responses model
|
||||
3. Set AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME for the embedding model
|
||||
3. Set AZURE_OPENAI_EMBEDDING_MODEL for the embedding model
|
||||
4. Deploy both a chat model (e.g. gpt-4) and an embedding model (e.g. text-embedding-3-small)
|
||||
"""
|
||||
load_dotenv()
|
||||
@@ -55,7 +55,7 @@ async def main() -> None:
|
||||
)
|
||||
memory_store_definition = MemoryStoreDefaultDefinition(
|
||||
chat_model=os.environ["FOUNDRY_MODEL"],
|
||||
embedding_model=os.environ["AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME"],
|
||||
embedding_model=os.environ["AZURE_OPENAI_EMBEDDING_MODEL"],
|
||||
options=options,
|
||||
)
|
||||
print(f"Creating memory store '{memory_store_name}'...")
|
||||
|
||||
+6
-7
@@ -100,7 +100,7 @@ async def main() -> None:
|
||||
credential=AzureCliCredential() if not search_key else None,
|
||||
mode="agentic",
|
||||
azure_openai_resource_url=azure_openai_resource_url,
|
||||
model_model=model_deployment,
|
||||
model_deployment_name=model_deployment,
|
||||
# Optional: Configure retrieval behavior
|
||||
knowledge_base_output_mode="extractive_data", # or "answer_synthesis"
|
||||
retrieval_reasoning_effort="minimal", # or "medium", "low"
|
||||
@@ -110,13 +110,12 @@ async def main() -> None:
|
||||
# Create agent with search context provider
|
||||
async with (
|
||||
search_provider,
|
||||
FoundryChatClient(
|
||||
project_endpoint=project_endpoint,
|
||||
model_model=model_deployment,
|
||||
credential=AzureCliCredential(),
|
||||
) as client,
|
||||
Agent(
|
||||
client=client,
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=project_endpoint,
|
||||
model=model_deployment,
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
name="SearchAgent",
|
||||
instructions=(
|
||||
"You are a helpful assistant with advanced reasoning capabilities. "
|
||||
|
||||
+7
-8
@@ -32,7 +32,7 @@ Prerequisites:
|
||||
- AZURE_SEARCH_INDEX_NAME: Your search index name
|
||||
- FOUNDRY_PROJECT_ENDPOINT: Your Azure AI Foundry project endpoint
|
||||
- FOUNDRY_MODEL: Your model deployment name (e.g., "gpt-4o")
|
||||
- AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME: (Optional) Your Azure OpenAI embedding deployment for hybrid search
|
||||
- AZURE_OPENAI_EMBEDDING_MODEL: (Optional) Your Azure OpenAI embedding deployment for hybrid search
|
||||
- AZURE_OPENAI_ENDPOINT: (Optional) Your Azure OpenAI resource URL, required if using Azure OpenAI embeddings
|
||||
"""
|
||||
|
||||
@@ -56,7 +56,7 @@ async def main() -> None:
|
||||
project_endpoint = os.environ["FOUNDRY_PROJECT_ENDPOINT"]
|
||||
model_deployment = os.environ.get("FOUNDRY_MODEL", "gpt-4o")
|
||||
openai_endpoint = os.environ.get("AZURE_OPENAI_ENDPOINT")
|
||||
embedding_deployment = os.environ.get("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME")
|
||||
embedding_deployment = os.environ.get("AZURE_OPENAI_EMBEDDING_MODEL")
|
||||
|
||||
embedding_client = None
|
||||
if openai_endpoint and embedding_deployment:
|
||||
@@ -85,13 +85,12 @@ async def main() -> None:
|
||||
# Create agent with search context provider
|
||||
async with (
|
||||
search_provider,
|
||||
FoundryChatClient(
|
||||
project_endpoint=project_endpoint,
|
||||
model_model=model_deployment,
|
||||
credential=credential,
|
||||
) as client,
|
||||
Agent(
|
||||
client=client,
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=project_endpoint,
|
||||
model=model_deployment,
|
||||
credential=credential,
|
||||
),
|
||||
name="SearchAgent",
|
||||
instructions=(
|
||||
"You are a helpful assistant. Use the provided context from the "
|
||||
|
||||
@@ -9,7 +9,7 @@ This folder contains examples demonstrating how to use the Mem0 context provider
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`mem0_basic.py`](mem0_basic.py) | Basic example of using Mem0 context provider to store and retrieve user preferences across different conversation threads. |
|
||||
| [`mem0_sessions.py`](mem0_sessions.py) | Advanced example demonstrating different thread scoping strategies with Mem0. Covers global thread scope (memories shared across all operations), per-operation thread scope (memories isolated per thread), and multiple agents with different memory configurations for personal vs. work contexts. |
|
||||
| [`mem0_sessions.py`](mem0_sessions.py) | Example demonstrating different memory scoping strategies with Mem0. Covers user-scoped memory (memories shared across all sessions for the same user), agent-scoped memory (memories isolated per agent), and multiple agents with different memory configurations for personal vs. work contexts. |
|
||||
| [`mem0_oss.py`](mem0_oss.py) | Example of using the Mem0 Open Source self-hosted version as the context provider. Demonstrates setup and configuration for local deployment. |
|
||||
|
||||
## Prerequisites
|
||||
@@ -40,16 +40,8 @@ Set the following environment variables:
|
||||
|
||||
### Memory Scoping
|
||||
|
||||
The Mem0 context provider supports different scoping strategies:
|
||||
The Mem0 context provider supports scoping via identifiers:
|
||||
|
||||
- **Global Scope** (`scope_to_per_operation_thread_id=False`): Memories are shared across all conversation threads
|
||||
- **Thread Scope** (`scope_to_per_operation_thread_id=True`): Memories are isolated per conversation thread
|
||||
|
||||
### Memory Association
|
||||
|
||||
Mem0 records can be associated with different identifiers:
|
||||
|
||||
- `user_id`: Associate memories with a specific user
|
||||
- `agent_id`: Associate memories with a specific agent
|
||||
- `thread_id`: Associate memories with a specific conversation thread
|
||||
- `application_id`: Associate memories with an application context
|
||||
- **User scope** (`user_id`): Associate memories with a specific user, shared across all sessions
|
||||
- **Agent scope** (`agent_id`): Isolate memories per agent persona
|
||||
- **Application scope** (`application_id`): Associate memories with an application context
|
||||
|
||||
@@ -31,7 +31,7 @@ def retrieve_company_report(company_code: str, detailed: bool) -> str:
|
||||
async def main() -> None:
|
||||
"""Example of memory usage with Mem0 context provider."""
|
||||
print("=== Mem0 Context Provider Example ===")
|
||||
# Each record in Mem0 should be associated with agent_id or user_id or application_id or thread_id.
|
||||
# Each record in Mem0 should be associated with agent_id or user_id or application_id.
|
||||
# In this example, we associate Mem0 records with user_id.
|
||||
user_id = str(uuid.uuid4())
|
||||
# For Azure authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
@@ -57,12 +57,16 @@ async def main() -> None:
|
||||
# Now tell the agent the company code and the report format that you want to use
|
||||
# and it should be able to invoke the tool and return the report.
|
||||
query = "I always work with CNTS and I always want a detailed report format. Please remember and retrieve it."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Mem0 processes and indexes memories asynchronously.
|
||||
# Wait for memories to be indexed before querying in a new thread.
|
||||
# In production, consider implementing retry logic or using Mem0's
|
||||
# eventual consistency handling instead of a fixed delay.
|
||||
print("Waiting for memories to be processed...")
|
||||
await asyncio.sleep(12) # Empirically determined delay for Mem0 indexing
|
||||
await asyncio.sleep(15) # Empirically determined delay for Mem0 indexing
|
||||
print("\nRequest within a new session:")
|
||||
# Create a new session for the agent.
|
||||
# The new session has no context of the previous conversation.
|
||||
@@ -70,7 +74,10 @@ async def main() -> None:
|
||||
# Since we have the mem0 component in the session, the agent should be able to
|
||||
# retrieve the company report without asking for clarification, as it will
|
||||
# be able to remember the user preferences from Mem0 component.
|
||||
query = "Please retrieve my company report"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query, session=session)
|
||||
print(f"Agent: {result}")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
|
||||
@@ -32,7 +32,7 @@ def retrieve_company_report(company_code: str, detailed: bool) -> str:
|
||||
async def main() -> None:
|
||||
"""Example of memory usage with local Mem0 OSS context provider."""
|
||||
print("=== Mem0 Context Provider Example ===")
|
||||
# Each record in Mem0 should be associated with agent_id or user_id or application_id or thread_id.
|
||||
# Each record in Mem0 should be associated with agent_id or user_id or application_id.
|
||||
# In this example, we associate Mem0 records with user_id.
|
||||
user_id = str(uuid.uuid4())
|
||||
# For Azure authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
|
||||
@@ -1,7 +1,6 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import uuid
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
@@ -27,103 +26,84 @@ def get_user_preferences(user_id: str) -> str:
|
||||
return preferences.get(user_id, "No specific preferences found")
|
||||
|
||||
|
||||
async def example_global_thread_scope() -> None:
|
||||
"""Example 1: Global thread_id scope (memories shared across all operations)."""
|
||||
print("1. Global Thread Scope Example:")
|
||||
async def example_user_scoped_memory() -> None:
|
||||
"""Example 1: User-scoped memory (memories shared across all sessions for the same user)."""
|
||||
print("1. User-Scoped Memory Example:")
|
||||
print("-" * 40)
|
||||
|
||||
global_thread_id = str(uuid.uuid4())
|
||||
user_id = "user123"
|
||||
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
Agent(
|
||||
client=FoundryChatClient(credential=credential),
|
||||
name="GlobalMemoryAssistant",
|
||||
name="UserMemoryAssistant",
|
||||
instructions="You are an assistant that remembers user preferences across conversations.",
|
||||
tools=get_user_preferences,
|
||||
context_providers=[
|
||||
Mem0ContextProvider(
|
||||
source_id="mem0",
|
||||
user_id=user_id,
|
||||
thread_id=global_thread_id,
|
||||
scope_to_per_operation_thread_id=False, # Share memories across all sessions
|
||||
)
|
||||
],
|
||||
) as global_agent,
|
||||
) as user_agent,
|
||||
):
|
||||
# Store some preferences in the global scope
|
||||
# Store some preferences
|
||||
query = "Remember that I prefer technical responses with code examples when discussing programming."
|
||||
print(f"User: {query}")
|
||||
result = await global_agent.run(query)
|
||||
result = await user_agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Create a new session - but memories should still be accessible due to global scope
|
||||
new_session = global_agent.create_session()
|
||||
# Create a new session - memories should still be accessible via user_id scoping
|
||||
new_session = user_agent.create_session()
|
||||
query = "What do you know about my preferences?"
|
||||
print(f"User (new session): {query}")
|
||||
result = await global_agent.run(query, session=new_session)
|
||||
result = await user_agent.run(query, session=new_session)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
|
||||
async def example_per_operation_thread_scope() -> None:
|
||||
"""Example 2: Per-operation thread scope (memories isolated per session).
|
||||
async def example_agent_scoped_memory() -> None:
|
||||
"""Example 2: Agent-scoped memory (memories isolated per agent_id).
|
||||
|
||||
Note: When scope_to_per_operation_thread_id=True, the provider is bound to a single session
|
||||
throughout its lifetime. Use the same session object for all operations with that provider.
|
||||
Note: Use different agent_id values to isolate memories between different
|
||||
agent personas, even when the user_id is the same.
|
||||
"""
|
||||
print("2. Per-Operation Thread Scope Example:")
|
||||
print("2. Agent-Scoped Memory Example:")
|
||||
print("-" * 40)
|
||||
|
||||
user_id = "user123"
|
||||
|
||||
async with (
|
||||
AzureCliCredential() as credential,
|
||||
Agent(
|
||||
client=FoundryChatClient(credential=credential),
|
||||
name="ScopedMemoryAssistant",
|
||||
instructions="You are an assistant with thread-scoped memory.",
|
||||
instructions="You are an assistant with agent-scoped memory.",
|
||||
tools=get_user_preferences,
|
||||
context_providers=[
|
||||
Mem0ContextProvider(
|
||||
source_id="mem0",
|
||||
user_id=user_id,
|
||||
scope_to_per_operation_thread_id=True, # Isolate memories per session
|
||||
agent_id="scoped_assistant",
|
||||
)
|
||||
],
|
||||
) as scoped_agent,
|
||||
):
|
||||
# Create a specific session for this scoped provider
|
||||
dedicated_session = scoped_agent.create_session()
|
||||
|
||||
# Store some information in the dedicated session
|
||||
query = "Remember that for this conversation, I'm working on a Python project about data analysis."
|
||||
print(f"User (dedicated session): {query}")
|
||||
result = await scoped_agent.run(query, session=dedicated_session)
|
||||
query = (
|
||||
"Remember that I'm working on a Python project about data analysis "
|
||||
"and I prefer using pandas and matplotlib."
|
||||
)
|
||||
print(f"User: {query}")
|
||||
result = await scoped_agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Test memory retrieval in the same dedicated session
|
||||
query = "What project am I working on?"
|
||||
print(f"User (same dedicated session): {query}")
|
||||
result = await scoped_agent.run(query, session=dedicated_session)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Store more information in the same session
|
||||
query = "Also remember that I prefer using pandas and matplotlib for this project."
|
||||
print(f"User (same dedicated session): {query}")
|
||||
result = await scoped_agent.run(query, session=dedicated_session)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Test comprehensive memory retrieval
|
||||
new_session = scoped_agent.create_session()
|
||||
query = "What do you know about my current project and preferences?"
|
||||
print(f"User (same dedicated session): {query}")
|
||||
result = await scoped_agent.run(query, session=dedicated_session)
|
||||
print(f"User (new session): {query}")
|
||||
result = await scoped_agent.run(query, session=new_session)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
|
||||
async def example_multiple_agents() -> None:
|
||||
"""Example 3: Multiple agents with different thread configurations."""
|
||||
print("3. Multiple Agents with Different Thread Configurations:")
|
||||
"""Example 3: Multiple agents with different memory configurations."""
|
||||
print("3. Multiple Agents with Different Memory Configurations:")
|
||||
print("-" * 40)
|
||||
|
||||
agent_id_1 = "agent_personal"
|
||||
@@ -178,11 +158,11 @@ async def example_multiple_agents() -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Run all Mem0 thread management examples."""
|
||||
print("=== Mem0 Thread Management Example ===\n")
|
||||
"""Run all Mem0 memory management examples."""
|
||||
print("=== Mem0 Memory Management Example ===\n")
|
||||
|
||||
await example_global_thread_scope()
|
||||
await example_per_operation_thread_scope()
|
||||
await example_user_scoped_memory()
|
||||
await example_agent_scoped_memory()
|
||||
await example_multiple_agents()
|
||||
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ This folder contains an example demonstrating how to use the Redis context provi
|
||||
| [`azure_redis_conversation.py`](azure_redis_conversation.py) | Demonstrates conversation persistence with RedisHistoryProvider and Azure Redis with Azure AD (Entra ID) authentication using credential provider. |
|
||||
| [`redis_basics.py`](redis_basics.py) | Shows standalone provider usage and agent integration. Demonstrates writing messages to Redis, retrieving context via full‑text or hybrid vector search, and persisting preferences across threads. Also includes a simple tool example whose outputs are remembered. |
|
||||
| [`redis_conversation.py`](redis_conversation.py) | Simple example showing conversation persistence with RedisContextProvider using traditional connection string authentication. |
|
||||
| [`redis_sessions.py`](redis_sessions.py) | Demonstrates thread scoping. Includes: (1) global thread scope with a fixed `thread_id` shared across operations; (2) per‑operation thread scope where `scope_to_per_operation_thread_id=True` binds memory to a single thread for the provider's lifetime; and (3) multiple agents with isolated memory via different `agent_id` values. |
|
||||
| [`redis_sessions.py`](redis_sessions.py) | Demonstrates memory scoping strategies. Includes: (1) global memory scope with `application_id`, `agent_id`, and `user_id` shared across operations; (2) hybrid vector search using a custom OpenAI vectorizer for richer context retrieval; and (3) multiple agents with isolated memory via different `agent_id` values. |
|
||||
|
||||
|
||||
## Prerequisites
|
||||
@@ -61,8 +61,7 @@ The provider supports both full‑text only and hybrid vector search:
|
||||
|
||||
- Set `vectorizer_choice` to `"openai"` or `"hf"` to enable embeddings and hybrid search.
|
||||
- When using a vectorizer, also set `vector_field_name` (e.g., `"vector"`).
|
||||
- Partition fields for scoping memory: `application_id`, `agent_id`, `user_id`, `thread_id`.
|
||||
- Thread scoping: `scope_to_per_operation_thread_id=True` isolates memory per operation thread.
|
||||
- Partition fields for scoping memory: `application_id`, `agent_id`, `user_id`.
|
||||
- Index management: `index_name`, `overwrite_redis_index`, `drop_redis_index`.
|
||||
|
||||
## What the example does
|
||||
@@ -104,8 +103,8 @@ You should see the agent responses and, when using embeddings, context retrieved
|
||||
|
||||
### Memory scoping
|
||||
|
||||
- Global scope: set `application_id`, `agent_id`, `user_id`, or `thread_id` on the provider to filter memory.
|
||||
- Per‑operation thread scope: set `scope_to_per_operation_thread_id=True` to isolate memory to the current thread created by the framework.
|
||||
- Global scope: set `application_id`, `agent_id`, or `user_id` on the provider to filter memory.
|
||||
- Agent isolation: use different `agent_id` values to keep memories separated for different agent personas.
|
||||
|
||||
### Hybrid vector search (optional)
|
||||
|
||||
@@ -118,7 +117,7 @@ You should see the agent responses and, when using embeddings, context retrieved
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
- Ensure at least one of `application_id`, `agent_id`, `user_id`, or `thread_id` is set; the provider requires a scope.
|
||||
- Ensure at least one of `application_id`, `agent_id`, or `user_id` is set; the provider requires a scope.
|
||||
- Verify `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL` are set for the chat client.
|
||||
- If using embeddings, verify `OPENAI_API_KEY` is set and reachable.
|
||||
- Make sure Redis exposes RediSearch (Redis Stack image or managed service with search enabled).
|
||||
|
||||
@@ -30,9 +30,10 @@ from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.redis import RedisHistoryProvider
|
||||
from azure.identity import AzureCliCredential
|
||||
from azure.identity.aio import AzureCliCredential as AsyncAzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from redis.credentials import CredentialProvider
|
||||
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
load_dotenv()
|
||||
|
||||
|
||||
class AzureCredentialProvider(CredentialProvider):
|
||||
|
||||
@@ -100,7 +100,7 @@ def search_flights(origin_airport_code: str, destination_airport_code: str, deta
|
||||
|
||||
|
||||
def create_chat_client() -> FoundryChatClient:
|
||||
"""Create an Azure OpenAI Responses client using a Foundry project endpoint."""
|
||||
"""Create a FoundryChatClient using a Foundry project endpoint."""
|
||||
return FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
|
||||
@@ -1,17 +1,18 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Redis Context Provider: Thread scoping examples
|
||||
"""Redis Context Provider: Memory scoping examples
|
||||
|
||||
This sample demonstrates how conversational memory can be scoped when using the
|
||||
Redis context provider. It covers three scenarios:
|
||||
|
||||
1) Global thread scope
|
||||
- Provide a fixed thread_id to share memories across operations/threads.
|
||||
1) Global memory scope
|
||||
- Use application_id, agent_id, and user_id to share memories across
|
||||
all operations/sessions.
|
||||
|
||||
2) Per-operation thread scope
|
||||
- Enable scope_to_per_operation_thread_id to bind the provider to a single
|
||||
thread for the lifetime of that provider instance. Use the same thread
|
||||
object for reads/writes with that provider.
|
||||
2) Hybrid vector search
|
||||
- Use a custom OpenAI vectorizer with the provider for hybrid vector search.
|
||||
Demonstrates combining full-text and semantic search for richer context
|
||||
retrieval.
|
||||
|
||||
3) Multiple agents with isolated memory
|
||||
- Use different agent_id values to keep memories separated for different
|
||||
@@ -23,7 +24,7 @@ Requirements:
|
||||
- Optionally an OpenAI API key for the chat client in this demo
|
||||
|
||||
Run:
|
||||
python redis_threads.py
|
||||
python redis_sessions.py
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
@@ -33,10 +34,12 @@ from agent_framework import Agent
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.redis import RedisContextProvider
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from redisvl.extensions.cache.embeddings import EmbeddingsCache
|
||||
from redisvl.utils.vectorize import OpenAITextVectorizer
|
||||
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# Default Redis URL for local Redis Stack.
|
||||
@@ -47,7 +50,7 @@ REDIS_URL = os.getenv("REDIS_URL", "redis://localhost:6379")
|
||||
# Please set OPENAI_API_KEY to use the OpenAI vectorizer.
|
||||
# For chat responses, also set FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL.
|
||||
def create_chat_client() -> FoundryChatClient:
|
||||
"""Create an Azure OpenAI Responses client using a Foundry project endpoint."""
|
||||
"""Create a FoundryChatClient using a Foundry project endpoint."""
|
||||
return FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
@@ -55,9 +58,9 @@ def create_chat_client() -> FoundryChatClient:
|
||||
)
|
||||
|
||||
|
||||
async def example_global_thread_scope() -> None:
|
||||
"""Example 1: Global thread_id scope (memories shared across all operations)."""
|
||||
print("1. Global Thread Scope Example:")
|
||||
async def example_global_memory_scope() -> None:
|
||||
"""Example 1: Global memory scope (memories shared across all operations)."""
|
||||
print("1. Global Memory Scope Example:")
|
||||
print("-" * 40)
|
||||
|
||||
client = create_chat_client()
|
||||
@@ -99,13 +102,13 @@ async def example_global_thread_scope() -> None:
|
||||
await provider.redis_index.delete()
|
||||
|
||||
|
||||
async def example_per_operation_thread_scope() -> None:
|
||||
"""Example 2: Per-operation thread scope (memories isolated per session).
|
||||
async def example_hybrid_vector_search() -> None:
|
||||
"""Example 2: Hybrid vector search with custom vectorizer.
|
||||
|
||||
Note: When scope_to_per_operation_thread_id=True, the provider is bound to a single session
|
||||
throughout its lifetime. Use the same session object for all operations with that provider.
|
||||
Demonstrates using a custom OpenAI vectorizer for hybrid vector search,
|
||||
combining full-text and semantic search for richer context retrieval.
|
||||
"""
|
||||
print("2. Per-Operation Thread Scope Example:")
|
||||
print("2. Hybrid Vector Search Example:")
|
||||
print("-" * 40)
|
||||
|
||||
client = create_chat_client()
|
||||
@@ -131,36 +134,33 @@ async def example_per_operation_thread_scope() -> None:
|
||||
|
||||
agent = Agent(
|
||||
client=client,
|
||||
name="ScopedMemoryAssistant",
|
||||
instructions="You are an assistant with thread-scoped memory.",
|
||||
name="HybridSearchAssistant",
|
||||
instructions="You are an assistant with hybrid vector search for richer context retrieval.",
|
||||
context_providers=[provider],
|
||||
)
|
||||
|
||||
# Create a specific session for this scoped provider
|
||||
dedicated_session = agent.create_session()
|
||||
|
||||
# Store some information in the dedicated session
|
||||
# Store some information
|
||||
query = "Remember that for this conversation, I'm working on a Python project about data analysis."
|
||||
print(f"User (dedicated session): {query}")
|
||||
result = await agent.run(query, session=dedicated_session)
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Test memory retrieval in the same dedicated session
|
||||
# Test memory retrieval via hybrid search
|
||||
query = "What project am I working on?"
|
||||
print(f"User (same dedicated session): {query}")
|
||||
result = await agent.run(query, session=dedicated_session)
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Store more information in the same session
|
||||
# Store more information
|
||||
query = "Also remember that I prefer using pandas and matplotlib for this project."
|
||||
print(f"User (same dedicated session): {query}")
|
||||
result = await agent.run(query, session=dedicated_session)
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Test comprehensive memory retrieval
|
||||
query = "What do you know about my current project and preferences?"
|
||||
print(f"User (same dedicated session): {query}")
|
||||
result = await agent.run(query, session=dedicated_session)
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result}\n")
|
||||
|
||||
# Clean up the Redis index
|
||||
@@ -168,8 +168,8 @@ async def example_per_operation_thread_scope() -> None:
|
||||
|
||||
|
||||
async def example_multiple_agents() -> None:
|
||||
"""Example 3: Multiple agents with different thread configurations (isolated via agent_id) but within 1 index."""
|
||||
print("3. Multiple Agents with Different Thread Configurations:")
|
||||
"""Example 3: Multiple agents with different memory configurations (isolated via agent_id) but within 1 index."""
|
||||
print("3. Multiple Agents with Different Memory Configurations:")
|
||||
print("-" * 40)
|
||||
|
||||
client = create_chat_client()
|
||||
@@ -247,9 +247,9 @@ async def example_multiple_agents() -> None:
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Redis Thread Scoping Examples ===\n")
|
||||
await example_global_thread_scope()
|
||||
await example_per_operation_thread_scope()
|
||||
print("=== Redis Memory Scoping Examples ===\n")
|
||||
await example_global_memory_scope()
|
||||
await example_hybrid_vector_search()
|
||||
await example_multiple_agents()
|
||||
|
||||
|
||||
|
||||
@@ -5,7 +5,7 @@ import os
|
||||
from contextlib import suppress
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, AgentSession, BaseContextProvider, SessionContext, SupportsChatGetResponse
|
||||
from agent_framework import Agent, AgentSession, ContextProvider, SessionContext, SupportsChatGetResponse
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
@@ -20,7 +20,7 @@ class UserInfo(BaseModel):
|
||||
age: int | None = None
|
||||
|
||||
|
||||
class UserInfoMemory(BaseContextProvider):
|
||||
class UserInfoMemory(ContextProvider):
|
||||
DEFAULT_SOURCE_ID = "user_info_memory"
|
||||
|
||||
def __init__(self, source_id: str = DEFAULT_SOURCE_ID, *, client: SupportsChatGetResponse, **kwargs: Any):
|
||||
@@ -50,9 +50,11 @@ class UserInfoMemory(BaseContextProvider):
|
||||
# Use the chat client to extract structured information
|
||||
result = await self._chat_client.get_response(
|
||||
messages=request_messages, # type: ignore
|
||||
instructions="Extract the user's name and age from the message if present. "
|
||||
"If not present return nulls.",
|
||||
options={"response_format": UserInfo},
|
||||
options={
|
||||
"instructions": "Extract the user's name and age from the message if present. "
|
||||
"If not present return nulls.",
|
||||
"response_format": UserInfo,
|
||||
},
|
||||
)
|
||||
|
||||
# Update user info with extracted data
|
||||
|
||||
@@ -0,0 +1,28 @@
|
||||
# Conversation & Session Management Samples
|
||||
|
||||
These samples demonstrate different approaches to managing conversation history and session state in Agent Framework.
|
||||
|
||||
## Samples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`suspend_resume_session.py`](suspend_resume_session.py) | Suspend and resume conversation sessions, comparing service-managed sessions (Azure AI Foundry) with in-memory sessions (OpenAI). |
|
||||
| [`custom_history_provider.py`](custom_history_provider.py) | Implement a custom history provider by extending `BaseHistoryProvider`, enabling conversation persistence in your preferred storage backend. |
|
||||
| [`redis_history_provider.py`](redis_history_provider.py) | Use Redis as a history provider for persistent conversation history storage across sessions. |
|
||||
|
||||
## Prerequisites
|
||||
|
||||
**For `suspend_resume_session.py`:**
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint (service-managed session)
|
||||
- `FOUNDRY_MODEL`: The Foundry model deployment name
|
||||
- `OPENAI_API_KEY`: Your OpenAI API key (in-memory session)
|
||||
- Azure CLI authentication (`az login`)
|
||||
|
||||
**For `custom_history_provider.py`:**
|
||||
- `OPENAI_API_KEY`: Your OpenAI API key
|
||||
|
||||
**For `redis_history_provider.py`:**
|
||||
- `OPENAI_API_KEY`: Your OpenAI API key
|
||||
- A running Redis server — default URL is `redis://localhost:6379`
|
||||
- Override via the `REDIS_URL` environment variable for remote or authenticated instances
|
||||
- Quickstart with Docker: `docker run -d --name redis-stack -p 6379:6379 redis/redis-stack-server:latest`
|
||||
@@ -4,7 +4,7 @@ import asyncio
|
||||
from collections.abc import Sequence
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, AgentSession, BaseHistoryProvider, Message
|
||||
from agent_framework import Agent, AgentSession, HistoryProvider, Message
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -20,7 +20,7 @@ preferred storage solution (database, file system, etc.).
|
||||
"""
|
||||
|
||||
|
||||
class CustomHistoryProvider(BaseHistoryProvider):
|
||||
class CustomHistoryProvider(HistoryProvider):
|
||||
"""Implementation of custom history provider.
|
||||
In real applications, this can be an implementation of relational database or vector store."""
|
||||
|
||||
|
||||
@@ -4,6 +4,7 @@ import asyncio
|
||||
|
||||
from agent_framework import Agent, AgentSession
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.openai import OpenAIChatCompletionClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
@@ -62,7 +63,7 @@ async def suspend_resume_in_memory_session() -> None:
|
||||
# OpenAI Chat Client is used as an example here,
|
||||
# other chat clients can be used as well.
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(),
|
||||
client=OpenAIChatCompletionClient(),
|
||||
name="MemoryBot",
|
||||
instructions="You are a helpful assistant that remembers our conversation.",
|
||||
)
|
||||
|
||||
@@ -68,7 +68,7 @@ Illustrates a basic agent using Azure OpenAI with structured responses.
|
||||
|
||||
**Key concepts**: Azure OpenAI integration, credential management, structured outputs
|
||||
|
||||
### 5. **OpenAI Responses Agent** ([`openai_responses_agent.py`](./openai_responses_agent.py))
|
||||
### 5. **OpenAI Responses Agent** ([`openai_agent.py`](./openai_agent.py))
|
||||
|
||||
Demonstrates the simplest possible agent using OpenAI directly.
|
||||
|
||||
@@ -159,7 +159,7 @@ agent_factory = AgentFactory(
|
||||
"MyProvider": {
|
||||
"package": "my_custom_module",
|
||||
"name": "MyCustomChatClient",
|
||||
"model_id_field": "model_id",
|
||||
"model_field": "model",
|
||||
}
|
||||
}
|
||||
)
|
||||
@@ -176,7 +176,7 @@ agent = agent_factory.create_agent_from_yaml_path(Path("custom_provider.yaml"))
|
||||
This allows you to extend the declarative framework with custom chat client implementations. The mapping requires:
|
||||
- **package**: The Python package/module to import from
|
||||
- **name**: The class name of your SupportsChatGetResponse implementation
|
||||
- **model_id_field**: The constructor parameter name that accepts the value of the `model.id` field from the YAML
|
||||
- **model_field**: The constructor parameter name that accepts the value of the `model.id` field from the YAML
|
||||
|
||||
You can reference your custom provider using either `Provider.ApiType` format or just `Provider` in your YAML configuration, as long as it matches the registered mapping.
|
||||
|
||||
|
||||
@@ -18,7 +18,7 @@ Prerequisites:
|
||||
- `pip install agent-framework-foundry agent-framework-declarative --pre`
|
||||
- Set the following environment variables in a .env file or your environment:
|
||||
- FOUNDRY_PROJECT_ENDPOINT
|
||||
- AZURE_OPENAI_MODEL
|
||||
- FOUNDRY_MODEL
|
||||
"""
|
||||
|
||||
|
||||
@@ -31,7 +31,7 @@ instructions: Specialized diagnostic and issue detection agent for systems with
|
||||
description: A agent that performs diagnostics on systems and can escalate issues when critical errors are detected.
|
||||
|
||||
model:
|
||||
id: =Env.AZURE_OPENAI_MODEL
|
||||
id: =Env.FOUNDRY_MODEL
|
||||
"""
|
||||
# create the agent from the yaml
|
||||
async with (
|
||||
|
||||
@@ -9,6 +9,20 @@ from dotenv import load_dotenv
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
This sample demonstrates creating an agent from a declarative YAML file specification.
|
||||
|
||||
It uses a MCP server to connect to the Microsoft Learn content and a FoundryChatClient.
|
||||
|
||||
The yaml also has some chat options set, such as temperature and topP.
|
||||
These options do not work with newer OpenAI models, so ensure to use a compatible model such as gpt-4o-mini.
|
||||
|
||||
Environment variables:
|
||||
- FOUNDRY_PROJECT_ENDPOINT: The endpoint URL for the Foundry project.
|
||||
- FOUNDRY_MODEL: The model ID to use for the agent, make sure it is compatible with the chat options specified in
|
||||
the yaml, or remove the options.
|
||||
"""
|
||||
|
||||
|
||||
async def main():
|
||||
"""Create an agent from a declarative yaml specification and run it."""
|
||||
|
||||
+1
-4
@@ -14,11 +14,8 @@ async def main():
|
||||
# get the path
|
||||
current_path = Path(__file__).parent
|
||||
yaml_path = current_path.parent.parent.parent.parent / "declarative-agents" / "agent-samples" / "openai" / "OpenAIResponses.yaml"
|
||||
# load the yaml from the path
|
||||
with yaml_path.open("r") as f:
|
||||
yaml_str = f.read()
|
||||
# create the agent from the yaml
|
||||
agent = AgentFactory(safe_mode=False).create_agent_from_yaml(yaml_str)
|
||||
agent = AgentFactory(safe_mode=False).create_agent_from_yaml_path(yaml_path)
|
||||
# use the agent
|
||||
response = await agent.run("Why is the sky blue, answer in Dutch?")
|
||||
# Use response.value with try/except for safe parsing
|
||||
@@ -0,0 +1,15 @@
|
||||
# Shared configuration for samples/02-agents/devui
|
||||
# Used by in_memory_mode.py, main.py, and as a fallback for discovered samples.
|
||||
# Run `az login` before starting Azure-backed samples.
|
||||
|
||||
# Microsoft Foundry samples
|
||||
FOUNDRY_PROJECT_ENDPOINT=https://your-project.services.ai.azure.com
|
||||
FOUNDRY_MODEL=gpt-4o
|
||||
|
||||
# Azure OpenAI workflow sample
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
|
||||
AZURE_OPENAI_RESPONSES_MODEL=gpt-4o
|
||||
# Optional fallback env name also supported by workflow_with_agents/workflow.py:
|
||||
AZURE_OPENAI_MODEL=gpt-4o
|
||||
# Optional if you need to override the default API version:
|
||||
AZURE_OPENAI_API_VERSION=2024-10-21
|
||||
@@ -16,76 +16,124 @@ DevUI is a sample application that provides:
|
||||
|
||||
## Quick Start
|
||||
|
||||
### Option 1: In-Memory Mode (Simplest)
|
||||
### Option 1: In-Memory Mode (Programmatic Registration)
|
||||
|
||||
Run a single sample directly. This demonstrates how to wrap agents and workflows programmatically without needing a directory structure:
|
||||
Run a single sample directly. This demonstrates how to register agents and workflows in code without using DevUI's directory discovery.
|
||||
|
||||
This sample uses Azure AI Foundry. Before running it:
|
||||
|
||||
1. Copy `.env.example` in this folder to `.env`, or export the same values in your shell
|
||||
2. Set `FOUNDRY_PROJECT_ENDPOINT` and `FOUNDRY_MODEL`
|
||||
3. Run `az login`
|
||||
|
||||
Then start the sample:
|
||||
|
||||
```bash
|
||||
cd python/samples/02-agents/devui
|
||||
python in_memory_mode.py
|
||||
```
|
||||
|
||||
This opens your browser at http://localhost:8090 with pre-configured agents and a basic workflow.
|
||||
This opens your browser at http://localhost:8090 with two Foundry-backed agents and a simple text transformation workflow.
|
||||
|
||||
### Option 2: Directory Discovery
|
||||
### Option 2: Directory Discovery with Shared Root `.env`
|
||||
|
||||
Launch DevUI to discover all samples in this folder:
|
||||
Run the folder-level launcher to load `samples/02-agents/devui/.env` and then start DevUI with directory discovery for this folder:
|
||||
|
||||
```bash
|
||||
cd python/samples/02-agents/devui
|
||||
devui
|
||||
python main.py
|
||||
```
|
||||
|
||||
This starts the server at http://localhost:8080 with all agents and workflows available.
|
||||
This starts the server at http://localhost:8080 with all discoverable agents and workflows available. The root `.env` acts as shared fallback configuration for discovered samples.
|
||||
|
||||
### Option 3: Directory Discovery with the `devui` CLI
|
||||
|
||||
If you prefer the CLI directly, you can still launch DevUI from this folder:
|
||||
|
||||
```bash
|
||||
cd python/samples/02-agents/devui
|
||||
devui .
|
||||
```
|
||||
|
||||
DevUI discovery checks for a sample-specific `.env` first and then falls back to `.env` in `samples/02-agents/devui/`.
|
||||
|
||||
## Sample Structure
|
||||
|
||||
Each agent/workflow follows a strict structure required by DevUI's discovery system:
|
||||
DevUI discovers samples from Python packages that export either `agent` or `workflow`.
|
||||
|
||||
Typical agent layout:
|
||||
|
||||
```
|
||||
agent_name/
|
||||
├── __init__.py # Must export: agent = Agent(...)
|
||||
├── __init__.py # Must export: agent = ...
|
||||
├── agent.py # Agent implementation
|
||||
└── .env.example # Example environment variables
|
||||
└── .env.example # Optional example environment variables
|
||||
```
|
||||
|
||||
Typical workflow layout:
|
||||
|
||||
```
|
||||
workflow_name/
|
||||
├── __init__.py # Must export: workflow = ...
|
||||
├── workflow.py # Workflow implementation
|
||||
├── workflow.yaml # Optional declarative definition
|
||||
└── .env.example # Optional example environment variables
|
||||
```
|
||||
|
||||
## Available Samples
|
||||
|
||||
### Agents
|
||||
|
||||
| Sample | Description | Features | Required Environment Variables |
|
||||
| ------------------------------------------------ | ------------------------------------------------------------------------------------------------- | -------------------------------------------------------------------------- | -------------------------------------------------------------------------------------------------- |
|
||||
| [**weather_agent_azure/**](weather_agent_azure/) | Weather agent using Azure OpenAI with API key authentication | Azure OpenAI integration, function calling, mock weather tools | `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_DEPLOYMENT_NAME`, `AZURE_OPENAI_ENDPOINT` |
|
||||
| [**foundry_agent/**](foundry_agent/) | Weather agent using Azure AI Agent (Foundry) with Azure CLI authentication (run `az login` first) | Azure AI Agent integration, Azure CLI authentication, mock weather tools | `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL` |
|
||||
| Sample | What it demonstrates | Required keys / auth |
|
||||
| ------ | -------------------- | -------------------- |
|
||||
| [**agent_weather/**](agent_weather/) | A richer Foundry-backed weather agent that shows chat middleware, function middleware, tool calling, and an approval-required tool alongside auto-approved tools. | `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, plus Azure CLI auth via `az login` |
|
||||
| [**agent_foundry/**](agent_foundry/) | A minimal Foundry-backed weather agent with current weather and forecast tools. Use this when you want the smallest possible directory-discovered agent sample. | `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, plus Azure CLI auth via `az login` |
|
||||
|
||||
### Workflows
|
||||
|
||||
| Sample | Description | Features | Required Environment Variables |
|
||||
| -------------------------------------------- | ----------------------------------------------------------------- | --------------------------------------------------------------------------------------------------------------------------- | ------------------------------------------------------------------------------------- |
|
||||
| [**declarative/**](declarative/) | Declarative YAML workflow with conditional branching | YAML-based workflow definition, conditional logic, no Python code required | None - uses mock data |
|
||||
| [**workflow_agents/**](workflow_agents/) | Content review workflow with agents as executors | Agents as workflow nodes, conditional routing based on structured outputs, quality-based paths (Writer -> Reviewer -> Editor/Publisher) | `AZURE_OPENAI_API_KEY`, `AZURE_OPENAI_DEPLOYMENT_NAME`, `AZURE_OPENAI_ENDPOINT` |
|
||||
| [**spam_workflow/**](spam_workflow/) | 5-step email spam detection workflow with branching logic | Sequential execution, conditional branching (spam vs. legitimate), multiple executors, mock spam detection | None - uses mock data |
|
||||
| [**fanout_workflow/**](fanout_workflow/) | Advanced data processing workflow with parallel execution | Fan-out/fan-in patterns, complex state management, multi-stage processing (validation -> transformation -> quality assurance) | None - uses mock data |
|
||||
| Sample | What it demonstrates | Required keys / auth |
|
||||
| ------ | -------------------- | -------------------- |
|
||||
| [**workflow_declarative/**](workflow_declarative/) | A YAML-defined workflow loaded through `WorkflowFactory`, with nested age-based branching and no model client code. | None |
|
||||
| [**workflow_with_agents/**](workflow_with_agents/) | A content review workflow that uses agents as executors and routes based on structured review output (`Writer -> Reviewer -> Editor/Publisher -> Summarizer`). | `AZURE_OPENAI_ENDPOINT`, plus `AZURE_OPENAI_RESPONSES_MODEL` or `AZURE_OPENAI_MODEL`; Azure CLI auth via `az login`; `AZURE_OPENAI_API_VERSION` is optional |
|
||||
| [**workflow_spam/**](workflow_spam/) | A multi-step spam detection workflow with human-in-the-loop approval, branching for spam vs. legitimate messages, and a final reporting step. | None |
|
||||
| [**workflow_fanout/**](workflow_fanout/) | A larger fan-out/fan-in data processing workflow with parallel validation, multiple transformations, QA, aggregation, and demo failure toggles. | None |
|
||||
|
||||
### Standalone Examples
|
||||
|
||||
| Sample | Description | Features |
|
||||
| ------------------------------------------ | ------------------------------------------------------------------------- | ------------------------------------------------------------------------------------------------------------------------------- |
|
||||
| [**in_memory_mode.py**](in_memory_mode.py) | Demonstrates programmatic entity registration without directory structure | In-memory agent and workflow registration, multiple entities served from a single file, includes basic workflow, simplest way to get started |
|
||||
| Sample | What it demonstrates | Required keys / auth |
|
||||
| ------ | -------------------- | -------------------- |
|
||||
| [**in_memory_mode.py**](in_memory_mode.py) | Registers multiple entities directly in Python: two Foundry-backed agents plus a simple workflow, all served from one file without directory discovery. | `FOUNDRY_PROJECT_ENDPOINT`, `FOUNDRY_MODEL`, plus Azure CLI auth via `az login` |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
Each sample that requires API keys includes a `.env.example` file. To use:
|
||||
For samples that require external services:
|
||||
|
||||
1. Copy `.env.example` to `.env` in the same directory
|
||||
2. Fill in your actual API keys
|
||||
3. DevUI automatically loads `.env` files from entity directories
|
||||
1. Copy `.env.example` to `.env`
|
||||
2. Fill in the required values
|
||||
3. Run `az login` for samples that use Azure CLI authentication
|
||||
|
||||
Directory discovery checks `.env` files in this order:
|
||||
|
||||
1. The entity directory itself, for example `agent_weather/.env`
|
||||
2. The root DevUI samples folder, `samples/02-agents/devui/.env`
|
||||
|
||||
That means the root `.env.example` can hold shared defaults for multiple samples, while a sample-specific `.env` can override those values when needed.
|
||||
|
||||
`in_memory_mode.py` and `main.py` both load `.env` from `samples/02-agents/devui/`, so the root `.env.example` in this folder is the right starting point for both commands.
|
||||
|
||||
Alternatively, set environment variables globally:
|
||||
|
||||
```bash
|
||||
export OPENAI_API_KEY="your-key-here"
|
||||
export OPENAI_CHAT_MODEL="gpt-4o"
|
||||
# Foundry-backed samples
|
||||
export FOUNDRY_PROJECT_ENDPOINT="https://your-project.services.ai.azure.com"
|
||||
export FOUNDRY_MODEL="gpt-4o"
|
||||
|
||||
# Azure OpenAI workflow_with_agents sample
|
||||
export AZURE_OPENAI_ENDPOINT="https://your-resource.openai.azure.com"
|
||||
export AZURE_OPENAI_RESPONSES_MODEL="gpt-4o"
|
||||
export AZURE_OPENAI_MODEL="gpt-4o"
|
||||
|
||||
az login
|
||||
```
|
||||
|
||||
## Using DevUI with Your Own Agents
|
||||
@@ -145,7 +193,7 @@ curl http://localhost:8080/v1/entities
|
||||
|
||||
## Troubleshooting
|
||||
|
||||
**Missing API keys**: Check your `.env` files or environment variables.
|
||||
**Missing credentials or settings**: Check your `.env` files, confirm the required variables for the sample you are running, and make sure `az login` has completed for Azure-authenticated samples.
|
||||
|
||||
**Import errors**: Make sure you've installed the devui package:
|
||||
|
||||
|
||||
@@ -0,0 +1,5 @@
|
||||
# Azure AI Foundry Configuration
|
||||
# Make sure to run 'az login' before starting devui
|
||||
|
||||
FOUNDRY_PROJECT_ENDPOINT=https://your-project.services.ai.azure.com
|
||||
FOUNDRY_MODEL=gpt-4o
|
||||
+1
-1
@@ -53,7 +53,7 @@ agent = Agent(
|
||||
name="FoundryWeatherAgent",
|
||||
client=FoundryChatClient(
|
||||
project_endpoint=os.environ.get("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
model_model=os.environ.get("FOUNDRY_MODEL"),
|
||||
model=os.environ.get("FOUNDRY_MODEL"),
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
instructions="""
|
||||
@@ -0,0 +1,5 @@
|
||||
# Azure AI Foundry Configuration
|
||||
# Make sure to run 'az login' before starting devui
|
||||
|
||||
FOUNDRY_PROJECT_ENDPOINT=https://your-project.services.ai.azure.com
|
||||
FOUNDRY_MODEL=gpt-4o
|
||||
+8
-5
@@ -22,6 +22,7 @@ from agent_framework import (
|
||||
)
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework_devui import register_cleanup
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -145,7 +146,7 @@ def send_email(
|
||||
|
||||
# Agent instance following Agent Framework conventions
|
||||
agent = Agent(
|
||||
name="AzureWeatherAgent",
|
||||
name="WeatherAgent",
|
||||
description="A helpful agent that provides weather information and forecasts",
|
||||
instructions="""
|
||||
You are a weather assistant. You can provide current weather information
|
||||
@@ -153,7 +154,9 @@ agent = Agent(
|
||||
weather information when asked.
|
||||
""",
|
||||
client=FoundryChatClient(
|
||||
api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""),
|
||||
project_endpoint=os.environ.get("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
model=os.environ.get("FOUNDRY_MODEL"),
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
tools=[get_weather, get_forecast, send_email],
|
||||
middleware=[security_filter_middleware, atlantis_location_filter_middleware],
|
||||
@@ -164,7 +167,7 @@ register_cleanup(agent, cleanup_resources)
|
||||
|
||||
|
||||
def main():
|
||||
"""Launch the Azure weather agent in DevUI."""
|
||||
"""Launch the Weather Agent in DevUI."""
|
||||
import logging
|
||||
|
||||
from agent_framework.devui import serve
|
||||
@@ -173,9 +176,9 @@ def main():
|
||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
logger.info("Starting Azure Weather Agent")
|
||||
logger.info("Starting Weather Agent")
|
||||
logger.info("Available at: http://localhost:8090")
|
||||
logger.info("Entity ID: agent_AzureWeatherAgent")
|
||||
logger.info("Entity ID: agent_WeatherAgent")
|
||||
|
||||
# Launch server with the agent
|
||||
serve(entities=[agent], port=8090, auto_open=True)
|
||||
@@ -1,15 +0,0 @@
|
||||
# Azure OpenAI Responses API Configuration
|
||||
# The Responses API supports PDF uploads, images, and other multimodal content.
|
||||
# Requires api-version 2025-03-01-preview or later.
|
||||
|
||||
# Option 1: Use API key authentication
|
||||
AZURE_OPENAI_API_KEY=your-azure-openai-api-key-here
|
||||
|
||||
# Option 2: Use Azure CLI authentication (run 'az login' first)
|
||||
# No API key needed - just leave AZURE_OPENAI_API_KEY unset
|
||||
|
||||
# Required: Azure OpenAI endpoint with Responses API support
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.cognitiveservices.azure.com/
|
||||
|
||||
# Required: Deployment name (must support Responses API)
|
||||
FOUNDRY_MODEL=gpt-4.1-mini
|
||||
@@ -1,6 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
"""Azure Responses Agent sample for DevUI."""
|
||||
|
||||
from .agent import agent
|
||||
|
||||
__all__ = ["agent"]
|
||||
@@ -1,128 +0,0 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
"""Sample agent using Azure OpenAI Responses API for Agent Framework DevUI.
|
||||
|
||||
This agent uses the Responses API which supports:
|
||||
- PDF file uploads
|
||||
- Image uploads
|
||||
- Audio inputs
|
||||
- And other multimodal content
|
||||
|
||||
The Chat Completions API (FoundryChatClient) does NOT support PDF uploads.
|
||||
Use this agent when you need to process documents or other file types.
|
||||
|
||||
Required environment variables:
|
||||
- AZURE_OPENAI_ENDPOINT: Your Azure OpenAI endpoint
|
||||
- FOUNDRY_MODEL: Deployment name for Responses API
|
||||
(falls back to FOUNDRY_MODEL if not set)
|
||||
- AZURE_OPENAI_API_KEY: Your API key (or use Azure CLI auth)
|
||||
"""
|
||||
|
||||
import logging
|
||||
import os
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
# Get deployment name - try responses-specific env var first, fall back to chat deployment
|
||||
_deployment_name = os.environ.get(
|
||||
"FOUNDRY_MODEL",
|
||||
os.environ.get("FOUNDRY_MODEL", ""),
|
||||
)
|
||||
|
||||
# Get endpoint - try responses-specific env var first, fall back to default
|
||||
_endpoint = os.environ.get(
|
||||
"AZURE_OPENAI_RESPONSES_ENDPOINT",
|
||||
os.environ.get("AZURE_OPENAI_ENDPOINT", ""),
|
||||
)
|
||||
|
||||
|
||||
def analyze_content(
|
||||
query: Annotated[str, "What to analyze or extract from the uploaded content"],
|
||||
) -> str:
|
||||
"""Analyze uploaded content based on the user's query.
|
||||
|
||||
This is a placeholder - the actual analysis is done by the model
|
||||
when processing the uploaded files.
|
||||
"""
|
||||
return f"Analyzing content for: {query}"
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
@tool(approval_mode="never_require")
|
||||
def summarize_document(
|
||||
length: Annotated[str, "Desired summary length: 'brief', 'medium', or 'detailed'"] = "medium",
|
||||
) -> str:
|
||||
"""Generate a summary of the uploaded document."""
|
||||
return f"Generating {length} summary of the document..."
|
||||
|
||||
|
||||
@tool(approval_mode="never_require")
|
||||
def extract_key_points(
|
||||
max_points: Annotated[int, "Maximum number of key points to extract"] = 5,
|
||||
) -> str:
|
||||
"""Extract key points from the uploaded document."""
|
||||
return f"Extracting up to {max_points} key points..."
|
||||
|
||||
|
||||
# Agent using Azure OpenAI Responses API (supports PDF uploads!)
|
||||
agent = Agent(
|
||||
name="AzureResponsesAgent",
|
||||
description="An agent that can analyze PDFs, images, and other documents using Azure OpenAI Responses API",
|
||||
instructions="""
|
||||
You are a helpful document analysis assistant. You can:
|
||||
|
||||
1. Analyze uploaded PDF documents and extract information
|
||||
2. Summarize document contents
|
||||
3. Answer questions about uploaded files
|
||||
4. Extract key points and insights
|
||||
|
||||
When a user uploads a file, carefully analyze its contents and provide
|
||||
helpful, accurate information based on what you find.
|
||||
|
||||
For PDFs, you can read and understand the text, tables, and structure.
|
||||
For images, you can describe what you see and extract any text.
|
||||
""",
|
||||
client=FoundryChatClient(
|
||||
model=_deployment_name,
|
||||
endpoint=_endpoint,
|
||||
api_version="2025-03-01-preview", # Required for Responses API
|
||||
),
|
||||
tools=[summarize_document, extract_key_points],
|
||||
)
|
||||
|
||||
|
||||
def main():
|
||||
"""Launch the Azure Responses agent in DevUI."""
|
||||
from agent_framework_devui import serve
|
||||
|
||||
logging.basicConfig(level=logging.INFO, format="%(message)s")
|
||||
|
||||
logger.info("=" * 60)
|
||||
logger.info("Starting Azure Responses Agent")
|
||||
logger.info("=" * 60)
|
||||
logger.info("")
|
||||
logger.info("This agent uses the Azure OpenAI Responses API which supports:")
|
||||
logger.info(" - PDF file uploads")
|
||||
logger.info(" - Image uploads")
|
||||
logger.info(" - Audio inputs")
|
||||
logger.info("")
|
||||
logger.info("Try uploading a PDF and asking questions about it!")
|
||||
logger.info("")
|
||||
logger.info("Required environment variables:")
|
||||
logger.info(" - AZURE_OPENAI_ENDPOINT")
|
||||
logger.info(" - FOUNDRY_MODEL")
|
||||
logger.info(" - AZURE_OPENAI_API_KEY (or use Azure CLI auth)")
|
||||
logger.info("")
|
||||
|
||||
serve(entities=[agent], port=8090, auto_open=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
@@ -1,6 +0,0 @@
|
||||
# Azure AI Foundry Configuration
|
||||
# Get your credentials from Azure AI Foundry portal
|
||||
# Make sure to run 'az login' before starting devui
|
||||
|
||||
FOUNDRY_PROJECT_ENDPOINT=https://your-project.api.azureml.ms
|
||||
FOUNDRY_MODEL=gpt-4o
|
||||
@@ -20,6 +20,7 @@ from agent_framework import (
|
||||
)
|
||||
from agent_framework.devui import serve
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity.aio import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from typing_extensions import Never
|
||||
|
||||
@@ -80,14 +81,13 @@ def main():
|
||||
|
||||
# Create Azure OpenAI chat client
|
||||
client = FoundryChatClient(
|
||||
api_key=os.environ.get("AZURE_OPENAI_API_KEY"),
|
||||
model=os.environ["FOUNDRY_MODEL"],
|
||||
endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"),
|
||||
api_version=os.environ.get("AZURE_OPENAI_API_VERSION", "2024-10-21"),
|
||||
project_endpoint=os.environ.get("FOUNDRY_PROJECT_ENDPOINT"),
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
# Create agents
|
||||
weather_agent = Agent(
|
||||
weather_assistant = Agent(
|
||||
name="weather-assistant",
|
||||
description="Provides weather information and time",
|
||||
instructions=(
|
||||
@@ -120,7 +120,7 @@ def main():
|
||||
)
|
||||
|
||||
# Collect entities for serving
|
||||
entities = [weather_agent, simple_agent, basic_workflow]
|
||||
entities = [weather_assistant, simple_agent, basic_workflow]
|
||||
|
||||
logger.info("Starting DevUI on http://localhost:8090")
|
||||
logger.info("Entities available:")
|
||||
|
||||
@@ -0,0 +1,32 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""Launch DevUI with folder discovery for the samples in this directory.
|
||||
|
||||
This sample demonstrates:
|
||||
- Loading a shared root `.env` file for the DevUI samples folder
|
||||
- Starting DevUI in directory discovery mode for this folder
|
||||
- Using root-level settings as fallbacks for discovered samples
|
||||
"""
|
||||
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework.devui import serve
|
||||
from dotenv import load_dotenv
|
||||
|
||||
|
||||
def main() -> None:
|
||||
"""Load the root .env file and launch DevUI with folder discovery."""
|
||||
samples_dir = Path(__file__).resolve().parent
|
||||
|
||||
# 1. Load shared defaults for the samples in this folder.
|
||||
load_dotenv(samples_dir / ".env")
|
||||
|
||||
# 2. Start DevUI and discover entities from this directory.
|
||||
serve(entities_dir=str(samples_dir), auto_open=True)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
main()
|
||||
|
||||
# Sample output:
|
||||
# Starting Agent Framework DevUI on 127.0.0.1:8080
|
||||
@@ -1,6 +0,0 @@
|
||||
# Azure OpenAI API Configuration
|
||||
# Get your credentials from Azure Portal
|
||||
|
||||
AZURE_OPENAI_API_KEY=your-azure-openai-api-key-here
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
|
||||
@@ -1,7 +0,0 @@
|
||||
# Azure OpenAI API Configuration
|
||||
# Get your credentials from Azure Portal
|
||||
|
||||
AZURE_OPENAI_API_KEY=your-azure-openai-api-key-here
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
|
||||
AZURE_OPENAI_API_VERSION=2024-10-21
|
||||
@@ -0,0 +1,9 @@
|
||||
# Azure OpenAI configuration for the Responses-based workflow sample
|
||||
# This sample uses Azure CLI auth, so run `az login` before starting DevUI.
|
||||
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com
|
||||
AZURE_OPENAI_RESPONSES_MODEL=gpt-4o
|
||||
# Optional fallback env name also supported by the client:
|
||||
# AZURE_OPENAI_MODEL=gpt-4o
|
||||
# Optional if you need to override the default API version:
|
||||
AZURE_OPENAI_API_VERSION=2024-10-21
|
||||
+9
-3
@@ -18,7 +18,8 @@ import os
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, AgentExecutorResponse, WorkflowBuilder
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.openai import OpenAIChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import BaseModel
|
||||
|
||||
@@ -62,8 +63,13 @@ def is_approved(message: Any) -> bool:
|
||||
return True
|
||||
|
||||
|
||||
# Create Azure OpenAI chat client
|
||||
client = FoundryChatClient(api_key=os.environ.get("AZURE_OPENAI_API_KEY", ""))
|
||||
# Create Azure OpenAI Responses chat client
|
||||
client = OpenAIChatClient(
|
||||
model=os.environ.get("AZURE_OPENAI_RESPONSES_MODEL") or os.environ.get("AZURE_OPENAI_MODEL"),
|
||||
azure_endpoint=os.environ.get("AZURE_OPENAI_ENDPOINT"),
|
||||
api_version=os.environ.get("AZURE_OPENAI_API_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
# Create Writer agent - generates content
|
||||
writer = Agent(
|
||||
@@ -12,7 +12,7 @@ import asyncio
|
||||
import pathlib
|
||||
|
||||
from agent_framework import Content
|
||||
from agent_framework_azure_ai import AzureAIInferenceEmbeddingClient
|
||||
from agent_framework.azure import AzureAIInferenceEmbeddingClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
@@ -24,12 +24,16 @@ Azure AI Inference embedding client with the Cohere-embed-v3-english model.
|
||||
Images are passed as ``Content`` objects created with ``Content.from_data()``.
|
||||
|
||||
Prerequisites:
|
||||
Set the following environment variables or add them to a .env file:
|
||||
- AZURE_AI_INFERENCE_ENDPOINT: Your Azure AI model inference endpoint URL
|
||||
Deploy an embedding model in Azure AI Inference that supports image inputs, such as Cohere-embed-v3-english.
|
||||
|
||||
The details page for that model, has a target URI and a Key, which should be set in environment variables or a .env
|
||||
file as follows, the target URI should append the `/models` path:
|
||||
- AZURE_AI_INFERENCE_ENDPOINT: Your Azure AI model inference endpoint URL, for instance:
|
||||
https://<apim-instance>.azure-api.net/<foundry-instance>/models
|
||||
- AZURE_AI_INFERENCE_API_KEY: Your API key
|
||||
- AZURE_AI_INFERENCE_EMBEDDING_MODEL_ID: The text embedding model name
|
||||
- AZURE_AI_INFERENCE_EMBEDDING_MODEL: The text embedding model name
|
||||
(e.g. "text-embedding-3-small")
|
||||
- AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL_ID: The image embedding model name
|
||||
- AZURE_AI_INFERENCE_IMAGE_EMBEDDING_MODEL: The image embedding model name
|
||||
(e.g. "Cohere-embed-v3-english")
|
||||
"""
|
||||
|
||||
@@ -45,7 +49,7 @@ async def main() -> None:
|
||||
result = await client.get_embeddings([image_content])
|
||||
print(f"Image embedding dimensions: {result[0].dimensions}")
|
||||
print(f"First 5 values: {result[0].vector[:5]}")
|
||||
print(f"Model: {result[0].model_id}")
|
||||
print(f"Model: {result[0].model}")
|
||||
print(f"Usage: {result.usage}")
|
||||
print()
|
||||
|
||||
@@ -73,15 +77,17 @@ if __name__ == "__main__":
|
||||
|
||||
|
||||
"""
|
||||
Sample output (using Cohere-embed-v3-english):
|
||||
Sample output (using deployment: Cohere-embed-v3-english, which is Cohere's "embed-english-v3.0-image" model):
|
||||
Image embedding dimensions: 1024
|
||||
First 5 values: [0.023, -0.045, 0.067, -0.089, 0.011]
|
||||
Model: Cohere-embed-v3-english
|
||||
Usage: {'prompt_tokens': 1, 'total_tokens': 1}
|
||||
First 5 values: [0.029159546, -0.007926941, -0.0032978058, -0.0030403137, -0.012786865]
|
||||
Model: embed-english-v3.0-image
|
||||
Usage: {'input_token_count': 1000, 'output_token_count': 0}
|
||||
|
||||
Image+text (separate) results:
|
||||
Text embedding dimensions: 1536
|
||||
First 5 values: [-0.019439403, 0.015791258, 0.012358093, 0.0028533707, -0.01649483]
|
||||
Image embedding dimensions: 1024
|
||||
First 5 values: [0.029159546, -0.007926941, -0.0032978058, -0.0030403137, -0.012786865]
|
||||
|
||||
Document embedding dimensions: 1024
|
||||
First 5 values: [0.029159546, -0.007926941, -0.0032978058, -0.0030403137, -0.012786865]
|
||||
"""
|
||||
|
||||
@@ -14,7 +14,7 @@ from dotenv import load_dotenv
|
||||
Prerequisites:
|
||||
Set the following environment variables or add them to a local ``.env`` file:
|
||||
- ``AZURE_OPENAI_ENDPOINT``: Your Azure OpenAI endpoint URL
|
||||
- ``AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME``: The embedding deployment name
|
||||
- ``AZURE_OPENAI_EMBEDDING_MODEL``: The embedding deployment name
|
||||
- ``AZURE_OPENAI_API_VERSION``: Optional API version override
|
||||
|
||||
Sign in with ``az login`` before running the sample.
|
||||
@@ -27,7 +27,7 @@ async def main() -> None:
|
||||
"""Generate embeddings with Azure OpenAI."""
|
||||
async with AzureCliCredential() as credential:
|
||||
client = OpenAIEmbeddingClient(
|
||||
model=os.getenv("AZURE_OPENAI_EMBEDDING_DEPLOYMENT_NAME"),
|
||||
model=os.getenv("AZURE_OPENAI_EMBEDDING_MODEL"),
|
||||
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
|
||||
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
|
||||
credential=credential,
|
||||
|
||||
@@ -41,7 +41,7 @@ def response_matches_expected(response: str, expected_output: str) -> float:
|
||||
async def main() -> None:
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"],
|
||||
model=os.environ.get("AZURE_AI_MODEL_DEPLOYMENT_NAME", "gpt-4o"),
|
||||
model=os.environ.get("FOUNDRY_MODEL", "gpt-4o"),
|
||||
credential=AzureCliCredential(),
|
||||
)
|
||||
|
||||
|
||||
@@ -13,8 +13,8 @@ This folder contains focused middleware samples for `Agent`, chat clients, tools
|
||||
| [`exception_handling_with_middleware.py`](./exception_handling_with_middleware.py) | Shows how middleware can handle failures and recover cleanly. |
|
||||
| [`function_based_middleware.py`](./function_based_middleware.py) | Shows function-based agent and function middleware. |
|
||||
| [`middleware_termination.py`](./middleware_termination.py) | Demonstrates stopping a middleware pipeline early. |
|
||||
| [`override_result_with_middleware.py`](./override_result_with_middleware.py) | Shows how middleware can replace the normal result. |
|
||||
| [`runtime_context_delegation.py`](./runtime_context_delegation.py) | Demonstrates delegating work with runtime context data. |
|
||||
| [`override_result_with_middleware.py`](./override_result_with_middleware.py) | Shows how middleware can replace regular and streaming results, then post-process the final response. |
|
||||
| [`runtime_context_delegation.py`](./runtime_context_delegation.py) | Demonstrates delegating arguments with runtime context data. |
|
||||
| [`session_behavior_middleware.py`](./session_behavior_middleware.py) | Shows how middleware interacts with session-backed runs. |
|
||||
| [`shared_state_middleware.py`](./shared_state_middleware.py) | Demonstrates sharing mutable state across middleware invocations. |
|
||||
| [`usage_tracking_middleware.py`](./usage_tracking_middleware.py) | Demonstrates one chat middleware function that tracks per-call usage in non-streaming and streaming tool-loop runs. |
|
||||
|
||||
@@ -81,7 +81,7 @@ async def weather_override_middleware(context: ChatContext, call_next: Callable[
|
||||
role="assistant",
|
||||
)
|
||||
|
||||
context.result = ResponseStream(_override_stream())
|
||||
context.result = ResponseStream(_override_stream(), finalizer=ChatResponse.from_updates)
|
||||
else:
|
||||
# For non-streaming: just replace with a new message
|
||||
current_text = context.result.text if isinstance(context.result, ChatResponse) else ""
|
||||
@@ -99,12 +99,17 @@ async def validate_weather_middleware(context: ChatContext, call_next: Callable[
|
||||
return
|
||||
|
||||
if context.stream and isinstance(context.result, ResponseStream):
|
||||
result_stream = context.result
|
||||
|
||||
def _append_validation_note(response: ChatResponse) -> ChatResponse:
|
||||
response.messages.append(Message(role="assistant", text=validation_note))
|
||||
return response
|
||||
async def _validated_stream() -> AsyncIterable[ChatResponseUpdate]:
|
||||
async for update in result_stream:
|
||||
yield update
|
||||
yield ChatResponseUpdate(
|
||||
contents=[Content.from_text(text=validation_note)],
|
||||
role="assistant",
|
||||
)
|
||||
|
||||
context.result.with_finalizer(_append_validation_note)
|
||||
context.result = ResponseStream(_validated_stream(), finalizer=ChatResponse.from_updates)
|
||||
elif isinstance(context.result, ChatResponse):
|
||||
context.result.messages.append(Message(role="assistant", text=validation_note))
|
||||
|
||||
@@ -118,11 +123,11 @@ async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[]
|
||||
|
||||
validation_note = "Validation: weather data verified."
|
||||
|
||||
state = {"found_prefix": False}
|
||||
state = {"found_prefix": False, "found_validation": False}
|
||||
|
||||
def _sanitize(response: AgentResponse) -> AgentResponse:
|
||||
found_prefix = state["found_prefix"]
|
||||
found_validation = False
|
||||
found_validation = state["found_validation"]
|
||||
cleaned_messages: list[Message] = []
|
||||
|
||||
for message in response.messages:
|
||||
@@ -141,12 +146,14 @@ async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[]
|
||||
found_prefix = True
|
||||
text = text.replace("Weather Advisory:", "")
|
||||
|
||||
text = re.sub(r"\[\d+\]\s*", "", text)
|
||||
text = re.sub(r"\[\d+\]\s*", "", text).strip()
|
||||
if not text:
|
||||
continue
|
||||
|
||||
cleaned_messages.append(
|
||||
Message(
|
||||
role=message.role,
|
||||
text=text.strip(),
|
||||
text=text,
|
||||
author_name=message.author_name,
|
||||
message_id=message.message_id,
|
||||
additional_properties=message.additional_properties,
|
||||
@@ -166,19 +173,30 @@ async def agent_cleanup_middleware(context: AgentContext, call_next: Callable[[]
|
||||
if context.stream and isinstance(context.result, ResponseStream):
|
||||
|
||||
def _clean_update(update: AgentResponseUpdate) -> AgentResponseUpdate:
|
||||
cleaned_contents: list[Content] = []
|
||||
|
||||
for content in update.contents or []:
|
||||
if not content.text:
|
||||
cleaned_contents.append(content)
|
||||
continue
|
||||
text = content.text
|
||||
if "Weather Advisory:" in text:
|
||||
state["found_prefix"] = True
|
||||
text = text.replace("Weather Advisory:", "")
|
||||
if validation_note in text:
|
||||
state["found_validation"] = True
|
||||
text = text.replace(validation_note, "").strip()
|
||||
if not text:
|
||||
continue
|
||||
text = re.sub(r"\[\d+\]\s*", "", text)
|
||||
content.text = text
|
||||
cleaned_contents.append(content)
|
||||
|
||||
update.contents = cleaned_contents
|
||||
return update
|
||||
|
||||
context.result.with_transform_hook(_clean_update)
|
||||
context.result.with_finalizer(_sanitize)
|
||||
context.result.with_result_hook(_sanitize)
|
||||
elif isinstance(context.result, AgentResponse):
|
||||
context.result = _sanitize(context.result)
|
||||
|
||||
|
||||
@@ -6,6 +6,7 @@ from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, FunctionInvocationContext, function_middleware, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
@@ -43,6 +44,13 @@ Key Concepts:
|
||||
- MiddlewareTypes: Intercepts function calls to access/modify kwargs
|
||||
- Closure: Functions capturing variables from outer scope
|
||||
- kwargs Propagation: Automatic forwarding of runtime context through delegation chains
|
||||
|
||||
Environment Setup:
|
||||
- Configure Azure credentials (e.g., via Azure CLI)
|
||||
- Run `az login` to authenticate
|
||||
- Set FOUNDRY_PROJECT_ENDPOINT to your Azure AI Foundry project endpoint
|
||||
- Set FOUNDRY_MODEL to the model deployment name (for example: gpt-4o)
|
||||
|
||||
"""
|
||||
|
||||
|
||||
@@ -85,7 +93,7 @@ class SessionContextContainer:
|
||||
runtime_context = SessionContextContainer()
|
||||
|
||||
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production; see samples/02-agents/tools/function_tool_with_approval.py and samples/02-agents/tools/function_tool_with_approval_and_sessions.py.
|
||||
# NOTE: approval_mode="never_require" is for sample brevity. Use "always_require" in production.
|
||||
@tool(approval_mode="never_require")
|
||||
async def send_email(
|
||||
to: Annotated[str, Field(description="Recipient email address")],
|
||||
@@ -149,7 +157,7 @@ async def pattern_1_single_agent_with_closure() -> None:
|
||||
print("Use case: Single agent with multiple tools sharing runtime context")
|
||||
print()
|
||||
|
||||
client = FoundryChatClient(model="gpt-4o-mini")
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Create agent with both tools and shared context via middleware
|
||||
communication_agent = Agent(
|
||||
@@ -177,9 +185,11 @@ async def pattern_1_single_agent_with_closure() -> None:
|
||||
result1 = await communication_agent.run(
|
||||
user_query,
|
||||
# Runtime context passed as kwargs
|
||||
api_token="sk-test-token-xyz-789",
|
||||
user_id="user-12345",
|
||||
session_metadata={"tenant": "acme-corp", "region": "us-west"},
|
||||
function_invocation_kwargs={
|
||||
"api_token": "sk-test-token-xyz-789",
|
||||
"user_id": "user-12345",
|
||||
"session_metadata": {"tenant": "acme-corp", "region": "us-west"},
|
||||
},
|
||||
)
|
||||
|
||||
print(f"\nAgent: {result1.text}")
|
||||
@@ -195,9 +205,11 @@ async def pattern_1_single_agent_with_closure() -> None:
|
||||
result2 = await communication_agent.run(
|
||||
user_query2,
|
||||
# Different runtime context for this request
|
||||
api_token="sk-prod-token-abc-456",
|
||||
user_id="user-67890",
|
||||
session_metadata={"tenant": "store-inc", "region": "eu-central"},
|
||||
function_invocation_kwargs={
|
||||
"api_token": "sk-prod-token-abc-456",
|
||||
"user_id": "user-67890",
|
||||
"session_metadata": {"tenant": "store-inc", "region": "eu-central"},
|
||||
},
|
||||
)
|
||||
|
||||
print(f"\nAgent: {result2.text}")
|
||||
@@ -215,9 +227,11 @@ async def pattern_1_single_agent_with_closure() -> None:
|
||||
|
||||
result3 = await communication_agent.run(
|
||||
user_query3,
|
||||
api_token="sk-dev-token-def-123",
|
||||
user_id="user-11111",
|
||||
session_metadata={"tenant": "dev-team", "region": "us-east"},
|
||||
function_invocation_kwargs={
|
||||
"api_token": "sk-dev-token-def-123",
|
||||
"user_id": "user-11111",
|
||||
"session_metadata": {"tenant": "dev-team", "region": "us-east"},
|
||||
},
|
||||
)
|
||||
|
||||
print(f"\nAgent: {result3.text}")
|
||||
@@ -234,7 +248,9 @@ async def pattern_1_single_agent_with_closure() -> None:
|
||||
result4 = await communication_agent.run(
|
||||
user_query4,
|
||||
# Missing api_token - tools should handle gracefully
|
||||
user_id="user-22222",
|
||||
function_invocation_kwargs={
|
||||
"user_id": "user-22222",
|
||||
},
|
||||
)
|
||||
|
||||
print(f"\nAgent: {result4.text}")
|
||||
@@ -295,7 +311,7 @@ async def pattern_2_hierarchical_with_kwargs_propagation() -> None:
|
||||
print(f"[SMSAgent] Received runtime context: {list(context.kwargs.keys())}")
|
||||
await call_next()
|
||||
|
||||
client = FoundryChatClient(model="gpt-4o-mini")
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Create specialized sub-agents
|
||||
email_agent = Agent(
|
||||
@@ -341,9 +357,11 @@ async def pattern_2_hierarchical_with_kwargs_propagation() -> None:
|
||||
print("Test: Send email with runtime context\n")
|
||||
await coordinator.run(
|
||||
"Send an email to john@example.com with subject 'Meeting' and body 'See you at 2pm'",
|
||||
api_token="secret-token-abc",
|
||||
user_id="user-999",
|
||||
tenant_id="tenant-acme",
|
||||
function_invocation_kwargs={
|
||||
"api_token": "secret-token-abc",
|
||||
"user_id": "user-999",
|
||||
"tenant_id": "tenant-acme",
|
||||
},
|
||||
)
|
||||
|
||||
print(f"\n[Verification] EmailAgent received kwargs keys: {list(email_agent_kwargs.keys())}")
|
||||
@@ -400,7 +418,7 @@ async def pattern_3_hierarchical_with_middleware() -> None:
|
||||
|
||||
auth_middleware = AuthContextMiddleware()
|
||||
|
||||
client = FoundryChatClient(model="gpt-4o-mini")
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Sub-agent with validation middleware
|
||||
protected_agent = Agent(
|
||||
@@ -428,16 +446,20 @@ async def pattern_3_hierarchical_with_middleware() -> None:
|
||||
print("Test 1: Valid token\n")
|
||||
await coordinator.run(
|
||||
"Execute operation: backup_database",
|
||||
api_token="valid-token-xyz-789",
|
||||
user_id="admin-123",
|
||||
function_invocation_kwargs={
|
||||
"api_token": "valid-token-xyz-789",
|
||||
"user_id": "admin-123",
|
||||
},
|
||||
)
|
||||
|
||||
# Test with invalid token
|
||||
print("\nTest 2: Invalid token\n")
|
||||
await coordinator.run(
|
||||
"Execute operation: delete_records",
|
||||
api_token="invalid-token-bad",
|
||||
user_id="user-456",
|
||||
function_invocation_kwargs={
|
||||
"api_token": "invalid-token-bad",
|
||||
"user_id": "user-456",
|
||||
},
|
||||
)
|
||||
|
||||
print(f"\n[Validation Summary] Validated tokens: {len(auth_middleware.validated_tokens)}")
|
||||
|
||||
@@ -32,8 +32,8 @@ Set the following environment variables before running the examples:
|
||||
**For Azure OpenAI:**
|
||||
|
||||
- `AZURE_OPENAI_ENDPOINT`: Your Azure OpenAI endpoint
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`: The name of your Azure OpenAI chat model deployment
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`: The name of your Azure OpenAI responses model deployment
|
||||
- `AZURE_OPENAI_MODEL`: The name of your Azure OpenAI chat model deployment
|
||||
- `AZURE_OPENAI_MODEL`: The name of your Azure OpenAI responses model deployment
|
||||
|
||||
Optionally for Azure OpenAI:
|
||||
- `AZURE_OPENAI_API_VERSION`: The API version to use (default is `2024-10-21`)
|
||||
|
||||
@@ -23,7 +23,7 @@ async def test_image() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option. Requires AZURE_OPENAI_ENDPOINT and FOUNDRY_MODEL
|
||||
# environment variables to be set.
|
||||
# Alternatively, you can pass deployment_name explicitly:
|
||||
# Alternatively, you can pass model explicitly:
|
||||
# client = FoundryChatClient(credential=AzureCliCredential(), model="your-deployment-name")
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
image_uri = create_sample_image()
|
||||
|
||||
@@ -32,7 +32,7 @@ async def test_image() -> None:
|
||||
# For authentication, run `az login` command in terminal or replace AzureCliCredential with preferred
|
||||
# authentication option. Requires AZURE_OPENAI_ENDPOINT and FOUNDRY_MODEL
|
||||
# environment variables to be set.
|
||||
# Alternatively, you can pass deployment_name explicitly:
|
||||
# Alternatively, you can pass model explicitly:
|
||||
# client = FoundryChatClient(credential=AzureCliCredential(), model="your-deployment-name")
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
|
||||
@@ -6,7 +6,7 @@ import struct
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework import Content, Message
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.openai import OpenAIChatClient, OpenAIChatCompletionClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -14,6 +14,13 @@ load_dotenv()
|
||||
|
||||
ASSETS_DIR = Path(__file__).resolve().parents[2] / "shared" / "sample_assets"
|
||||
|
||||
"""
|
||||
Leverage multimodel capabilities of different models.
|
||||
|
||||
Uses the OpenAIChatClient and OpenAIChatCompletionClient to demonstrate multimodal input handling with the gpt-4o and gpt-4o-audio-preview models, respectively. The sample includes demonstrations for image, audio, and PDF inputs, showcasing how to create appropriate Content objects and send them in messages to the chat clients.
|
||||
|
||||
"""
|
||||
|
||||
|
||||
def load_sample_pdf() -> bytes:
|
||||
"""Read the bundled sample PDF for tests."""
|
||||
@@ -46,7 +53,7 @@ def create_sample_audio() -> str:
|
||||
|
||||
async def test_image() -> None:
|
||||
"""Test image analysis with OpenAI."""
|
||||
client = FoundryChatClient(model="gpt-4o")
|
||||
client = OpenAIChatClient(model="gpt-4o")
|
||||
|
||||
image_uri = create_sample_image()
|
||||
message = Message(
|
||||
@@ -63,7 +70,7 @@ async def test_image() -> None:
|
||||
|
||||
async def test_audio() -> None:
|
||||
"""Test audio analysis with OpenAI."""
|
||||
client = FoundryChatClient(model="gpt-4o-audio-preview")
|
||||
client = OpenAIChatCompletionClient(model="gpt-4o-audio-preview-2025-06-03")
|
||||
|
||||
audio_uri = create_sample_audio()
|
||||
message = Message(
|
||||
@@ -80,7 +87,7 @@ async def test_audio() -> None:
|
||||
|
||||
async def test_pdf() -> None:
|
||||
"""Test PDF document analysis with OpenAI."""
|
||||
client = FoundryChatClient(model="gpt-4o")
|
||||
client = OpenAIChatClient(model="gpt-4o")
|
||||
|
||||
pdf_bytes = load_sample_pdf()
|
||||
message = Message(
|
||||
|
||||
@@ -8,11 +8,12 @@ from typing import Annotated
|
||||
from agent_framework import Message, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.observability import enable_instrumentation
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from opentelemetry._logs import set_logger_provider
|
||||
from opentelemetry.metrics import set_meter_provider
|
||||
from opentelemetry.sdk._logs import LoggerProvider, LoggingHandler
|
||||
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogExporter
|
||||
from opentelemetry.sdk._logs.export import BatchLogRecordProcessor, ConsoleLogRecordExporter
|
||||
from opentelemetry.sdk.metrics import MeterProvider
|
||||
from opentelemetry.sdk.metrics.export import ConsoleMetricExporter, PeriodicExportingMetricReader
|
||||
from opentelemetry.sdk.resources import Resource
|
||||
@@ -37,7 +38,7 @@ def setup_logging():
|
||||
# Create and set a global logger provider for the application.
|
||||
logger_provider = LoggerProvider(resource=resource)
|
||||
# Log processors are initialized with an exporter which is responsible
|
||||
logger_provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogExporter()))
|
||||
logger_provider.add_log_record_processor(BatchLogRecordProcessor(ConsoleLogRecordExporter()))
|
||||
# Sets the global default logger provider
|
||||
set_logger_provider(logger_provider)
|
||||
# Create a logging handler to write logging records, in OTLP format, to the exporter.
|
||||
@@ -115,11 +116,15 @@ async def run_chat_client() -> None:
|
||||
2 spans with gen_ai.operation.name=execute_tool
|
||||
|
||||
"""
|
||||
client = FoundryChatClient()
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
message = "What's the weather in Amsterdam and in Paris?"
|
||||
print(f"User: {message}")
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response([Message(role="user", text=message)], tools=get_weather, stream=True):
|
||||
async for chunk in client.get_response(
|
||||
[Message(role="user", text=message)],
|
||||
stream=True,
|
||||
options={"tools": [get_weather]},
|
||||
):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import TYPE_CHECKING, Annotated
|
||||
from agent_framework import Message, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.observability import get_tracer
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from opentelemetry.trace import SpanKind
|
||||
from opentelemetry.trace.span import format_trace_id
|
||||
@@ -90,12 +91,19 @@ async def run_chat_client(client: "SupportsChatGetResponse", stream: bool = Fals
|
||||
print(f"User: {message}")
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response([Message(role="user", text=message)], tools=get_weather, stream=True):
|
||||
async for chunk in client.get_response(
|
||||
[Message(role="user", text=message)],
|
||||
stream=True,
|
||||
options={"tools": [get_weather]},
|
||||
):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response([Message(role="user", text=message)], tools=get_weather)
|
||||
response = await client.get_response(
|
||||
[Message(role="user", text=message)],
|
||||
options={"tools": [get_weather]},
|
||||
)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
@@ -103,7 +111,7 @@ async def main() -> None:
|
||||
with get_tracer().start_as_current_span("Zero Code", kind=SpanKind.CLIENT) as current_span:
|
||||
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
|
||||
|
||||
client = FoundryChatClient()
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
await run_chat_client(client, stream=True)
|
||||
await run_chat_client(client, stream=False)
|
||||
|
||||
@@ -7,6 +7,7 @@ from typing import Annotated
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.observability import configure_otel_providers, get_tracer
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from opentelemetry.trace import SpanKind
|
||||
from opentelemetry.trace.span import format_trace_id
|
||||
@@ -18,6 +19,12 @@ load_dotenv()
|
||||
"""
|
||||
This sample shows how you can observe an agent in Agent Framework by using the
|
||||
same observability setup function.
|
||||
|
||||
Pre-requisites:
|
||||
- A Foundry project
|
||||
- An observability backend to receive traces and metrics (for example, a local or remote
|
||||
OpenTelemetry Collector, another OTLP-compatible backend, or console exporters enabled
|
||||
via environment variables).
|
||||
"""
|
||||
|
||||
|
||||
@@ -47,7 +54,7 @@ async def main():
|
||||
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
|
||||
|
||||
agent = Agent(
|
||||
client=FoundryChatClient(),
|
||||
client=FoundryChatClient(credential=AzureCliCredential()),
|
||||
tools=get_weather,
|
||||
name="WeatherAgent",
|
||||
instructions="You are a weather assistant.",
|
||||
|
||||
@@ -9,6 +9,7 @@ from typing import TYPE_CHECKING, Annotated, Literal
|
||||
from agent_framework import Message, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.observability import configure_otel_providers, get_tracer
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.trace.span import format_trace_id
|
||||
@@ -24,8 +25,9 @@ This sample shows how you can configure observability of an application via the
|
||||
When you run this sample with an OTLP endpoint or an Application Insights connection string,
|
||||
you should see traces, logs, and metrics in the configured backend.
|
||||
|
||||
If no OTLP endpoint or Application Insights connection string is configured, the sample will
|
||||
output traces, logs, and metrics to the console.
|
||||
Pre-requisites:
|
||||
- A Foundry project
|
||||
- A local OpenTelemetry Collector instance to receive the traces and metrics.
|
||||
"""
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -78,13 +80,18 @@ async def run_chat_client(client: "SupportsChatGetResponse", stream: bool = Fals
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response(
|
||||
[Message(role="user", text=message)], tools=get_weather, stream=True
|
||||
[Message(role="user", text=message)],
|
||||
stream=True,
|
||||
options={"tools": [get_weather]},
|
||||
):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response([Message(role="user", text=message)], tools=get_weather)
|
||||
response = await client.get_response(
|
||||
[Message(role="user", text=message)],
|
||||
options={"tools": [get_weather]},
|
||||
)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
@@ -101,7 +108,7 @@ async def run_tool() -> None:
|
||||
with get_tracer().start_as_current_span("Scenario: AI Function", kind=trace.SpanKind.CLIENT):
|
||||
print("Running scenario: AI Function")
|
||||
weather = await get_weather.invoke(location="Amsterdam")
|
||||
print(f"Weather in Amsterdam:\n{weather}")
|
||||
print(f"Weather in Amsterdam:\n{weather[-1]}")
|
||||
|
||||
|
||||
async def main(scenario: Literal["client", "client_stream", "tool", "all"] = "all"):
|
||||
@@ -114,7 +121,7 @@ async def main(scenario: Literal["client", "client_stream", "tool", "all"] = "al
|
||||
with get_tracer().start_as_current_span("Sample Scenarios", kind=trace.SpanKind.CLIENT) as current_span:
|
||||
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
|
||||
|
||||
client = FoundryChatClient()
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Scenarios where telemetry is collected in the SDK, from the most basic to the most complex.
|
||||
if scenario == "tool" or scenario == "all":
|
||||
|
||||
@@ -10,6 +10,7 @@ from typing import TYPE_CHECKING, Annotated, Literal
|
||||
from agent_framework import Message, tool
|
||||
from agent_framework.foundry import FoundryChatClient
|
||||
from agent_framework.observability import configure_otel_providers, get_tracer
|
||||
from azure.identity import AzureCliCredential
|
||||
from dotenv import load_dotenv
|
||||
from opentelemetry import trace
|
||||
from opentelemetry.trace.span import format_trace_id
|
||||
@@ -27,6 +28,10 @@ and allows you to add multiple exporters programmatically.
|
||||
|
||||
For standard OTLP setup, it's recommended to use environment variables (see configure_otel_providers_with_env_var.py).
|
||||
Use this approach when you need custom exporter configuration beyond what environment variables provide.
|
||||
|
||||
Pre-requisites:
|
||||
- A Foundry project
|
||||
- A local OpenTelemetry Collector instance to receive the traces and metrics.
|
||||
"""
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -79,13 +84,18 @@ async def run_chat_client(client: "SupportsChatGetResponse", stream: bool = Fals
|
||||
if stream:
|
||||
print("Assistant: ", end="")
|
||||
async for chunk in client.get_response(
|
||||
[Message(role="user", text=message)], stream=True, tools=get_weather
|
||||
[Message(role="user", text=message)],
|
||||
stream=True,
|
||||
options={"tools": [get_weather]},
|
||||
):
|
||||
if chunk.text:
|
||||
print(chunk.text, end="")
|
||||
print("")
|
||||
else:
|
||||
response = await client.get_response([Message(role="user", text=message)], tools=get_weather)
|
||||
response = await client.get_response(
|
||||
[Message(role="user", text=message)],
|
||||
options={"tools": [get_weather]},
|
||||
)
|
||||
print(f"Assistant: {response}")
|
||||
|
||||
|
||||
@@ -102,7 +112,7 @@ async def run_tool() -> None:
|
||||
with get_tracer().start_as_current_span("Scenario: AI Function", kind=trace.SpanKind.CLIENT):
|
||||
print("Running scenario: AI Function")
|
||||
weather = await get_weather.invoke(location="Amsterdam")
|
||||
print(f"Weather in Amsterdam:\n{weather}")
|
||||
print(f"Weather in Amsterdam:\n{weather[-1]}")
|
||||
|
||||
|
||||
async def main(scenario: Literal["client", "client_stream", "tool", "all"] = "all"):
|
||||
@@ -153,7 +163,7 @@ async def main(scenario: Literal["client", "client_stream", "tool", "all"] = "al
|
||||
with get_tracer().start_as_current_span("Sample Scenarios", kind=trace.SpanKind.CLIENT) as current_span:
|
||||
print(f"Trace ID: {format_trace_id(current_span.get_span_context().trace_id)}")
|
||||
|
||||
client = FoundryChatClient()
|
||||
client = FoundryChatClient(credential=AzureCliCredential())
|
||||
|
||||
# Scenarios where telemetry is collected in the SDK, from the most basic to the most complex.
|
||||
if scenario == "tool" or scenario == "all":
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
# Bedrock Examples
|
||||
|
||||
This folder contains examples demonstrating how to use AWS Bedrock models with the Agent Framework. The sample
|
||||
uses `BEDROCK_CHAT_MODEL_ID`, `BEDROCK_REGION`, and AWS credentials (`AWS_ACCESS_KEY_ID`,
|
||||
uses `BEDROCK_CHAT_MODEL`, `BEDROCK_REGION`, and AWS credentials (`AWS_ACCESS_KEY_ID`,
|
||||
`AWS_SECRET_ACCESS_KEY`, optional `AWS_SESSION_TOKEN`).
|
||||
|
||||
## Examples
|
||||
@@ -12,6 +12,6 @@ uses `BEDROCK_CHAT_MODEL_ID`, `BEDROCK_REGION`, and AWS credentials (`AWS_ACCESS
|
||||
|
||||
## Environment Variables
|
||||
|
||||
- `BEDROCK_CHAT_MODEL_ID`: Bedrock model ID (for example, `anthropic.claude-3-5-sonnet-20240620-v1:0`)
|
||||
- `BEDROCK_CHAT_MODEL`: Bedrock model ID (for example, `anthropic.claude-3-5-sonnet-20240620-v1:0`)
|
||||
- `BEDROCK_REGION`: AWS region (defaults to `us-east-1` if unset)
|
||||
- AWS credentials via standard variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`, optional `AWS_SESSION_TOKEN`)
|
||||
|
||||
@@ -17,7 +17,7 @@ Bedrock Chat Client Example
|
||||
This sample demonstrates using `BedrockChatClient` with an agent and a simple tool.
|
||||
|
||||
Environment variables used:
|
||||
- `BEDROCK_CHAT_MODEL_ID`
|
||||
- `BEDROCK_CHAT_MODEL`
|
||||
- `BEDROCK_REGION` (defaults to `us-east-1` if unset)
|
||||
- AWS credentials via standard variables (`AWS_ACCESS_KEY_ID`, `AWS_SECRET_ACCESS_KEY`,
|
||||
optional `AWS_SESSION_TOKEN`)
|
||||
|
||||
@@ -28,14 +28,14 @@ This folder contains examples demonstrating how to use Anthropic's Claude models
|
||||
### Anthropic Client
|
||||
|
||||
- `ANTHROPIC_API_KEY`: Your Anthropic API key (get one from [Anthropic Console](https://console.anthropic.com/))
|
||||
- `ANTHROPIC_CHAT_MODEL_ID`: The Claude model to use (e.g., `claude-haiku-4-5`, `claude-sonnet-4-5-20250929`)
|
||||
- `ANTHROPIC_CHAT_MODEL`: The Claude model to use (e.g., `claude-haiku-4-5`, `claude-sonnet-4-5-20250929`)
|
||||
|
||||
### Foundry
|
||||
|
||||
- `ANTHROPIC_FOUNDRY_API_KEY`: Your Foundry Anthropic API key
|
||||
- `ANTHROPIC_FOUNDRY_RESOURCE`: Your Foundry resource name (for example `my-foundry-resource`)
|
||||
- `ANTHROPIC_FOUNDRY_BASE_URL`: Optional full Foundry Anthropic base URL alternative to `ANTHROPIC_FOUNDRY_RESOURCE`
|
||||
- `ANTHROPIC_CHAT_MODEL_ID`: The Claude model to use in Foundry (e.g., `claude-haiku-4-5`)
|
||||
- `ANTHROPIC_CHAT_MODEL`: The Claude model to use in Foundry (e.g., `claude-haiku-4-5`)
|
||||
|
||||
### Claude Agent
|
||||
|
||||
|
||||
@@ -35,7 +35,7 @@ async def non_streaming_example() -> None:
|
||||
print("=== Non-streaming Response Example ===")
|
||||
|
||||
agent = Agent(
|
||||
client=AnthropicClient(model_id="claude-sonnet-4-5-20250929"),
|
||||
client=AnthropicClient(model="claude-sonnet-4-5-20250929"),
|
||||
name="WeatherAgent",
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
@@ -52,7 +52,7 @@ async def streaming_example() -> None:
|
||||
print("=== Streaming Response Example ===")
|
||||
|
||||
agent = Agent(
|
||||
client=AnthropicClient(model_id="claude-sonnet-4-5-20250929"),
|
||||
client=AnthropicClient(model="claude-sonnet-4-5-20250929"),
|
||||
name="WeatherAgent",
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
|
||||
@@ -2,8 +2,7 @@
|
||||
import asyncio
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.anthropic import AnthropicClient
|
||||
from anthropic import AsyncAnthropicFoundry
|
||||
from agent_framework.foundry import AnthropicFoundryClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
@@ -27,14 +26,14 @@ To use the Foundry integration ensure you have the following environment variabl
|
||||
- ANTHROPIC_FOUNDRY_BASE_URL
|
||||
Optional alternative to ANTHROPIC_FOUNDRY_RESOURCE. Should be something like
|
||||
https://<your-resource-name>.services.ai.azure.com/anthropic/
|
||||
- ANTHROPIC_CHAT_MODEL_ID
|
||||
- ANTHROPIC_CHAT_MODEL
|
||||
Should be something like claude-haiku-4-5
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
client = AnthropicClient(anthropic_client=AsyncAnthropicFoundry())
|
||||
client = AnthropicFoundryClient()
|
||||
|
||||
# Create MCP tool configuration using instance method
|
||||
mcp_tool = client.get_mcp_tool(
|
||||
|
||||
@@ -29,7 +29,7 @@ async def main() -> None:
|
||||
client = AnthropicClient[AnthropicChatOptions](additional_beta_flags=["skills-2025-10-02"])
|
||||
|
||||
# List Anthropic-managed Skills
|
||||
skills = await client.anthropic_client.beta.skills.list(source="anthropic", betas=["skills-2025-10-02"])
|
||||
skills = await client.anthropic_client.beta.skills.list(source="anthropic", betas=["skills-2025-10-02"]) # type: ignore
|
||||
for skill in skills.data:
|
||||
print(f"{skill.source}: {skill.id} (version: {skill.latest_version})")
|
||||
|
||||
@@ -81,7 +81,7 @@ async def main() -> None:
|
||||
# Since I'm using the pptx skill, the files will be PowerPoint presentations
|
||||
print("Generated files:")
|
||||
for idx, file in enumerate(files):
|
||||
file_content = await client.anthropic_client.beta.files.download(
|
||||
file_content = await client.anthropic_client.beta.files.download( # type: ignore
|
||||
file_id=file.file_id, betas=["files-api-2025-04-14"]
|
||||
)
|
||||
with open(Path(__file__).parent / f"python_programming-{idx}.pptx", "wb") as f:
|
||||
|
||||
@@ -26,7 +26,7 @@ This folder contains Azure-backed samples for the generic OpenAI clients in
|
||||
Set these before running the Azure provider samples:
|
||||
|
||||
- `AZURE_OPENAI_ENDPOINT`
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`
|
||||
- `AZURE_OPENAI_MODEL`
|
||||
|
||||
Optionally, you can also set:
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ async def non_streaming_example() -> None:
|
||||
|
||||
agent = Agent(
|
||||
client=OpenAIChatCompletionClient(
|
||||
model=os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"),
|
||||
model=os.getenv("AZURE_OPENAI_MODEL"),
|
||||
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
|
||||
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
@@ -60,7 +60,7 @@ async def streaming_example() -> None:
|
||||
|
||||
agent = Agent(
|
||||
client=OpenAIChatCompletionClient(
|
||||
model=os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"),
|
||||
model=os.getenv("AZURE_OPENAI_MODEL"),
|
||||
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
|
||||
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ async def main() -> None:
|
||||
# authentication option.
|
||||
agent = Agent(
|
||||
client=OpenAIChatCompletionClient(
|
||||
model=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
|
||||
model=os.environ["AZURE_OPENAI_MODEL"],
|
||||
azure_endpoint=os.environ["AZURE_OPENAI_ENDPOINT"],
|
||||
credential=AzureCliCredential(),
|
||||
),
|
||||
|
||||
@@ -38,7 +38,7 @@ async def non_streaming_example() -> None:
|
||||
|
||||
agent = Agent(
|
||||
client=OpenAIChatClient(
|
||||
model=os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"),
|
||||
model=os.getenv("AZURE_OPENAI_MODEL"),
|
||||
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
|
||||
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
@@ -60,7 +60,7 @@ async def streaming_example() -> None:
|
||||
|
||||
agent = Agent(
|
||||
client=OpenAIChatClient(
|
||||
model=os.getenv("AZURE_OPENAI_DEPLOYMENT_NAME"),
|
||||
model=os.getenv("AZURE_OPENAI_MODEL"),
|
||||
azure_endpoint=os.getenv("AZURE_OPENAI_ENDPOINT"),
|
||||
api_version=os.getenv("AZURE_OPENAI_API_VERSION"),
|
||||
credential=AzureCliCredential(),
|
||||
|
||||
@@ -45,4 +45,4 @@ This folder contains Azure AI Foundry and Foundry Local samples for Agent Framew
|
||||
|
||||
### Environment Variables
|
||||
|
||||
- `FOUNDRY_LOCAL_MODEL_ID`: Optional model alias/ID to use by default when `model_id` is not passed to `FoundryLocalClient`.
|
||||
- `FOUNDRY_LOCAL_MODEL`: Optional model alias/ID to use by default when `model` is not passed to `FoundryLocalClient`.
|
||||
|
||||
@@ -51,7 +51,7 @@ async def handle_approvals_with_session(query: str, agent: "SupportsAgentRun", s
|
||||
"""Here we let the session deal with the previous responses, and we just rerun with the approval."""
|
||||
from agent_framework import Message
|
||||
|
||||
result = await agent.run(query, session=session, store=True)
|
||||
result = await agent.run(query, session=session, options={"store": True})
|
||||
while len(result.user_input_requests) > 0:
|
||||
new_input: list[Any] = []
|
||||
for user_input_needed in result.user_input_requests:
|
||||
@@ -66,7 +66,7 @@ async def handle_approvals_with_session(query: str, agent: "SupportsAgentRun", s
|
||||
contents=[user_input_needed.to_function_approval_response(user_approval.lower() == "y")],
|
||||
)
|
||||
)
|
||||
result = await agent.run(new_input, session=session, store=True)
|
||||
result = await agent.run(new_input, session=session, options={"store": True})
|
||||
return result
|
||||
|
||||
|
||||
|
||||
@@ -40,8 +40,8 @@ Set the following environment variables:
|
||||
- `OLLAMA_HOST`: The base URL for your Ollama server (optional, defaults to `http://localhost:11434`)
|
||||
- Example: `export OLLAMA_HOST="http://localhost:11434"`
|
||||
|
||||
- `OLLAMA_MODEL_ID`: The model name to use
|
||||
- Example: `export OLLAMA_MODEL_ID="qwen2.5:8b"`
|
||||
- `OLLAMA_MODEL`: The model name to use
|
||||
- Example: `export OLLAMA_MODEL="qwen2.5:8b"`
|
||||
- Must be a model you have pulled with Ollama
|
||||
|
||||
### For OpenAI Client with Ollama (`ollama_with_openai_chat_client.py`)
|
||||
|
||||
@@ -17,7 +17,7 @@ This sample demonstrates implementing a Ollama agent with basic tool usage.
|
||||
|
||||
Ensure to install Ollama and have a model running locally before running the sample
|
||||
Not all Models support function calling, to test function calling try llama3.2 or qwen3:4b
|
||||
Set the model to use via the OLLAMA_MODEL_ID environment variable or modify the code below.
|
||||
Set the model to use via the OLLAMA_MODEL environment variable or modify the code below.
|
||||
https://ollama.com/
|
||||
|
||||
"""
|
||||
|
||||
@@ -16,7 +16,7 @@ This sample demonstrates implementing a Ollama agent with reasoning.
|
||||
|
||||
Ensure to install Ollama and have a model running locally before running the sample
|
||||
Not all Models support reasoning, to test reasoning try qwen3:8b
|
||||
Set the model to use via the OLLAMA_MODEL_ID environment variable or modify the code below.
|
||||
Set the model to use via the OLLAMA_MODEL environment variable or modify the code below.
|
||||
https://ollama.com/
|
||||
|
||||
"""
|
||||
|
||||
@@ -17,7 +17,7 @@ This sample demonstrates using the native Ollama Chat Client directly.
|
||||
|
||||
Ensure to install Ollama and have a model running locally before running the sample.
|
||||
Not all Models support function calling, to test function calling try llama3.2
|
||||
Set the model to use via the OLLAMA_MODEL_ID environment variable or modify the code below.
|
||||
Set the model to use via the OLLAMA_MODEL environment variable or modify the code below.
|
||||
https://ollama.com/
|
||||
|
||||
"""
|
||||
|
||||
@@ -16,7 +16,7 @@ This sample demonstrates implementing a Ollama agent with multimodal input capab
|
||||
|
||||
Ensure to install Ollama and have a model running locally before running the sample
|
||||
Not all Models support multimodal input, to test multimodal input try gemma3:4b
|
||||
Set the model to use via the OLLAMA_MODEL_ID environment variable or modify the code below.
|
||||
Set the model to use via the OLLAMA_MODEL environment variable or modify the code below.
|
||||
https://ollama.com/
|
||||
|
||||
"""
|
||||
|
||||
@@ -28,7 +28,7 @@ code_defined_skill/
|
||||
Set the required environment variables in a `.env` file (see `python/.env.example`):
|
||||
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`: The name of your model deployment (defaults to `gpt-4o-mini`)
|
||||
- `AZURE_OPENAI_MODEL`: The name of your model deployment (defaults to `gpt-4o-mini`)
|
||||
|
||||
### Authentication
|
||||
|
||||
|
||||
@@ -48,7 +48,7 @@ file_based_skill/
|
||||
Set the required environment variables in a `.env` file (see `python/.env.example`):
|
||||
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`: The name of your model deployment (defaults to `gpt-4o-mini`)
|
||||
- `AZURE_OPENAI_MODEL`: The name of your model deployment (defaults to `gpt-4o-mini`)
|
||||
|
||||
### Authentication
|
||||
|
||||
|
||||
@@ -61,7 +61,7 @@ Set environment variables (or create a `.env` file):
|
||||
|
||||
```
|
||||
FOUNDRY_PROJECT_ENDPOINT=https://your-project.openai.azure.com/
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME=gpt-4o-mini
|
||||
AZURE_OPENAI_MODEL=gpt-4o-mini
|
||||
```
|
||||
|
||||
Authenticate with Azure CLI:
|
||||
|
||||
@@ -29,7 +29,7 @@ When `require_script_approval=True` is set, the agent pauses before executing an
|
||||
Set the required environment variables in a `.env` file (see `python/.env.example`):
|
||||
|
||||
- `FOUNDRY_PROJECT_ENDPOINT`: Your Azure AI Foundry project endpoint
|
||||
- `AZURE_OPENAI_DEPLOYMENT_NAME`: The name of your model deployment (defaults to `gpt-4o-mini`)
|
||||
- `AZURE_OPENAI_MODEL`: The name of your model deployment (defaults to `gpt-4o-mini`)
|
||||
|
||||
### Authentication
|
||||
|
||||
|
||||
@@ -38,7 +38,7 @@ async def demo_anthropic_chat_client() -> None:
|
||||
print("\n=== Anthropic ChatClient with TypedDict Options ===\n")
|
||||
|
||||
# Create Anthropic client
|
||||
client = AnthropicClient(model_id="claude-sonnet-4-5-20250929")
|
||||
client = AnthropicClient(model="claude-sonnet-4-5-20250929")
|
||||
|
||||
# Standard options work great:
|
||||
response = await client.get_response(
|
||||
@@ -53,14 +53,14 @@ async def demo_anthropic_chat_client() -> None:
|
||||
)
|
||||
|
||||
print(f"Anthropic Response: {response.text}")
|
||||
print(f"Model used: {response.model_id}")
|
||||
print(f"Model used: {response.model}")
|
||||
|
||||
|
||||
async def demo_anthropic_agent() -> None:
|
||||
"""Demonstrate Agent with Anthropic client and typed options."""
|
||||
print("\n=== Agent with Anthropic and Typed Options ===\n")
|
||||
|
||||
client = AnthropicClient(model_id="claude-sonnet-4-5-20250929")
|
||||
client = AnthropicClient(model="claude-sonnet-4-5-20250929")
|
||||
|
||||
# Create a typed agent for Anthropic - IDE knows Anthropic-specific options!
|
||||
agent = Agent(
|
||||
@@ -129,7 +129,7 @@ async def demo_openai_chat_client_reasoning_models() -> None:
|
||||
)
|
||||
|
||||
print(f"OpenAI Response: {response.text}")
|
||||
print(f"Model used: {response.model_id}")
|
||||
print(f"Model used: {response.model}")
|
||||
|
||||
|
||||
async def demo_openai_agent() -> None:
|
||||
|
||||
@@ -48,9 +48,7 @@ async def main() -> None:
|
||||
project_endpoint = os.getenv("FOUNDRY_PROJECT_ENDPOINT")
|
||||
model = os.getenv("FOUNDRY_MODEL")
|
||||
if not project_endpoint or not model:
|
||||
raise ValueError(
|
||||
"FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL must be set"
|
||||
)
|
||||
raise ValueError("FOUNDRY_PROJECT_ENDPOINT and FOUNDRY_MODEL must be set")
|
||||
|
||||
print(f"Connecting to A2A agent at: {a2a_agent_host}")
|
||||
|
||||
|
||||
@@ -67,12 +67,12 @@ def main() -> None:
|
||||
|
||||
# Validate environment
|
||||
project_endpoint = os.getenv("FOUNDRY_PROJECT_ENDPOINT")
|
||||
deployment_name = os.getenv("FOUNDRY_MODEL")
|
||||
model = os.getenv("FOUNDRY_MODEL")
|
||||
|
||||
if not project_endpoint:
|
||||
print("Error: FOUNDRY_PROJECT_ENDPOINT environment variable is not set.")
|
||||
sys.exit(1)
|
||||
if not deployment_name:
|
||||
if not model:
|
||||
print("Error: FOUNDRY_MODEL environment variable is not set.")
|
||||
sys.exit(1)
|
||||
|
||||
@@ -80,7 +80,7 @@ def main() -> None:
|
||||
credential = AzureCliCredential()
|
||||
client = FoundryChatClient(
|
||||
project_endpoint=project_endpoint,
|
||||
model=deployment_name,
|
||||
model=model,
|
||||
credential=credential,
|
||||
)
|
||||
|
||||
|
||||
@@ -7,7 +7,7 @@ Components used in this sample:
|
||||
- AgentFunctionApp to register multiple agents and expose dedicated HTTP endpoints.
|
||||
- Custom tool functions to demonstrate tool invocation from different agents.
|
||||
|
||||
Prerequisites: set `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_DEPLOYMENT_NAME`, and sign in with Azure CLI before starting the Functions host."""
|
||||
Prerequisites: set `AZURE_OPENAI_ENDPOINT`, `AZURE_OPENAI_MODEL`, and sign in with Azure CLI before starting the Functions host."""
|
||||
|
||||
import logging
|
||||
from typing import Any
|
||||
|
||||
@@ -10,4 +10,4 @@ TASKHUB_NAME=default
|
||||
|
||||
# Azure OpenAI Configuration
|
||||
AZURE_OPENAI_ENDPOINT=https://your-resource.openai.azure.com/
|
||||
AZURE_OPENAI_DEPLOYMENT_NAME=your-deployment-name
|
||||
AZURE_OPENAI_MODEL=your-deployment-name
|
||||
|
||||
@@ -99,7 +99,7 @@ The sample can run locally without Azure Functions infrastructure using DevUI:
|
||||
```
|
||||
|
||||
2. Configure `.env` with your Azure OpenAI credentials (`AZURE_OPENAI_ENDPOINT` and
|
||||
`AZURE_OPENAI_DEPLOYMENT_NAME`)
|
||||
`AZURE_OPENAI_MODEL`)
|
||||
|
||||
3. Install dependencies:
|
||||
```bash
|
||||
|
||||
@@ -21,7 +21,7 @@ Key architectural points:
|
||||
- Mixed agent/executor fan-outs execute concurrently
|
||||
|
||||
Prerequisites:
|
||||
- Configure `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_DEPLOYMENT_NAME`
|
||||
- Configure `AZURE_OPENAI_ENDPOINT` and `AZURE_OPENAI_MODEL`
|
||||
- Sign in with Azure CLI (`az login`) for `AzureCliCredential`
|
||||
- Ensure Azurite and the Durable Task Scheduler emulator are running
|
||||
"""
|
||||
@@ -362,7 +362,7 @@ def _create_workflow() -> Workflow:
|
||||
credential = AzureCliCredential()
|
||||
|
||||
chat_client = OpenAIChatCompletionClient(
|
||||
model=os.environ["AZURE_OPENAI_DEPLOYMENT_NAME"],
|
||||
model=os.environ["AZURE_OPENAI_MODEL"],
|
||||
api_key=get_bearer_token_provider(credential, "https://cognitiveservices.azure.com/.default"),
|
||||
)
|
||||
|
||||
|
||||
+1
-1
@@ -6,6 +6,6 @@
|
||||
"DURABLE_TASK_SCHEDULER_CONNECTION_STRING": "Endpoint=http://localhost:8080;TaskHub=default;Authentication=None",
|
||||
"TASKHUB_NAME": "default",
|
||||
"AZURE_OPENAI_ENDPOINT": "<AZURE_OPENAI_ENDPOINT>",
|
||||
"AZURE_OPENAI_DEPLOYMENT_NAME": "<AZURE_OPENAI_DEPLOYMENT_NAME>"
|
||||
"AZURE_OPENAI_MODEL": "<AZURE_OPENAI_MODEL>"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -8,7 +8,7 @@ each with their own specialized capabilities and tools.
|
||||
|
||||
Prerequisites:
|
||||
- The worker must be running with both agents registered
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME when running the worker
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_MODEL when running the worker
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running
|
||||
"""
|
||||
|
||||
@@ -5,7 +5,7 @@ This sample demonstrates running both the worker and client in a single process
|
||||
for multiple agents with different tools. The worker registers two agents
|
||||
(WeatherAgent and MathAgent), each with their own specialized capabilities.
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Durable Task Scheduler must be running (e.g., using Docker)
|
||||
To run this sample:
|
||||
|
||||
@@ -7,7 +7,7 @@ with their own specialized tools. This demonstrates how to host multiple agents
|
||||
with different capabilities in a single worker process.
|
||||
|
||||
Prerequisites:
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_DEPLOYMENT_NAME
|
||||
- Set AZURE_OPENAI_ENDPOINT and AZURE_OPENAI_MODEL
|
||||
- Sign in with Azure CLI for AzureCliCredential authentication
|
||||
- Start a Durable Task Scheduler (e.g., using Docker)
|
||||
"""
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user