Python: small fixes in foundry (#297)

* small fixes in foundry

* other samples updated

* make it optional

* added instructions and response format to create agent

* mypy fix

* shortened main readme and improved python readme
This commit is contained in:
Eduard van Valkenburg
2025-08-04 10:13:44 +02:00
committed by GitHub
Unverified
parent 30fc2b6e9b
commit c39845d473
9 changed files with 448 additions and 274 deletions
+162
View File
@@ -6,6 +6,8 @@ want to run the tests included.
## System setup
We are using a tool called [poethepoet](https://github.com/nat-n/poethepoet) for task management and [uv](https://github.com/astral-sh/uv) for dependency management. At the [end of this document](#available-poe-tasks), you will find the available Poe tasks.
## If you're on WSL
Check that you've cloned the repository to `~/workspace` or a similar folder.
@@ -461,3 +463,163 @@ or:
This is assuming the upstream branch refers to the main repository. If you have a different name for the upstream branch, you can replace `upstream` with the name of your upstream branch.
After running the rebase command, you may need to resolve any conflicts that arise. If you are unsure how to resolve a conflict, please refer to the [GitHub's documentation on resolving conflicts](https://docs.github.com/en/get-started/using-git/resolving-merge-conflicts-after-a-git-rebase), or for [VSCode](https://code.visualstudio.com/docs/sourcecontrol/overview#_merge-conflicts).
# Task automation
## Available Poe Tasks
This project uses [poethepoet](https://github.com/nat-n/poethepoet) for task management and [uv](https://github.com/astral-sh/uv) for dependency management.
### Setup and Installation
Once uv is installed, and you do not yet have a virtual environment setup:
```bash
uv venv
```
and then you can run the following tasks:
```bash
uv sync --all-extras --dev
```
After this initial setup, you can use the following tasks to manage your development environment, it is adviced to use the following setup command since that also installs the pre-commit hooks.
#### `setup`
Set up the development environment with a virtual environment, install dependencies and pre-commit hooks:
```bash
uv run poe setup
# or with specific Python version
uv run poe setup --python 3.12
```
#### `install`
Install all dependencies including extras and dev dependencies, including updates:
```bash
uv run poe install
```
#### `venv`
Create a virtual environment with specified Python version or switch python version:
```bash
uv run poe venv
# or with specific Python version
uv run poe venv --python 3.12
```
#### `pre-commit-install`
Install pre-commit hooks:
```bash
uv run poe pre-commit-install
```
### Code Quality and Formatting
Each of the following tasks are designed to run against both the main `agent-framework` package and the extension packages, ensuring consistent code quality across the project.
#### `fmt` (format)
Format code using ruff:
```bash
uv run poe fmt
```
#### `lint`
Run linting checks and fix issues:
```bash
uv run poe lint
```
#### `pyright`
Run Pyright type checking:
```bash
uv run poe pyright
```
#### `mypy`
Run MyPy type checking:
```bash
uv run poe mypy
```
### Testing
#### `test`
Run unit tests with coverage:
```bash
uv run poe test
```
### Documentation
#### `docs-clean`
Remove the docs build directory:
```bash
uv run poe docs-clean
```
#### `docs-build`
Build the documentation:
```bash
uv run poe docs-build
```
#### `docs-serve`
Serve documentation locally with auto-reload:
```bash
uv run poe docs-serve
```
#### `docs-check`
Build documentation and fail on warnings:
```bash
uv run poe docs-check
```
#### `docs-check-examples`
Check documentation examples for code correctness:
```bash
uv run poe docs-check-examples
```
### Code Validation
#### `markdown-code-lint`
Lint markdown code blocks:
```bash
uv run poe markdown-code-lint
```
#### `samples-code-check`
Run type checking on samples:
```bash
uv run poe samples-code-check
```
### Comprehensive Checks
#### `check`
Run all quality checks (format, lint, pyright, mypy, test, markdown lint, samples check):
```bash
uv run poe check
```
#### `pre-commit-check`
Run pre-commit specific checks (all of the above, excluding `mypy`):
```bash
uv run poe pre-commit-check
```
### Building
#### `build`
Build the package:
```bash
uv run poe build
```
## Pre-commit Hooks
You can also run all checks using pre-commit directly:
```bash
uv run pre-commit run -a
```
+224 -149
View File
@@ -1,160 +1,235 @@
# Python
# Get Started with Microsoft Agent Framework
This project uses [poethepoet](https://github.com/nat-n/poethepoet) for task management and [uv](https://github.com/astral-sh/uv) for dependency management.
Highlights
- Flexible Agent Framework: build, orchestrate, and deploy AI agents and multi-agent systems
- Multi-Agent Orchestration: Group chat, sequential, concurrent, and handoff patterns
- Plugin Ecosystem: Extend with native functions, OpenAPI, Model Context Protocol (MCP), and more
- LLM Support: OpenAI, Azure OpenAI, Azure AI Foundry, and more
- Runtime Support: In-process and distributed agent execution
- Multimodal: Text, vision, and function calling
- Cross-Platform: .NET and Python implementations
## Available Poe Tasks
### Setup and Installation
Once uv is installed, and you do not yet have a virtual environment setup:
## Quick Install
```bash
uv venv
pip install agent-framework
# Optional: Add Azure integration
pip install agent-framework[azure]
# Optional: Add Foundry integration
pip install agent-framework[foundry]
# Optional: Both
pip install agent-framework[azure,foundry]
```
and then you can run the following tasks:
```bash
uv sync --all-extras --dev
```
Supported Platforms:
- Python: 3.10+
- OS: Windows, macOS, Linux
After this initial setup, you can use the following tasks to manage your development environment, it is adviced to use the following setup command since that also installs the pre-commit hooks.
## 1. Setup API Keys
#### `setup`
Set up the development environment with a virtual environment, install dependencies and pre-commit hooks:
```bash
uv run poe setup
# or with specific Python version
uv run poe setup --python 3.12
```
#### `install`
Install all dependencies including extras and dev dependencies, including updates:
```bash
uv run poe install
```
#### `venv`
Create a virtual environment with specified Python version or switch python version:
```bash
uv run poe venv
# or with specific Python version
uv run poe venv --python 3.12
```
#### `pre-commit-install`
Install pre-commit hooks:
```bash
uv run poe pre-commit-install
```
### Code Quality and Formatting
Each of the following tasks are designed to run against both the main `agent-framework` package and the extension packages, ensuring consistent code quality across the project.
#### `fmt` (format)
Format code using ruff:
```bash
uv run poe fmt
```
#### `lint`
Run linting checks and fix issues:
```bash
uv run poe lint
```
#### `pyright`
Run Pyright type checking:
```bash
uv run poe pyright
```
#### `mypy`
Run MyPy type checking:
```bash
uv run poe mypy
```
### Testing
#### `test`
Run unit tests with coverage:
```bash
uv run poe test
```
### Documentation
#### `docs-clean`
Remove the docs build directory:
```bash
uv run poe docs-clean
```
#### `docs-build`
Build the documentation:
```bash
uv run poe docs-build
```
#### `docs-serve`
Serve documentation locally with auto-reload:
```bash
uv run poe docs-serve
```
#### `docs-check`
Build documentation and fail on warnings:
```bash
uv run poe docs-check
```
#### `docs-check-examples`
Check documentation examples for code correctness:
```bash
uv run poe docs-check-examples
```
### Code Validation
#### `markdown-code-lint`
Lint markdown code blocks:
```bash
uv run poe markdown-code-lint
```
#### `samples-code-check`
Run type checking on samples:
```bash
uv run poe samples-code-check
```
### Comprehensive Checks
#### `check`
Run all quality checks (format, lint, pyright, mypy, test, markdown lint, samples check):
```bash
uv run poe check
```
#### `pre-commit-check`
Run pre-commit specific checks (all of the above, excluding `mypy`):
```bash
uv run poe pre-commit-check
```
### Building
#### `build`
Build the package:
```bash
uv run poe build
```
## Pre-commit Hooks
You can also run all checks using pre-commit directly:
Set as environment variables, or create a .env file at your project root:
```bash
uv run pre-commit run -a
OPENAI_API_KEY=sk-...
OPENAI_CHAT_MODEL_ID=...
...
AZURE_OPENAI_API_KEY=...
AZURE_OPENAI_ENDPOINT=...
AZURE_OPENAI_CHAT_DEPLOYMENT_NAME=...
...
FOUNDRY_PROJECT_ENDPOINT=...
FOUNDRY_MODEL_DEPLOYMENT_NAME=...
```
You can also override environment variables by explicitly passing configuration parameters to the chat client constructor:
```python
from agent_framework.azure import AzureChatClient
chat_client = AzureChatClient(
api_key='',
endpoint='',
deployment_name='',
api_version='',
)
```
See the following [setup guide](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started) for more information.
## 2. Create a Simple Agent
Create agents and invoke them directly:
```python
import asyncio
from agent_framework import ChatClientAgent
from agent_framework.openai import OpenAIChatClient
async def main():
agent = ChatClientAgent(
chat_client=OpenAIChatClient(),
instructions="""
1) A robot may not injure a human being...
2) A robot must obey orders given it by human beings...
3) A robot must protect its own existence...
Give me the TLDR in exactly 5 words.
"""
)
result = await agent.run("Summarize the Three Laws of Robotics")
print(result)
asyncio.run(main())
# Output: Protect humans, obey, self-preserve, prioritized.
```
## 3. Directly Use Chat Clients (No Agent Required)
You can use the chat client classes directly for advanced workflows:
```python
import asyncio
from agent_framework.openai import OpenAIChatClient
from agent_framework import ChatMessage, ChatRole
async def main():
client = OpenAIChatClient()
messages = [
ChatMessage(role=ChatRole.SYSTEM, text="You are a helpful assistant."),
ChatMessage(role=ChatRole.USER, text="Write a haiku about Agent Framework.")
]
response = await client.get_response(messages)
print(response.messages[0].text)
"""
Output:
Agents work in sync,
Framework threads through each task—
Code sparks collaboration.
"""
asyncio.run(main())
```
## 4. Build an Agent with Tools and Functions
Enhance your agent with custom tools and function calling:
```python
import asyncio
from typing import Annotated
from random import randint
from pydantic import Field
from agent_framework import ChatClientAgent
from agent_framework.openai import OpenAIChatClient
def get_weather(
location: Annotated[str, Field(description="The location to get the weather for.")],
) -> str:
"""Get the weather for a given location."""
conditions = ["sunny", "cloudy", "rainy", "stormy"]
return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C."
def get_menu_specials() -> str:
"""Get today's menu specials."""
return """
Special Soup: Clam Chowder
Special Salad: Cobb Salad
Special Drink: Chai Tea
"""
async def main():
agent = ChatClientAgent(
chat_client=OpenAIChatClient(),
instructions="You are a helpful assistant that can provide weather and restaurant information.",
tools=[get_weather, get_menu_specials]
)
response = await agent.run("What's the weather in Amsterdam and what are today's specials?")
print(response)
"""
Output:
The weather in Amsterdam is sunny with a high of 22°C. Today's specials include
Clam Chowder soup, Cobb Salad, and Chai Tea as the special drink.
"""
if __name__ == "__main__":
asyncio.run(main())
```
You can explore additional agent samples [here](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/agents).
## 5. Multi-Agent Orchestration
Coordinate multiple agents to collaborate on complex tasks using orchestration patterns:
```python
import asyncio
from agent_framework import ChatClientAgent
from agent_framework.openai import OpenAIChatClient
async def main():
# Create specialized agents
writer = ChatClientAgent(
chat_client=OpenAIChatClient(),
name="Writer",
instructions="You are a creative content writer. Generate and refine slogans based on feedback."
)
reviewer = ChatClientAgent(
chat_client=OpenAIChatClient(),
name="Reviewer",
instructions="You are a critical reviewer. Provide detailed feedback on proposed slogans."
)
# Sequential workflow: Writer creates, Reviewer provides feedback
task = "Create a slogan for a new electric SUV that is affordable and fun to drive."
# Step 1: Writer creates initial slogan
initial_result = await writer.run(task)
print(f"Writer: {initial_result}")
# Step 2: Reviewer provides feedback
feedback_request = f"Please review this slogan: {initial_result}"
feedback = await reviewer.run(feedback_request)
print(f"Reviewer: {feedback}")
# Step 3: Writer refines based on feedback
refinement_request = f"Please refine this slogan based on the feedback: {initial_result}\nFeedback: {feedback}"
final_result = await writer.run(refinement_request)
print(f"Final Slogan: {final_result}")
# Example Output:
# Writer: "Charge Forward: Affordable Adventure Awaits!"
# Reviewer: "Good energy, but 'Charge Forward' is overused in EV marketing..."
# Final Slogan: "Power Up Your Adventure: Premium Feel, Smart Price!"
if __name__ == "__main__":
asyncio.run(main())
```
**Note**: Advanced orchestration patterns like GroupChat, Sequential, and Concurrent orchestrations are coming soon.
## More Examples & Samples
- [Getting Started with Agents](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/agents): Basic agent creation and tool usage
- [Chat Client Examples](https://github.com/microsoft/agent-framework/tree/main/python/samples/getting_started/chat_client): Direct chat client usage patterns
- [Azure Integration](https://github.com/microsoft/agent-framework/tree/main/python/packages/azure): Azure OpenAI and AI Foundry integration
- [.NET Orchestration Samples](https://github.com/microsoft/agent-framework/tree/main/dotnet/samples/GettingStarted/Orchestration): Advanced multi-agent patterns (.NET)
## Agent Framework Documentation
- [Agent Framework Repository](https://github.com/microsoft/agent-framework)
- [Python Package Documentation](https://github.com/microsoft/agent-framework/tree/main/python)
- [.NET Package Documentation](https://github.com/microsoft/agent-framework/tree/main/dotnet)
- [Design Documents](https://github.com/microsoft/agent-framework/tree/main/docs/design)
- Learn docs are coming soon.
@@ -245,7 +245,7 @@ class FoundryChatClient(ChatClientBase):
raise ValueError("No thread ID was provided, but chat messages includes tool results.")
# Determine which agent to use and create if needed
agent_id = await self._get_agent_id_or_create()
agent_id = await self._get_agent_id_or_create(run_options)
# Create the streaming response
stream, thread_id = await self._create_agent_stream(thread_id, agent_id, run_options, tool_results)
@@ -254,7 +254,7 @@ class FoundryChatClient(ChatClientBase):
async for update in self._process_stream_events(stream, thread_id):
yield update
async def _get_agent_id_or_create(self) -> str:
async def _get_agent_id_or_create(self, run_options: dict[str, Any] | None = None) -> str:
"""Determine which agent to use and create if needed.
Returns:
@@ -266,9 +266,15 @@ class FoundryChatClient(ChatClientBase):
raise ServiceInitializationError("Model deployment name is required for agent creation.")
agent_name = self._foundry_settings.agent_name
created_agent = await self.client.agents.create_agent(
model=self._foundry_settings.model_deployment_name, name=agent_name
)
args = {"model": self._foundry_settings.model_deployment_name, "name": agent_name}
if run_options:
if "tools" in run_options:
args["tools"] = run_options["tools"]
if "instructions" in run_options:
args["instructions"] = run_options["instructions"]
if "response_format" in run_options:
args["response_format"] = run_options["response_format"]
created_agent = await self.client.agents.create_agent(**args) # type: ignore[arg-type]
self.agent_id = created_agent.id
self._should_delete_agent = True
@@ -2,6 +2,7 @@
import sys
from collections.abc import AsyncIterable, Callable, MutableMapping, Sequence
from contextlib import AbstractAsyncContextManager
from enum import Enum
from typing import Any, ClassVar, Literal, Protocol, TypeVar, runtime_checkable
from uuid import uuid4
@@ -417,7 +418,7 @@ class ChatClientAgent(AgentBase):
If the chat_client supports async context management, enter its context.
"""
if hasattr(self.chat_client, "__aenter__") and hasattr(self.chat_client, "__aexit__"):
if isinstance(self.chat_client, AbstractAsyncContextManager):
await self.chat_client.__aenter__() # type: ignore[reportUnknownMemberType]
return self
@@ -426,7 +427,7 @@ class ChatClientAgent(AgentBase):
If the chat_client supports async context management, exit its context.
"""
if hasattr(self.chat_client, "__aenter__") and hasattr(self.chat_client, "__aexit__"):
if isinstance(self.chat_client, AbstractAsyncContextManager):
await self.chat_client.__aexit__(exc_type, exc_val, exc_tb) # type: ignore[reportUnknownMemberType]
async def run(
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import AgentRunResponseUpdate, ChatClientAgent, HostedCodeInterpreterTool
from agent_framework import AgentRunResponseUpdate, ChatClientAgent, ChatResponseUpdate, HostedCodeInterpreterTool
from agent_framework.foundry import FoundryChatClient
from azure.ai.agents.models import (
RunStepDelta,
@@ -16,12 +16,13 @@ from azure.ai.agents.models import (
def get_code_interpreter_chunk(chunk: AgentRunResponseUpdate) -> str | None:
"""Helper method to access code interpreter data."""
if (
isinstance(chunk.raw_representation, RunStepDeltaChunk)
and isinstance(chunk.raw_representation.delta, RunStepDelta)
and isinstance(chunk.raw_representation.delta.step_details, RunStepDeltaToolCallObject)
and chunk.raw_representation.delta.step_details.tool_calls
isinstance(chunk.raw_representation, ChatResponseUpdate)
and isinstance(chunk.raw_representation.raw_representation, RunStepDeltaChunk)
and isinstance(chunk.raw_representation.raw_representation.delta, RunStepDelta)
and isinstance(chunk.raw_representation.raw_representation.delta.step_details, RunStepDeltaToolCallObject)
and chunk.raw_representation.raw_representation.delta.step_details.tool_calls
):
for tool_call in chunk.raw_representation.delta.step_details.tool_calls:
for tool_call in chunk.raw_representation.raw_representation.delta.step_details.tool_calls:
if (
isinstance(tool_call, RunStepDeltaCodeInterpreterToolCall)
and isinstance(tool_call.code_interpreter, RunStepDeltaCodeInterpreterDetailItemObject)
@@ -40,7 +41,7 @@ async def main() -> None:
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
tools=HostedCodeInterpreterTool(),
) as agent:
query = "What is current datetime?"
query = "Generate the factorial of 100 using python code."
print(f"User: {query}")
print("Agent: ", end="", flush=True)
generated_code = ""
@@ -33,6 +33,8 @@ async def main() -> None:
try:
async with ChatClientAgent(
# passing in the client is optional here, so if you take the agent_id from the portal
# you can use it directly without the two lines above.
chat_client=FoundryChatClient(client=client, agent_id=created_agent.id),
instructions="You are a helpful weather agent.",
tools=get_weather,
@@ -16,12 +16,13 @@ from openai.types.beta.threads.runs.code_interpreter_tool_call_delta import Code
def get_code_interpreter_chunk(chunk: AgentRunResponseUpdate) -> str | None:
"""Helper method to access code interpreter data."""
if (
isinstance(chunk.raw_representation, RunStepDeltaEvent)
and isinstance(chunk.raw_representation.delta, RunStepDelta)
and isinstance(chunk.raw_representation.delta.step_details, ToolCallDeltaObject)
and chunk.raw_representation.delta.step_details.tool_calls
isinstance(chunk.raw_representation, AgentRunResponseUpdate)
and isinstance(chunk.raw_representation.raw_representation, RunStepDeltaEvent)
and isinstance(chunk.raw_representation.raw_representation.delta, RunStepDelta)
and isinstance(chunk.raw_representation.raw_representation.delta.step_details, ToolCallDeltaObject)
and chunk.raw_representation.raw_representation.delta.step_details.tool_calls
):
for tool_call in chunk.raw_representation.delta.step_details.tool_calls:
for tool_call in chunk.raw_representation.raw_representation.delta.step_details.tool_calls:
if (
isinstance(tool_call, CodeInterpreterToolCallDelta)
and isinstance(tool_call.code_interpreter, CodeInterpreter)
@@ -40,7 +41,7 @@ async def main() -> None:
instructions="You are a helpful assistant that can write and execute Python code to solve problems.",
tools=HostedCodeInterpreterTool(),
) as agent:
query = "What is current datetime?"
query = "Use code to get the factorial of 100?"
print(f"User: {query}")
print("Agent: ", end="", flush=True)
generated_code = ""
@@ -2,7 +2,7 @@
import asyncio
from agent_framework import ChatClientAgent, HostedCodeInterpreterTool
from agent_framework import ChatClientAgent, ChatResponse, HostedCodeInterpreterTool
from agent_framework.openai import OpenAIResponsesClient
from openai.types.responses.response import Response as OpenAIResponse
from openai.types.responses.response_code_interpreter_tool_call import ResponseCodeInterpreterToolCall
@@ -18,17 +18,18 @@ async def main() -> None:
tools=HostedCodeInterpreterTool(),
)
query = "What is current datetime?"
query = "Use code to get the factorial of 100?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Result: {result}\n")
if (
isinstance(result.raw_representation, OpenAIResponse)
and len(result.raw_representation.output) > 0
and isinstance(result.raw_representation.output[0], ResponseCodeInterpreterToolCall)
isinstance(result.raw_representation, ChatResponse)
and isinstance(result.raw_representation.raw_representation, OpenAIResponse)
and len(result.raw_representation.raw_representation.output) > 0
and isinstance(result.raw_representation.raw_representation.output[0], ResponseCodeInterpreterToolCall)
):
generated_code = result.raw_representation.output[0].code
generated_code = result.raw_representation.raw_representation.output[0].code
print(f"Generated code:\n{generated_code}")