Python: restructure: Python samples into progressive 01-05 layout (#3862)

* restructure: Python samples into progressive 01-05 layout

- 01-get-started/: 6 numbered steps (hello agent → hosting)
- 02-agents/: all agent concept samples (tools, middleware, providers, etc.)
- 03-workflows/: ALL existing workflow samples preserved as-is
- 04-hosting/: azure-functions, durabletask, a2a
- 05-end-to-end/: demos, evaluation, hosted agents
- Old files moved to _to_delete/ for review
- Added AGENTS.md with structure documentation
- autogen-migration/ and semantic-kernel-migration/ preserved at root

* fix: switch to AzureOpenAI Foundry, fix CI failures

- Switch all 01-get-started samples to AzureOpenAIResponsesClient with
  Azure AI Foundry project endpoint (AZURE_AI_PROJECT_ENDPOINT +
  AZURE_OPENAI_RESPONSES_DEPLOYMENT_NAME + AzureCliCredential)
- Add _to_delete/ and 05-end-to-end/ to pyrightconfig.samples.json excludes
- Fix test paths in packages/ that referenced old getting_started/ dirs:
  durabletask conftest + streaming test, azurefunctions conftest,
  devui conftest + capture_messages + openai_sdk_integration
- Fix workflow_as_agent_human_in_the_loop.py import (sibling import)
- Update hosting READMEs and tool comment paths
- Replace root README.md with new structure overview
- Update AGENTS.md to document Azure OpenAI Foundry as default provider

* cleanup: remove _to_delete folder, copy resource files to active dirs

All files in _to_delete/ were either:
- Exact duplicates of files in the new structure (240 files)
- Same file with only comment path updates (100 files)
- One import-fix diff (workflow_as_agent_human_in_the_loop.py)
- One superseded minimal_sample.py

Resource files (sample.pdf, countries.json, employees.pdf, weather.json)
copied to 02-agents/sample_assets/ and 02-agents/resources/ since active
samples reference them.

* fix: address PR review comments, centralize resources, remove root duplicates

- Fix type annotation in 04_memory.py (string union -> proper types)
- Fix old sample paths in observability files
- Fix grammar/spelling in observability samples
- Move sample_assets/ and resources/ to shared/ folder
- Remove 8 duplicate observability files from 02-agents root
- Update resource path references in multimodal_input and provider samples

* fix: update broken links from old getting_started paths to new structure

- Update relative paths in READMEs: getting_started/ → 01-get-started/,
  02-agents/, 03-workflows/, 04-hosting/, 05-end-to-end/
- Fix absolute GitHub URLs in package READMEs
- Fix broken link in ollama package README

* fix: convert absolute GitHub URLs to relative paths for link checker

Absolute URLs to python/samples/ on main branch 404 until PR merges.
Converted to relative paths that linkspector can verify locally.

* fix: update link for handoff sample moved to orchestrations/

* fix: update chatkit-integration README path from demos/ to 05-end-to-end/

* fix: update broken links in orchestrations README to match flat directory structure
This commit is contained in:
Eduard van Valkenburg
2026-02-12 18:36:36 +01:00
committed by GitHub
Unverified
parent 69dcfe31ee
commit a2856d3b92
536 changed files with 3816 additions and 1632 deletions
@@ -0,0 +1,37 @@
# GitHub Copilot Agent Examples
This directory contains examples demonstrating how to use the `GitHubCopilotAgent` from the Microsoft Agent Framework.
> **Security Note**: These examples demonstrate various permission types (shell, read, write, url). Only enable permissions that are necessary for your use case. Each permission grants the agent additional capabilities that could affect your system.
## Prerequisites
1. **GitHub Copilot CLI**: Install and authenticate the Copilot CLI
2. **GitHub Copilot Subscription**: An active GitHub Copilot subscription
3. **Install the package**:
```bash
pip install agent-framework-github-copilot --pre
```
## Environment Variables
The following environment variables can be configured:
| Variable | Description | Default |
|----------|-------------|---------|
| `GITHUB_COPILOT_CLI_PATH` | Path to the Copilot CLI executable | `copilot` |
| `GITHUB_COPILOT_MODEL` | Model to use (e.g., "gpt-5", "claude-sonnet-4") | Server default |
| `GITHUB_COPILOT_TIMEOUT` | Request timeout in seconds | `60` |
| `GITHUB_COPILOT_LOG_LEVEL` | CLI log level | `info` |
## Examples
| File | Description |
|------|-------------|
| [`github_copilot_basic.py`](github_copilot_basic.py) | The simplest way to create an agent using `GitHubCopilotAgent`. Demonstrates both streaming and non-streaming responses with function tools. |
| [`github_copilot_with_session.py`](github_copilot_with_session.py) | Shows session management with automatic creation, persistence via thread objects, and resuming sessions by ID. |
| [`github_copilot_with_shell.py`](github_copilot_with_shell.py) | Shows how to enable shell command execution permissions. Demonstrates running system commands like listing files and getting system information. |
| [`github_copilot_with_file_operations.py`](github_copilot_with_file_operations.py) | Shows how to enable file read and write permissions. Demonstrates reading file contents and creating new files. |
| [`github_copilot_with_url.py`](github_copilot_with_url.py) | Shows how to enable URL fetching permissions. Demonstrates fetching and processing web content. |
| [`github_copilot_with_mcp.py`](github_copilot_with_mcp.py) | Shows how to configure MCP (Model Context Protocol) servers, including local (stdio) and remote (HTTP) servers. |
| [`github_copilot_with_multiple_permissions.py`](github_copilot_with_multiple_permissions.py) | Shows how to combine multiple permission types for complex tasks that require shell, read, and write access. |
@@ -0,0 +1,113 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GitHub Copilot Agent Basic Example
This sample demonstrates basic usage of GitHubCopilotAgent.
Shows both streaming and non-streaming responses with function tools.
Environment variables (optional):
- GITHUB_COPILOT_CLI_PATH - Path to the Copilot CLI executable
- GITHUB_COPILOT_MODEL - Model to use (e.g., "gpt-5", "claude-sonnet-4")
- GITHUB_COPILOT_TIMEOUT - Request timeout in seconds
- GITHUB_COPILOT_LOG_LEVEL - CLI log level
"""
import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.github import GitHubCopilotAgent
from pydantic import Field
# 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_threads.py.
@tool(approval_mode="never_require")
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."
async def non_streaming_example() -> None:
"""Example of non-streaming response (get the complete result at once)."""
print("=== Non-streaming Response Example ===")
agent = GitHubCopilotAgent(
instructions="You are a helpful weather agent.",
tools=[get_weather],
)
async with agent:
query = "What's the weather like in Seattle?"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result}\n")
async def streaming_example() -> None:
"""Example of streaming response (get results as they are generated)."""
print("=== Streaming Response Example ===")
agent = GitHubCopilotAgent(
instructions="You are a helpful weather agent.",
tools=[get_weather],
)
async with agent:
query = "What's the weather like in Tokyo?"
print(f"User: {query}")
print("Agent: ", end="", flush=True)
async for chunk in agent.run(query, stream=True):
if chunk.text:
print(chunk.text, end="", flush=True)
print("\n")
async def runtime_options_example() -> None:
"""Example of overriding system message at runtime."""
print("=== Runtime Options Example ===")
agent = GitHubCopilotAgent(
instructions="Always respond in exactly 3 words.",
tools=[get_weather],
)
async with agent:
query = "What's the weather like in Paris?"
# First call uses default instructions (3 words response)
print("Using default instructions (3 words):")
print(f"User: {query}")
result1 = await agent.run(query)
print(f"Agent: {result1}\n")
# Second call overrides with runtime system_message in replace mode
print("Using runtime system_message with replace mode (detailed response):")
print(f"User: {query}")
result2 = await agent.run(
query,
options={
"system_message": {
"mode": "replace",
"content": "You are a weather expert. Provide detailed weather information "
"with temperature, and recommendations.",
}
},
)
print(f"Agent: {result2}\n")
async def main() -> None:
print("=== Basic GitHub Copilot Agent Example ===")
await non_streaming_example()
await streaming_example()
await runtime_options_example()
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,51 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GitHub Copilot Agent with File Operation Permissions
This sample demonstrates how to enable file read and write operations with GitHubCopilotAgent.
By providing a permission handler that approves "read" and/or "write" requests, the agent can
read from and write to files on the filesystem.
SECURITY NOTE: Only enable file permissions when you trust the agent's actions.
- "read" allows the agent to read any accessible file
- "write" allows the agent to create or modify files
"""
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
if "path" in request:
print(f" Path: {request.get('path')}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
return PermissionRequestResult(kind="approved")
return PermissionRequestResult(kind="denied-interactively-by-user")
async def main() -> None:
print("=== GitHub Copilot Agent with File Operation Permissions ===\n")
agent = GitHubCopilotAgent(
instructions="You are a helpful assistant that can read and write files.",
default_options={"on_permission_request": prompt_permission},
)
async with agent:
query = "Read the contents of README.md and summarize it"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result}\n")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,75 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GitHub Copilot Agent with MCP Servers
This sample demonstrates how to configure MCP (Model Context Protocol) servers
with GitHubCopilotAgent. It shows both local (stdio) and remote (HTTP) server
configurations, giving the agent access to external tools and data sources.
SECURITY NOTE: MCP servers can expose powerful capabilities. Only configure
servers you trust. The permission handler below prompts the user for approval
of MCP-related actions.
"""
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import MCPServerConfig, PermissionRequest, PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
return PermissionRequestResult(kind="approved")
return PermissionRequestResult(kind="denied-interactively-by-user")
async def main() -> None:
print("=== GitHub Copilot Agent with MCP Servers ===\n")
# Configure both local and remote MCP servers
mcp_servers: dict[str, MCPServerConfig] = {
# Local stdio server: provides filesystem access tools
"filesystem": {
"type": "stdio",
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
"tools": ["*"],
},
# Remote HTTP server: Microsoft Learn documentation
"microsoft-learn": {
"type": "http",
"url": "https://learn.microsoft.com/api/mcp",
"tools": ["*"],
},
}
agent = GitHubCopilotAgent(
instructions="You are a helpful assistant with access to the local filesystem and Microsoft Learn.",
default_options={
"on_permission_request": prompt_permission,
"mcp_servers": mcp_servers,
},
)
async with agent:
# Query that exercises the local filesystem MCP server
query1 = "List the files in the current directory"
print(f"User: {query1}")
result1 = await agent.run(query1)
print(f"Agent: {result1}\n")
# Query that exercises the remote Microsoft Learn MCP server
query2 = "Search Microsoft Learn for 'Azure Functions Python' and summarize the top result"
print(f"User: {query2}")
result2 = await agent.run(query2)
print(f"Agent: {result2}\n")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,59 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GitHub Copilot Agent with Multiple Permissions
This sample demonstrates how to enable multiple permission types with GitHubCopilotAgent.
By combining different permission kinds in the handler, the agent can perform complex tasks
that require multiple capabilities.
Available permission kinds:
- "shell": Execute shell commands
- "read": Read files from the filesystem
- "write": Write files to the filesystem
- "mcp": Use MCP (Model Context Protocol) servers
- "url": Fetch content from URLs
SECURITY NOTE: Only enable permissions that are necessary for your use case.
More permissions mean more potential for unintended actions.
"""
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
if "command" in request:
print(f" Command: {request.get('command')}")
if "path" in request:
print(f" Path: {request.get('path')}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
return PermissionRequestResult(kind="approved")
return PermissionRequestResult(kind="denied-interactively-by-user")
async def main() -> None:
print("=== GitHub Copilot Agent with Multiple Permissions ===\n")
agent = GitHubCopilotAgent(
instructions="You are a helpful development assistant that can read, write files and run commands.",
default_options={"on_permission_request": prompt_permission},
)
async with agent:
query = "List the first 3 Python files, then read the first one and create a summary in summary.txt"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result}\n")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,140 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GitHub Copilot Agent with Session Management
This sample demonstrates session management with GitHubCopilotAgent, showing
persistent conversation capabilities. Sessions are automatically persisted
server-side by the Copilot CLI.
"""
import asyncio
from random import randint
from typing import Annotated
from agent_framework import tool
from agent_framework.github import GitHubCopilotAgent
from pydantic import Field
# 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_threads.py.
@tool(approval_mode="never_require")
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."
async def example_with_automatic_session_creation() -> None:
"""Each run() without thread creates a new session."""
print("=== Automatic Session Creation Example ===")
agent = GitHubCopilotAgent(
instructions="You are a helpful weather agent.",
tools=[get_weather],
)
async with agent:
# First query - creates a new session
query1 = "What's the weather like in Seattle?"
print(f"User: {query1}")
result1 = await agent.run(query1)
print(f"Agent: {result1}")
# Second query - without thread, creates another new session
query2 = "What was the last city I asked about?"
print(f"\nUser: {query2}")
result2 = await agent.run(query2)
print(f"Agent: {result2}")
print("Note: Each call creates a separate session, so the agent doesn't remember previous context.\n")
async def example_with_session_persistence() -> None:
"""Reuse session via thread object for multi-turn conversations."""
print("=== Session Persistence Example ===")
agent = GitHubCopilotAgent(
instructions="You are a helpful weather agent.",
tools=[get_weather],
)
async with agent:
# Create a thread to maintain conversation context
thread = agent.get_new_thread()
# First query
query1 = "What's the weather like in Tokyo?"
print(f"User: {query1}")
result1 = await agent.run(query1, thread=thread)
print(f"Agent: {result1}")
# Second query - using same thread maintains context
query2 = "How about London?"
print(f"\nUser: {query2}")
result2 = await agent.run(query2, thread=thread)
print(f"Agent: {result2}")
# Third query - agent should remember both previous cities
query3 = "Which of the cities I asked about has better weather?"
print(f"\nUser: {query3}")
result3 = await agent.run(query3, thread=thread)
print(f"Agent: {result3}")
print("Note: The agent remembers context from previous messages in the same session.\n")
async def example_with_existing_session_id() -> None:
"""Resume session in new agent instance using service_thread_id."""
print("=== Existing Session ID Example ===")
existing_session_id = None
# First agent instance - start a conversation
agent1 = GitHubCopilotAgent(
instructions="You are a helpful weather agent.",
tools=[get_weather],
)
async with agent1:
thread = agent1.get_new_thread()
query1 = "What's the weather in Paris?"
print(f"User: {query1}")
result1 = await agent1.run(query1, thread=thread)
print(f"Agent: {result1}")
# Capture the session ID for later use
existing_session_id = thread.service_thread_id
print(f"Session ID: {existing_session_id}")
if existing_session_id:
print("\n--- Continuing with the same session ID in a new agent instance ---")
# Second agent instance - resume the conversation
agent2 = GitHubCopilotAgent(
instructions="You are a helpful weather agent.",
tools=[get_weather],
)
async with agent2:
# Create thread with existing session ID
thread = agent2.get_new_thread(service_thread_id=existing_session_id)
query2 = "What was the last city I asked about?"
print(f"User: {query2}")
result2 = await agent2.run(query2, thread=thread)
print(f"Agent: {result2}")
print("Note: The agent continues the conversation using the session ID.\n")
async def main() -> None:
print("=== GitHub Copilot Agent Session Management Examples ===\n")
await example_with_automatic_session_creation()
await example_with_session_persistence()
await example_with_existing_session_id()
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,50 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GitHub Copilot Agent with Shell Permissions
This sample demonstrates how to enable shell command execution with GitHubCopilotAgent.
By providing a permission handler that approves "shell" requests, the agent can execute
shell commands to perform tasks like listing files, running scripts, or executing system commands.
SECURITY NOTE: Only enable shell permissions when you trust the agent's actions.
Shell commands have full access to your system within the permissions of the running process.
"""
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
if "command" in request:
print(f" Command: {request.get('command')}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
return PermissionRequestResult(kind="approved")
return PermissionRequestResult(kind="denied-interactively-by-user")
async def main() -> None:
print("=== GitHub Copilot Agent with Shell Permissions ===\n")
agent = GitHubCopilotAgent(
instructions="You are a helpful assistant that can execute shell commands.",
default_options={"on_permission_request": prompt_permission},
)
async with agent:
query = "List the first 3 Python files in the current directory"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result}\n")
if __name__ == "__main__":
asyncio.run(main())
@@ -0,0 +1,50 @@
# Copyright (c) Microsoft. All rights reserved.
"""
GitHub Copilot Agent with URL Fetching
This sample demonstrates how to enable URL fetching with GitHubCopilotAgent.
By providing a permission handler that approves "url" requests, the agent can
fetch and process content from web URLs.
SECURITY NOTE: Only enable URL permissions when you trust the agent's actions.
URL fetching allows the agent to access any URL accessible from your network.
"""
import asyncio
from agent_framework.github import GitHubCopilotAgent
from copilot.types import PermissionRequest, PermissionRequestResult
def prompt_permission(request: PermissionRequest, context: dict[str, str]) -> PermissionRequestResult:
"""Permission handler that prompts the user for approval."""
kind = request.get("kind", "unknown")
print(f"\n[Permission Request: {kind}]")
if "url" in request:
print(f" URL: {request.get('url')}")
response = input("Approve? (y/n): ").strip().lower()
if response in ("y", "yes"):
return PermissionRequestResult(kind="approved")
return PermissionRequestResult(kind="denied-interactively-by-user")
async def main() -> None:
print("=== GitHub Copilot Agent with URL Fetching ===\n")
agent = GitHubCopilotAgent(
instructions="You are a helpful assistant that can fetch and summarize web content.",
default_options={"on_permission_request": prompt_permission},
)
async with agent:
query = "Fetch https://learn.microsoft.com/agent-framework/tutorials/quick-start and summarize its contents"
print(f"User: {query}")
result = await agent.run(query)
print(f"Agent: {result}\n")
if __name__ == "__main__":
asyncio.run(main())