chore: import upstream snapshot with attribution
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
CodeQL / Analyze (csharp) (push) Waiting to run
CodeQL / Analyze (python) (push) Waiting to run
dotnet-build-and-test / dotnet-test-functions (push) Has been cancelled
dotnet-build-and-test / paths-filter (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Debug, windows-latest, net9.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, ubuntu-latest, net8.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-build (Release, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, ubuntu-latest, net10.0) (push) Has been cancelled
dotnet-build-and-test / dotnet-test (Release, integration, true, windows-latest, net472) (push) Has been cancelled
dotnet-build-and-test / dotnet-foundry-hosted-it (push) Has been cancelled
dotnet-build-and-test / dotnet-build-and-test-check (push) Has been cancelled
dotnet-build-and-test / Integration Test Report (push) Has been cancelled
This commit is contained in:
@@ -0,0 +1,47 @@
|
||||
# Anthropic Examples
|
||||
|
||||
This folder contains examples demonstrating how to use Anthropic's Claude models with the Agent Framework.
|
||||
|
||||
## Anthropic Client Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`anthropic_basic.py`](anthropic_basic.py) | Demonstrates how to setup a simple agent using the AnthropicClient, with both streaming and non-streaming responses. |
|
||||
| [`anthropic_advanced.py`](anthropic_advanced.py) | Shows advanced usage of the AnthropicClient, including hosted tools and `thinking`. |
|
||||
| [`anthropic_skills.py`](anthropic_skills.py) | Illustrates how to use Anthropic-managed Skills with an agent, including the Code Interpreter tool and file generation and saving. |
|
||||
| [`anthropic_foundry.py`](anthropic_foundry.py) | Example of using Foundry's Anthropic integration with the Agent Framework. |
|
||||
|
||||
## Claude Agent Examples
|
||||
|
||||
| File | Description |
|
||||
|------|-------------|
|
||||
| [`anthropic_claude_basic.py`](anthropic_claude_basic.py) | Basic usage of ClaudeAgent with streaming, non-streaming, and custom tools. |
|
||||
| [`anthropic_claude_with_tools.py`](anthropic_claude_with_tools.py) | Using built-in tools (Read, Glob, Grep, etc.). |
|
||||
| [`anthropic_claude_with_shell.py`](anthropic_claude_with_shell.py) | Shell command execution with interactive permission handling. |
|
||||
| [`anthropic_claude_with_multiple_permissions.py`](anthropic_claude_with_multiple_permissions.py) | Combining multiple tools (Bash, Read, Write) with permission prompts. |
|
||||
| [`anthropic_claude_with_url.py`](anthropic_claude_with_url.py) | Fetching and processing web content with WebFetch. |
|
||||
| [`anthropic_claude_with_mcp.py`](anthropic_claude_with_mcp.py) | Local (stdio) and remote (HTTP) MCP server configuration. |
|
||||
| [`anthropic_claude_with_session.py`](anthropic_claude_with_session.py) | Session management, persistence, and resumption. |
|
||||
|
||||
## Environment Variables
|
||||
|
||||
### Anthropic Client
|
||||
|
||||
- `ANTHROPIC_API_KEY`: Your Anthropic API key (get one from [Anthropic Console](https://console.anthropic.com/))
|
||||
- `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`: The Claude model to use in Foundry (e.g., `claude-haiku-4-5`)
|
||||
|
||||
### Claude Agent
|
||||
|
||||
- `CLAUDE_AGENT_CLI_PATH`: Path to the Claude Code CLI executable
|
||||
- `CLAUDE_AGENT_MODEL`: Model to use (sonnet, opus, haiku)
|
||||
- `CLAUDE_AGENT_CWD`: Working directory for Claude CLI
|
||||
- `CLAUDE_AGENT_PERMISSION_MODE`: Permission mode (default, acceptEdits, plan, bypassPermissions)
|
||||
- `CLAUDE_AGENT_MAX_TURNS`: Maximum number of conversation turns
|
||||
- `CLAUDE_AGENT_MAX_BUDGET_USD`: Maximum budget in USD
|
||||
@@ -0,0 +1,72 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
import asyncio
|
||||
import os
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.anthropic import AnthropicClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Anthropic Chat Agent Example
|
||||
|
||||
This sample demonstrates using Anthropic with:
|
||||
- Setting up an Anthropic-based agent with hosted tools.
|
||||
- Using the `thinking` feature.
|
||||
- Displaying both thinking and usage information during streaming responses.
|
||||
|
||||
Environment variables:
|
||||
ANTHROPIC_API_KEY — Your Anthropic API key
|
||||
ANTHROPIC_CHAT_MODEL — The Anthropic model to use (e.g., "claude-sonnet-4-6")
|
||||
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
client = AnthropicClient(
|
||||
api_key=os.getenv("ANTHROPIC_API_KEY"),
|
||||
model=os.getenv("ANTHROPIC_CHAT_MODEL"),
|
||||
)
|
||||
|
||||
# Create MCP tool configuration using instance method
|
||||
mcp_tool = client.get_mcp_tool(
|
||||
name="Microsoft_Learn_MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
)
|
||||
|
||||
# Create web search tool configuration using instance method
|
||||
web_search_tool = client.get_web_search_tool()
|
||||
|
||||
agent = Agent(
|
||||
client=client,
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful agent for both Microsoft docs questions and general questions.",
|
||||
tools=[mcp_tool, web_search_tool],
|
||||
default_options={
|
||||
# anthropic needs a value for the max_tokens parameter
|
||||
# we set it to 1024, but you can override like this:
|
||||
"max_tokens": 20000,
|
||||
"thinking": {"type": "enabled", "budget_tokens": 10000},
|
||||
},
|
||||
)
|
||||
|
||||
query = "Can you compare Python decorators with C# attributes?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
for content in chunk.contents:
|
||||
if content.type == "text_reasoning" and content.text:
|
||||
print(f"\033[32m{content.text}\033[0m", end="", flush=True)
|
||||
if content.type == "usage":
|
||||
print(f"\n\033[34m[Usage so far: {content.usage_details}]\033[0m\n", end="", flush=True)
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
|
||||
print("\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,78 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Agent, tool
|
||||
from agent_framework.anthropic import AnthropicClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Anthropic Chat Agent Example
|
||||
|
||||
This sample demonstrates using Anthropic with an agent and a single custom tool.
|
||||
"""
|
||||
|
||||
|
||||
# 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, "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 = Agent(
|
||||
client=AnthropicClient(model="claude-sonnet-4-5-20250929"),
|
||||
name="WeatherAgent",
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
query = "What's the weather like in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Result: {result}\n")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
print("=== Streaming Response Example ===")
|
||||
|
||||
agent = Agent(
|
||||
client=AnthropicClient(model="claude-sonnet-4-5-20250929"),
|
||||
name="WeatherAgent",
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=get_weather,
|
||||
)
|
||||
|
||||
query = "What's the weather like in Portland and in Paris?"
|
||||
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 main() -> None:
|
||||
print("=== Anthropic Example ===")
|
||||
|
||||
await streaming_example()
|
||||
await non_streaming_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,80 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent Basic Example
|
||||
|
||||
This sample demonstrates using ClaudeAgent for basic interactions
|
||||
with Claude Agent SDK.
|
||||
|
||||
Prerequisites:
|
||||
- Claude Code CLI must be installed and configured
|
||||
- pip install agent-framework-claude
|
||||
|
||||
Environment variables:
|
||||
- CLAUDE_AGENT_MODEL: Model to use (sonnet, opus, haiku)
|
||||
- CLAUDE_AGENT_PERMISSION_MODE: Permission mode (default, acceptEdits, bypassPermissions)
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.anthropic import ClaudeAgent
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
@tool
|
||||
def get_weather(location: Annotated[str, "The city name"]) -> str:
|
||||
"""Get the current weather for a location."""
|
||||
return f"The weather in {location} is sunny with a high of 25C."
|
||||
|
||||
|
||||
async def non_streaming_example() -> None:
|
||||
"""Example of non-streaming response."""
|
||||
print("=== Non-streaming Example ===")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
name="BasicAgent",
|
||||
instructions="You are a helpful assistant. Keep responses concise.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "What's the weather in Seattle?"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}\n")
|
||||
|
||||
|
||||
async def streaming_example() -> None:
|
||||
"""Example of streaming response."""
|
||||
print("=== Streaming Example ===")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
name="StreamingAgent",
|
||||
instructions="You are a helpful assistant.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "What's the weather in Paris?"
|
||||
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 main() -> None:
|
||||
print("=== Claude Agent Basic Example ===\n")
|
||||
|
||||
await non_streaming_example()
|
||||
await streaming_example()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+129
@@ -0,0 +1,129 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Function Approval
|
||||
|
||||
This sample demonstrates how to enforce ``approval_mode="always_require"`` on a
|
||||
``FunctionTool`` when using ``ClaudeAgent``. Because the Claude Agent SDK runs
|
||||
its own tool-calling loop, the standard agent-framework approval round-trip
|
||||
(``FunctionApprovalRequestContent`` → ``FunctionApprovalResponseContent``) is
|
||||
not available — the agent instead awaits an ``on_function_approval`` callback
|
||||
inside the tool handler before executing the tool.
|
||||
|
||||
Key points:
|
||||
- ``on_function_approval`` is set on ``ClaudeAgentOptions`` and receives a
|
||||
``FunctionCallContent`` describing the pending call. It must return ``True``
|
||||
to allow execution or ``False`` to deny it. Async callbacks are also
|
||||
supported.
|
||||
- If no callback is configured, calls to ``always_require`` tools are denied
|
||||
by default and the model receives an explanatory error so it can react.
|
||||
- This callback is independent of Claude's built-in ``permission_mode`` /
|
||||
``can_use_tool`` features, which gate the SDK's own shell/file actions.
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from random import randrange
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import Content, tool
|
||||
from agent_framework.anthropic import ClaudeAgent
|
||||
from dotenv import load_dotenv
|
||||
|
||||
load_dotenv()
|
||||
|
||||
|
||||
# Always-require tool: execution must be gated by on_function_approval.
|
||||
@tool(approval_mode="always_require")
|
||||
def get_weather_detail(location: Annotated[str, "The city and state, e.g. San Francisco, CA"]) -> str:
|
||||
"""Get a detailed weather report for a location."""
|
||||
conditions = ["sunny", "cloudy", "rainy", "stormy"]
|
||||
return (
|
||||
f"The weather in {location} is {conditions[randrange(0, len(conditions))]} "
|
||||
f"with a high of {randrange(10, 30)}C and humidity of 88%."
|
||||
)
|
||||
|
||||
|
||||
def prompt_for_approval(call: Content) -> bool:
|
||||
"""Synchronous approval prompt.
|
||||
|
||||
The callback receives a ``FunctionCallContent`` so the operator can review
|
||||
the tool name and arguments before deciding. Returning ``True`` allows the
|
||||
call; returning ``False`` denies it and a tool-error is returned to the
|
||||
model.
|
||||
"""
|
||||
print(f"\n[Function Approval Request]\n Tool: {call.name}\n Arguments: {call.arguments}")
|
||||
response = input("Approve this tool call? (y/n): ").strip().lower()
|
||||
return response in ("y", "yes")
|
||||
|
||||
|
||||
async def prompt_for_approval_async(call: Content) -> bool:
|
||||
"""Async approval prompt.
|
||||
|
||||
Use an async callback when approval requires I/O (e.g. an HTTP call to a
|
||||
review service or queueing the request to a UI). ``input()`` is wrapped
|
||||
with ``asyncio.to_thread`` so the event loop is not blocked.
|
||||
"""
|
||||
print(f"\n[Function Approval Request - async]\n Tool: {call.name}\n Arguments: {call.arguments}")
|
||||
response = await asyncio.to_thread(input, "Approve this tool call? (y/n): ")
|
||||
return response.strip().lower() in ("y", "yes")
|
||||
|
||||
|
||||
async def run_with_sync_callback() -> None:
|
||||
print("\n=== Claude Agent: synchronous approval callback ===")
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful weather assistant.",
|
||||
tools=[get_weather_detail],
|
||||
default_options={"on_function_approval": prompt_for_approval},
|
||||
)
|
||||
async with agent:
|
||||
query = "Give me the detailed weather for Seattle."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}")
|
||||
|
||||
|
||||
async def run_with_async_callback() -> None:
|
||||
print("\n=== Claude Agent: asynchronous approval callback ===")
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful weather assistant.",
|
||||
tools=[get_weather_detail],
|
||||
default_options={"on_function_approval": prompt_for_approval_async},
|
||||
)
|
||||
async with agent:
|
||||
query = "Give me the detailed weather for Tokyo."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}")
|
||||
|
||||
|
||||
async def run_without_callback() -> None:
|
||||
"""Default-deny demonstration.
|
||||
|
||||
With no ``on_function_approval`` configured, the always-require tool is
|
||||
refused and the model receives an explanatory error, so it can apologise
|
||||
or try a different approach instead of silently failing.
|
||||
"""
|
||||
print("\n=== Claude Agent: no callback configured (deny by default) ===")
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful weather assistant.",
|
||||
tools=[get_weather_detail],
|
||||
)
|
||||
async with agent:
|
||||
query = "Give me the detailed weather for Paris."
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent: Function approval enforcement ===")
|
||||
await run_with_sync_callback()
|
||||
await run_with_async_callback()
|
||||
await run_without_callback()
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,88 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with MCP Servers
|
||||
|
||||
This sample demonstrates how to configure MCP (Model Context Protocol) servers
|
||||
with ClaudeAgent. It shows both local (stdio) and remote (HTTP) server
|
||||
configurations, giving the agent access to external tools and data sources.
|
||||
|
||||
Supported MCP server types:
|
||||
- "stdio": Local process-based server
|
||||
- "http": Remote HTTP server
|
||||
- "sse": Remote SSE (Server-Sent Events) server
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key
|
||||
|
||||
SECURITY NOTE: MCP servers can expose powerful capabilities. Only configure
|
||||
servers you trust. Use permission handlers to control what actions are allowed.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework.anthropic import ClaudeAgent
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def prompt_permission(
|
||||
tool_name: str,
|
||||
tool_input: dict[str, Any],
|
||||
context: object,
|
||||
) -> PermissionResultAllow | PermissionResultDeny:
|
||||
"""Permission handler that prompts the user for approval."""
|
||||
print(f"\n[Permission Request: {tool_name}]")
|
||||
|
||||
response = input("Approve? (y/n): ").strip().lower()
|
||||
if response in ("y", "yes"):
|
||||
return PermissionResultAllow()
|
||||
return PermissionResultDeny(message="Denied by user")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with MCP Servers ===\n")
|
||||
|
||||
# Configure both local and remote MCP servers
|
||||
mcp_servers: dict[str, Any] = {
|
||||
# Local stdio server: provides filesystem access tools
|
||||
"filesystem": {
|
||||
"type": "stdio",
|
||||
"command": "npx",
|
||||
"args": ["-y", "@modelcontextprotocol/server-filesystem", "."],
|
||||
},
|
||||
# Remote HTTP server: Microsoft Learn documentation
|
||||
"microsoft-learn": {
|
||||
"type": "http",
|
||||
"url": "https://learn.microsoft.com/api/mcp",
|
||||
},
|
||||
}
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant with access to the local filesystem and Microsoft Learn.",
|
||||
default_options={
|
||||
"can_use_tool": prompt_permission,
|
||||
"mcp_servers": mcp_servers,
|
||||
},
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# Query that exercises the local filesystem MCP server
|
||||
query1 = "List the first three files in the current directory"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1)
|
||||
print(f"Agent: {result1.text}\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.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
+76
@@ -0,0 +1,76 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Multiple Permissions
|
||||
|
||||
This sample demonstrates how to enable multiple permission types with ClaudeAgent.
|
||||
By combining different tools and using a permission handler, the agent can perform
|
||||
complex tasks that require multiple capabilities.
|
||||
|
||||
Available built-in tools:
|
||||
- "Bash": Execute shell commands
|
||||
- "Read": Read files from the filesystem
|
||||
- "Write": Write files to the filesystem
|
||||
- "Edit": Edit existing files
|
||||
- "Glob": Search for files by pattern
|
||||
- "Grep": Search file contents
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key
|
||||
|
||||
SECURITY NOTE: Only enable permissions that are necessary for your use case.
|
||||
More permissions mean more potential for unintended actions.
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from typing import Any
|
||||
|
||||
from agent_framework.anthropic import ClaudeAgent
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def prompt_permission(
|
||||
tool_name: str,
|
||||
tool_input: dict[str, Any],
|
||||
context: object,
|
||||
) -> PermissionResultAllow | PermissionResultDeny:
|
||||
"""Permission handler that prompts the user for approval."""
|
||||
print(f"\n[Permission Request: {tool_name}]")
|
||||
|
||||
if "command" in tool_input:
|
||||
print(f" Command: {tool_input.get('command')}")
|
||||
if "file_path" in tool_input:
|
||||
print(f" Path: {tool_input.get('file_path')}")
|
||||
if "pattern" in tool_input:
|
||||
print(f" Pattern: {tool_input.get('pattern')}")
|
||||
|
||||
response = input("Approve? (y/n): ").strip().lower()
|
||||
if response in ("y", "yes"):
|
||||
return PermissionResultAllow()
|
||||
return PermissionResultDeny(message="Denied by user")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with Multiple Permissions ===\n")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful development assistant that can read, write files and run commands.",
|
||||
tools=["Bash", "Read", "Write", "Glob"],
|
||||
default_options={
|
||||
"can_use_tool": 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.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,152 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Session Management
|
||||
|
||||
This sample demonstrates session management with ClaudeAgent, showing
|
||||
persistent conversation capabilities. Sessions are automatically persisted
|
||||
by the Claude Code CLI.
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
from random import randint
|
||||
from typing import Annotated
|
||||
|
||||
from agent_framework import tool
|
||||
from agent_framework.anthropic import ClaudeAgent
|
||||
from dotenv import load_dotenv
|
||||
from pydantic import Field
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
@tool
|
||||
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 agent instance creates a new session."""
|
||||
print("=== Automatic Session Creation Example ===")
|
||||
|
||||
# First agent - first session
|
||||
agent1 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent1:
|
||||
query1 = "What's the weather like in Seattle?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent1.run(query1)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Second agent - new session, no memory of previous conversation
|
||||
agent2 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent2:
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent2.run(query2)
|
||||
print(f"Agent: {result2.text}")
|
||||
print("Note: Each agent instance 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 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent:
|
||||
# Create a session to maintain conversation context
|
||||
session = agent.create_session()
|
||||
|
||||
# First query
|
||||
query1 = "What's the weather like in Tokyo?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent.run(query1, session=session)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Second query - using same thread maintains context
|
||||
query2 = "How about London?"
|
||||
print(f"\nUser: {query2}")
|
||||
result2 = await agent.run(query2, session=session)
|
||||
print(f"Agent: {result2.text}")
|
||||
|
||||
# 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, session=session)
|
||||
print(f"Agent: {result3.text}")
|
||||
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_session_id."""
|
||||
print("=== Existing Session ID Example ===")
|
||||
|
||||
existing_session_id = None
|
||||
|
||||
# First agent instance - start a conversation
|
||||
agent1 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent1:
|
||||
session = agent1.create_session()
|
||||
|
||||
query1 = "What's the weather in Paris?"
|
||||
print(f"User: {query1}")
|
||||
result1 = await agent1.run(query1, session=session)
|
||||
print(f"Agent: {result1.text}")
|
||||
|
||||
# Capture the session ID for later use
|
||||
existing_session_id = session.service_session_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 = ClaudeAgent(
|
||||
instructions="You are a helpful weather agent.",
|
||||
tools=[get_weather],
|
||||
)
|
||||
|
||||
async with agent2:
|
||||
# Get session with existing session ID
|
||||
session = agent2.get_session(service_session_id=existing_session_id)
|
||||
|
||||
query2 = "What was the last city I asked about?"
|
||||
print(f"User: {query2}")
|
||||
result2 = await agent2.run(query2, session=session)
|
||||
print(f"Agent: {result2.text}")
|
||||
print("Note: The agent continues the conversation using the session ID.\n")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude 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,64 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Shell Permissions
|
||||
|
||||
This sample demonstrates how to enable shell command execution with ClaudeAgent.
|
||||
By providing a permission handler via `can_use_tool`, the agent can execute
|
||||
shell commands to perform tasks like listing files, running scripts, or executing system commands.
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key
|
||||
|
||||
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 typing import Any
|
||||
|
||||
from agent_framework.anthropic import ClaudeAgent
|
||||
from claude_agent_sdk import PermissionResultAllow, PermissionResultDeny
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def prompt_permission(
|
||||
tool_name: str,
|
||||
tool_input: dict[str, Any],
|
||||
context: object,
|
||||
) -> PermissionResultAllow | PermissionResultDeny:
|
||||
"""Permission handler that prompts the user for approval."""
|
||||
print(f"\n[Permission Request: {tool_name}]")
|
||||
|
||||
if "command" in tool_input:
|
||||
print(f" Command: {tool_input.get('command')}")
|
||||
|
||||
response = input("Approve? (y/n): ").strip().lower()
|
||||
if response in ("y", "yes"):
|
||||
return PermissionResultAllow()
|
||||
return PermissionResultDeny(message="Denied by user")
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with Shell Permissions ===\n")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant that can execute shell commands.",
|
||||
tools=["Bash"],
|
||||
default_options={
|
||||
"can_use_tool": prompt_permission,
|
||||
},
|
||||
)
|
||||
|
||||
async with agent:
|
||||
query = "List the first 3 markdown (.md) files in the current directory"
|
||||
print(f"User: {query}")
|
||||
result = await agent.run(query)
|
||||
print(f"Agent: {result.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,47 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with Built-in Tools
|
||||
|
||||
This sample demonstrates using ClaudeAgent with built-in tools for file operations.
|
||||
Built-in tools are specified as strings in the tools parameter.
|
||||
|
||||
Available built-in tools:
|
||||
- "Bash": Execute shell commands
|
||||
- "Read": Read files from the filesystem
|
||||
- "Write": Write files to the filesystem
|
||||
- "Edit": Edit existing files
|
||||
- "Glob": Search for files by pattern
|
||||
- "Grep": Search file contents
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
||||
from agent_framework.anthropic import ClaudeAgent
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with Built-in Tools ===\n")
|
||||
|
||||
# Built-in tools can be specified as strings in the tools parameter
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant that can read files.",
|
||||
tools=["Read", "Glob"],
|
||||
)
|
||||
|
||||
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.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,45 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
"""
|
||||
Claude Agent with URL Fetching
|
||||
|
||||
This sample demonstrates how to enable URL fetching with ClaudeAgent.
|
||||
By enabling the WebFetch tool, the agent can fetch and process content from web URLs.
|
||||
|
||||
Available web tools:
|
||||
- "WebFetch": Fetch content from URLs
|
||||
- "WebSearch": Search the web
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key
|
||||
|
||||
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.anthropic import ClaudeAgent
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
print("=== Claude Agent with URL Fetching ===\n")
|
||||
|
||||
agent = ClaudeAgent(
|
||||
instructions="You are a helpful assistant that can fetch and summarize web content.",
|
||||
tools=["WebFetch"],
|
||||
)
|
||||
|
||||
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.text}\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,76 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
import asyncio
|
||||
|
||||
from agent_framework import Agent
|
||||
from agent_framework.foundry import AnthropicFoundryClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Anthropic Foundry Chat Agent Example
|
||||
|
||||
This sample demonstrates using Anthropic with:
|
||||
- Setting up an Anthropic-based agent with hosted tools.
|
||||
- Using the `thinking` feature.
|
||||
- Displaying both thinking and usage information during streaming responses.
|
||||
|
||||
This example requires `anthropic>=0.74.0` and an endpoint in Foundry for Anthropic.
|
||||
|
||||
To use the Foundry integration ensure you have the following environment variables set:
|
||||
- ANTHROPIC_FOUNDRY_API_KEY
|
||||
Alternatively you can pass in a azure_ad_token_provider function to the AsyncAnthropicFoundry constructor.
|
||||
- ANTHROPIC_FOUNDRY_RESOURCE
|
||||
Should be the resource name portion of your Foundry Anthropic URL, such as <your-resource-name>.
|
||||
- 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
|
||||
Should be something like claude-haiku-4-5
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
client = AnthropicFoundryClient()
|
||||
|
||||
# Create MCP tool configuration using instance method
|
||||
mcp_tool = client.get_mcp_tool(
|
||||
name="Microsoft_Learn_MCP",
|
||||
url="https://learn.microsoft.com/api/mcp",
|
||||
)
|
||||
|
||||
# Create web search tool configuration using instance method
|
||||
web_search_tool = client.get_web_search_tool()
|
||||
|
||||
agent = Agent(
|
||||
client=client,
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful agent for both Microsoft docs questions and general questions.",
|
||||
tools=[mcp_tool, web_search_tool],
|
||||
default_options={
|
||||
# anthropic needs a value for the max_tokens parameter
|
||||
# we set it to 1024, but you can override like this:
|
||||
"max_tokens": 20000,
|
||||
"thinking": {"type": "enabled", "budget_tokens": 10000},
|
||||
},
|
||||
)
|
||||
|
||||
query = "Can you compare Python decorators with C# attributes?"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
for content in chunk.contents:
|
||||
if content.type == "text_reasoning":
|
||||
print(f"\033[32m{content.text}\033[0m", end="", flush=True)
|
||||
if content.type == "usage":
|
||||
print(f"\n\033[34m[Usage so far: {content.usage_details}]\033[0m\n", end="", flush=True)
|
||||
if chunk.text:
|
||||
print(chunk.text, end="", flush=True)
|
||||
|
||||
print("\n")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,99 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import logging
|
||||
from pathlib import Path
|
||||
|
||||
from agent_framework import Agent, Content
|
||||
from agent_framework.anthropic import AnthropicChatOptions, AnthropicClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
logger = logging.getLogger(__name__)
|
||||
"""
|
||||
Anthropic Skills Agent Example
|
||||
|
||||
This sample demonstrates using Anthropic with:
|
||||
- Listing and using Anthropic-managed Skills.
|
||||
- One approach to add additional beta flags.
|
||||
You can also set additonal_chat_options with "additional_beta_flags" per request.
|
||||
- Creating an agent with the Code Interpreter tool and a Skill.
|
||||
- Catching and downloading generated files from the agent.
|
||||
|
||||
Environment variables:
|
||||
- ANTHROPIC_API_KEY: Your Anthropic API key
|
||||
- ANTHROPIC_CHAT_MODEL_ID: The Anthropic model to use, such as "claude-sonnet-4-5-20250929"
|
||||
"""
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example of streaming response (get results as they are generated)."""
|
||||
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"]) # type: ignore
|
||||
for skill in skills.data:
|
||||
print(f"{skill.source}: {skill.id} (version: {skill.latest_version})")
|
||||
|
||||
# Create a agent with the pptx skill enabled
|
||||
# Skills also need the code interpreter tool to function
|
||||
agent = Agent(
|
||||
client=client,
|
||||
name="DocsAgent",
|
||||
instructions="You are a helpful agent for creating powerpoint presentations.",
|
||||
tools=client.get_code_interpreter_tool(),
|
||||
default_options={
|
||||
"max_tokens": 4096,
|
||||
"thinking": {"type": "enabled", "budget_tokens": 2000},
|
||||
"container": {"skills": [{"type": "anthropic", "skill_id": "pptx", "version": "latest"}]},
|
||||
},
|
||||
)
|
||||
|
||||
print(
|
||||
"The agent output will use the following colors:\n"
|
||||
"\033[0mUser: (default)\033[0m\n"
|
||||
"\033[0mAgent: (default)\033[0m\n"
|
||||
"\033[32mAgent Reasoning: (green)\033[0m\n"
|
||||
"\033[34mUsage: (blue)\033[0m\n"
|
||||
)
|
||||
query = "Create a simple presentation with 2 slides about Python programming"
|
||||
print(f"User: {query}")
|
||||
print("Agent: ", end="", flush=True)
|
||||
files: list[Content] = []
|
||||
async for chunk in agent.run(query, stream=True):
|
||||
for content in chunk.contents:
|
||||
match content.type:
|
||||
case "text":
|
||||
print(content.text, end="", flush=True)
|
||||
case "text_reasoning":
|
||||
print(f"\033[32m{content.text}\033[0m", end="", flush=True)
|
||||
case "usage":
|
||||
print(f"\n\033[34m[Usage so far: {content.usage_details}]\033[0m\n", end="", flush=True)
|
||||
case "hosted_file":
|
||||
# Catch generated files
|
||||
files.append(content)
|
||||
case _:
|
||||
logger.debug("Unhandled content type: %s", content.type)
|
||||
pass
|
||||
|
||||
print("\n")
|
||||
if files:
|
||||
# Save to a new file (will be in the folder where you are running this script)
|
||||
# When running this sample multiple times, the files will be overritten
|
||||
# Since I'm using the pptx skill, the files will be PowerPoint presentations
|
||||
print("Generated files:")
|
||||
for idx, file in enumerate(files):
|
||||
if file.file_id is None:
|
||||
continue
|
||||
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:
|
||||
await file_content.write_to_file(f.name)
|
||||
print(f"File {idx}: python_programming-{idx}.pptx saved to disk.")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
@@ -0,0 +1,102 @@
|
||||
# Copyright (c) Microsoft. All rights reserved.
|
||||
|
||||
import asyncio
|
||||
import subprocess
|
||||
from typing import Any
|
||||
|
||||
from agent_framework import Agent, Message, tool
|
||||
from agent_framework.anthropic import AnthropicClient
|
||||
from dotenv import load_dotenv
|
||||
|
||||
# Load environment variables from .env file
|
||||
load_dotenv()
|
||||
|
||||
"""
|
||||
Anthropic Client with Shell Tool Example
|
||||
|
||||
This sample demonstrates using @tool(approval_mode=...) with AnthropicClient
|
||||
for executing bash commands locally. The bash tool tells the model it can
|
||||
request shell commands, while the actual execution happens on YOUR machine
|
||||
via a user-provided function.
|
||||
|
||||
SECURITY NOTE: This example executes real commands on your local machine.
|
||||
Only enable this when you trust the agent's actions. Consider implementing
|
||||
allowlists, sandboxing, or approval workflows for production use.
|
||||
"""
|
||||
|
||||
|
||||
@tool(approval_mode="always_require")
|
||||
def run_bash(command: str) -> str:
|
||||
"""Execute a bash command using subprocess and return the output."""
|
||||
try:
|
||||
result = subprocess.run(
|
||||
command,
|
||||
shell=True,
|
||||
capture_output=True,
|
||||
text=True,
|
||||
timeout=30,
|
||||
)
|
||||
parts: list[str] = []
|
||||
if result.stdout:
|
||||
parts.append(result.stdout)
|
||||
if result.stderr:
|
||||
parts.append(f"stderr: {result.stderr}")
|
||||
parts.append(f"exit_code: {result.returncode}")
|
||||
return "\n".join(parts)
|
||||
except subprocess.TimeoutExpired:
|
||||
return "Command timed out after 30 seconds"
|
||||
except Exception as e:
|
||||
return f"Error executing command: {e}"
|
||||
|
||||
|
||||
async def main() -> None:
|
||||
"""Example showing how to use the shell tool with AnthropicClient."""
|
||||
print("=== Anthropic Agent with Shell Tool Example ===")
|
||||
print("NOTE: Commands will execute on your local machine.\n")
|
||||
|
||||
client = AnthropicClient()
|
||||
shell = client.get_shell_tool(func=run_bash)
|
||||
agent = Agent(
|
||||
client=client,
|
||||
instructions="You are a helpful assistant that can execute bash commands to answer questions.",
|
||||
tools=[shell],
|
||||
)
|
||||
|
||||
query = "Use bash to print 'Hello from Anthropic shell!' and show the current working directory"
|
||||
print(f"User: {query}")
|
||||
result = await run_with_approvals(query, agent)
|
||||
print(f"Result: {result}\n")
|
||||
|
||||
|
||||
async def run_with_approvals(query: str, agent: Agent[Any]) -> Any:
|
||||
"""Run the agent and handle shell approvals outside tool execution."""
|
||||
current_input: str | list[Any] = query
|
||||
while True:
|
||||
result = await agent.run(current_input)
|
||||
if not result.user_input_requests:
|
||||
return result
|
||||
|
||||
next_input: list[Any] = [query]
|
||||
rejected = False
|
||||
for user_input_needed in result.user_input_requests:
|
||||
if user_input_needed.function_call is None:
|
||||
continue
|
||||
print(
|
||||
f"\nShell request: {user_input_needed.function_call.name}"
|
||||
f"\nArguments: {user_input_needed.function_call.arguments}"
|
||||
)
|
||||
user_approval = await asyncio.to_thread(input, "\nApprove shell command? (y/n): ")
|
||||
approved = user_approval.strip().lower() == "y"
|
||||
next_input.append(Message("assistant", [user_input_needed]))
|
||||
next_input.append(Message("user", [user_input_needed.to_function_approval_response(approved)]))
|
||||
if not approved:
|
||||
rejected = True
|
||||
break
|
||||
if rejected:
|
||||
print("\nShell command rejected. Stopping without additional approval prompts.")
|
||||
return "Shell command execution was rejected by user."
|
||||
current_input = next_input
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main())
|
||||
Reference in New Issue
Block a user