diff --git a/python/samples/04-hosting/foundry-hosted-agents/README.md b/python/samples/04-hosting/foundry-hosted-agents/README.md index 72b21128ba..dfecb1531b 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/README.md @@ -13,7 +13,8 @@ This directory contains samples that demonstrate how to use hosted [Agent Framew | 3 | [MCP](responses/03_mcp/) | An agent connected to a remote MCP server (GitHub), demonstrating external MCP tool provider integration. | | 4 | [Foundry Toolbox](responses/04_foundry_toolbox/) | An agent using Azure Foundry Toolbox, demonstrating toolbox provisioning and querying available tools at runtime. | | 5 | [Workflows](responses/05_workflows/) | An agent with a multi-step orchestrated workflow, demonstrating chaining prompts through an orchestrated flow. | -| 6 | [Using deployed agent](responses/using_deployed_agent.py) | A sample demonstrating how to invoke an agent that has already been deployed to Foundry, showing how to interact with a hosted agent in code. | +| 6 | [Files](responses/06_files/) | An agent demonstrating how to work with files in a hosted agent session, including uploading files to a hosted agent session and having the agent read and manipulate those files at runtime. | +| 7 | [Using deployed agent](responses/using_deployed_agent.py) | A sample demonstrating how to invoke an agent that has already been deployed to Foundry, showing how to interact with a hosted agent in code. | ### Invocations API diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md index dc1883778f..78447f73e4 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/01_basic/README.md @@ -14,6 +14,10 @@ See [main.py](main.py) for the full implementation. The agent is hosted using the [Agent Framework](https://github.com/microsoft/agent-framework) with the `ResponsesHostServer`, which provisions a REST API endpoint compatible with the OpenAI Responses protocol. +## Running the Agent Host + +Follow the instructions in the [Running the Agent Host Locally](../../README.md#running-the-agent-host-locally) section of the README in the parent directory to run the agent host. + ## Interacting with the agent > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/main.py b/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/main.py index 43b77b9fe0..cb49e070c6 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/main.py +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/02_tools/main.py @@ -25,7 +25,7 @@ def get_weather( return f"The weather in {location} is {conditions[randint(0, 3)]} with a high of {randint(10, 30)}°C." -@tool(approval_mode="always_require") +@tool(approval_mode="never_require") def run_bash(command: str) -> str: """Execute a shell command locally and return stdout, stderr, and exit code.""" try: diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md index c7f8721d5c..fb774681f9 100644 --- a/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/04_foundry_toolbox/README.md @@ -16,8 +16,6 @@ You can also create a Foundry Toolbox in the Foundry portal. Read more about it The agent uses `FoundryChatClient` from the Agent Framework to create an OpenAI-compatible Responses client. It loads a named Foundry Toolbox via `client.get_toolbox(name)` — the toolbox is a server-side bundle of tool configurations (e.g., `code_interpreter`, `web_search`) defined in the Foundry portal or by `azd provision`. Omitting `version` resolves the toolbox's current default version at runtime. -The sample then narrows the toolbox to a subset of tool types via `select_toolbox_tools(toolbox, include_types=[...])` before handing it to the agent. This demonstrates how one toolbox can be reused across agents that each expose only the tools they need — here, the agent only sees `code_interpreter` even though the toolbox also includes `web_search`. - See [main.py](main.py) for the full implementation. ### Agent Hosting @@ -28,6 +26,18 @@ The agent is hosted using the [Agent Framework](https://github.com/microsoft/age Follow the instructions in the [Running the Agent Host Locally](../../README.md#running-the-agent-host-locally) section of the README in the parent directory to run the agent host. +An extra environoment variable `TOOLBOX_NAME` must be set to the name of the Foundry Toolbox that the agent should load at runtime. This allows the agent host to dynamically retrieve the correct toolbox from Foundry when it starts. Run the following: + +```bash +export TOOLBOX_NAME="" +``` + +Or in PowerShell: + +```powershell +$env:TOOLBOX_NAME="" +``` + ## Interacting with the agent > Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.dockerignore b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.dockerignore new file mode 100644 index 0000000000..1ec65f9d1c --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.dockerignore @@ -0,0 +1,9 @@ +.venv +__pycache__ +*.pyc +*.pyo +*.pyd +.Python + +# Local-only client tooling and sample data; not needed inside the agent image. +resources/ diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.env.example b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.env.example new file mode 100644 index 0000000000..91ae96ac46 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/.env.example @@ -0,0 +1,3 @@ +FOUNDRY_PROJECT_ENDPOINT="..." +AZURE_AI_MODEL_DEPLOYMENT_NAME="..." +TOOLBOX_NAME="..." \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/Dockerfile b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/Dockerfile new file mode 100644 index 0000000000..eaffb94f19 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/Dockerfile @@ -0,0 +1,16 @@ +FROM python:3.12-slim + +WORKDIR /app + +COPY . user_agent/ +WORKDIR /app/user_agent + +RUN if [ -f requirements.txt ]; then \ + pip install -r requirements.txt; \ + else \ + echo "No requirements.txt found"; \ + fi + +EXPOSE 8088 + +CMD ["python", "main.py"] \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/README.md b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/README.md new file mode 100644 index 0000000000..d5722553a2 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/README.md @@ -0,0 +1,111 @@ +# What this sample demonstrates + +An [Agent Framework](https://github.com/microsoft/agent-framework) agent that uses a local shell tool and a code interpreter tool for working with files, and hosted using the **Responses protocol**. + +## How It Works + +### Model Integration + +The agent uses `FoundryChatClient` from the Agent Framework to create a Responses client from the project endpoint and model deployment. The agent supports both streaming (SSE events) and non-streaming (JSON) response modes. + +See [main.py](main.py) for the full implementation. + +### Agent Hosting + +The agent is hosted using the [Agent Framework](https://github.com/microsoft/agent-framework) with the `ResponsesHostServer`, which provisions a REST API endpoint compatible with the OpenAI Responses protocol. + +### Tools + +This agent uses two tools: + +1. **Local Shell Tool (`run_bash`)** – Executes shell commands locally to perform filesystem operations such as listing files, reading file contents, or creating directories. The tool captures stdout, stderr, and the exit code of the command and returns them as a string. +2. **Code Interpreter Tool (`code_interpreter`)** – Allows the agent to execute Python code in a safe, sandboxed environment. This tool can be used to read and manipulate file contents, perform calculations, or run other Python logic as part of the agent's responses. + +> In this sample, the local shell tool is a function tool defined in Python using the `@tool` decorator from the Agent Framework. The code interpreter tool is a managed tool provided by [Foundry Toolbox](https://learn.microsoft.com/en-us/azure/foundry/agents/how-to/tools/toolbox). Learn more about foundry toolbox integration with hosted agents with this [sample](../04_foundry_toolbox/). + +## Running the Agent Host + +Follow the instructions in the [Running the Agent Host Locally](../../README.md#running-the-agent-host-locally) section of the README in the parent directory to run the agent host. + +An extra environoment variable `TOOLBOX_NAME` must be set to the name of the Foundry Toolbox that the agent should load at runtime. This allows the agent host to dynamically retrieve the correct toolbox from Foundry when it starts. Run the following: + +```bash +export TOOLBOX_NAME="" +``` + +Or in PowerShell: + +```powershell +$env:TOOLBOX_NAME="" +``` + +## Interacting with the agent + +> Depending on how you run the agent host, you can invoke the agent using `curl` (`Invoke-WebRequest` in PowerShell) or `azd`. Please refer to the [parent README](../../README.md) for more details. Use this README for sample queries you can send to the agent. + +Send a POST request to the server with a JSON body containing an `"input"` field to interact with the agent. For example: + +```bash +curl -X POST http://localhost:8088/responses -H "Content-Type: application/json" -d '{"input": "Find the quauterly report under `{cwd}/resources` and tell me the difference of revenue between q1 2026 and q1 2025?"}' +``` + +The server will respond with a JSON object containing the response text and a response ID. You can use this response ID to continue the conversation in subsequent requests. + +## Deploying the Agent to Foundry + +To host the agent on Foundry, follow the instructions in the [Deploying the Agent to Foundry](../../README.md#deploying-the-agent-to-foundry) section of the README in the parent directory. + +## Uploading a file to a session + +Deploying the agent won't automatically upload the files included with this sample to Foundry. To make these files available to the agent at runtime, you must upload them to a [hosted agent session](https://learn.microsoft.com/azure/foundry/agents/how-to/manage-hosted-sessions). Files are tied to a specific hosted agent session, so each time you start a new session you will need to upload the files again if the agent needs access to them during that session. + +After you deploy the agent to Foundry, you have two ways to interact with the agent: + +1. Using `azd ai agent invoke`. +2. Through the Foundry portal. + +### Using `azd ai agent invoke` + +After successfully deploying the agent to Foundry, run the following command: + +> You must remain in the directory where your `azd` project is initialized so that the CLI can locate the deployed agent configuration. + +```bash +azd ai agent invoke "Hi!" +``` + +The command will invoke the agent and the server will create a new session if one does not already exist for this interaction, returning the agent's response from the hosted agent session. Run the following if you want to force a new session: + +```bash +azd ai agent invoke --new-session "Hi!" +``` + +Run the following command to upload a file to the hosted agent session: + +```bash +azd ai agent files upload -f +``` + +> The above command will automatically detect the last active session and upload the file to that session without requiring you to explicitly provide a session ID. It is also possible to specify a particular session ID to upload the file to a specific hosted agent session by using the `--session-id` flag. Run `azd ai agent files upload -h` to see the full list of options and flags available for the `upload` command. + +Once the file is uploaded to the hosted agent session, the agent will be able to access it during that session and use it to respond to queries that reference the uploaded file. + +Invoke the agent again with a query that references the uploaded file to see how it can now use the file in its responses. For example: + +```bash +azd ai agent invoke "Find the quarterly report under the home directory and tell me the difference of revenue between q1 2026 and q1 2025?" +``` + +### Using the Foundry Portal + +Similar to using the `azd` CLI, you must invoke the agent first to create a session: + +![alt text](./resources/start-a-session.png) + +Once the session is created, you can grad the session ID and use `azd ai agent files upload --session-id ` to upload files to that specific hosted agent session. + +![alt text](./resources/session-started.png) + +Or you can upload files directly through the Foundry portal by navigating to Files tab in the agent playground: + +![alt text](./resources/file-upload-portal.png) diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/agent.manifest.yaml b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/agent.manifest.yaml new file mode 100644 index 0000000000..9fde493341 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/agent.manifest.yaml @@ -0,0 +1,32 @@ +name: agent-framework-agent-files-responses +description: > + An Agent Framework agent that can work with files hosted by Foundry. +metadata: + tags: + - Agent Framework + - AI Agent Hosting + - Azure AI AgentServer + - Responses Protocol + - Streaming +template: + name: agent-framework-agent-files-responses + kind: hosted + protocols: + - protocol: responses + version: 1.0.0 + environment_variables: + - name: AZURE_AI_MODEL_DEPLOYMENT_NAME + value: "{{AZURE_AI_MODEL_DEPLOYMENT_NAME}}" + - name: TOOLBOX_NAME + value: "agent-tools" +resources: + - kind: model + id: gpt-4.1-mini + name: AZURE_AI_MODEL_DEPLOYMENT_NAME + - kind: toolbox + name: agent-tools + tools: + - type: web_search + name: web_search + - type: code_interpreter + name: code_interpreter \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/agent.yaml b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/agent.yaml new file mode 100644 index 0000000000..5c95360fa3 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/agent.yaml @@ -0,0 +1,9 @@ +# yaml-language-server: $schema=https://raw.githubusercontent.com/microsoft/AgentSchema/refs/heads/main/schemas/v1.0/ContainerAgent.yaml +kind: hosted +name: agent-framework-agent-files-responses +protocols: + - protocol: responses + version: 1.0.0 +resources: + cpu: '0.25' + memory: '0.5Gi' \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/main.py b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/main.py new file mode 100644 index 0000000000..f136b49ff7 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/main.py @@ -0,0 +1,83 @@ +# Copyright (c) Microsoft. All rights reserved. + +import asyncio +import os +import subprocess + +from agent_framework import Agent, tool +from agent_framework.foundry import FoundryChatClient +from agent_framework_foundry import select_toolbox_tools +from agent_framework_foundry_hosting import ResponsesHostServer +from azure.identity import DefaultAzureCredential +from dotenv import load_dotenv + +# Load environment variables from .env file +load_dotenv() + + +@tool( + description="Execute a shell command for filesystem operations.", + approval_mode="never_require", +) +def run_bash(command: str) -> str: + """Execute a shell command locally and return stdout, stderr, and exit code.""" + 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(): + client = FoundryChatClient( + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["AZURE_AI_MODEL_DEPLOYMENT_NAME"], + credential=DefaultAzureCredential(), + ) + + # Load the named toolbox from the Foundry project. Omitting `version` + # resolves the toolbox's current default version at runtime. + toolbox = await client.get_toolbox(os.environ["TOOLBOX_NAME"]) + # The toolbox deployed has two tools: (see agent.manifest.yaml) + # - `code_interpreter` + # - `web_search` + # We only need the `code_interpreter` tool for this sample + selected_tools = select_toolbox_tools( + toolbox, + include_names=["code_interpreter"], + ) + + agent = Agent( + client=client, + instructions=( + "You are a friendly assistant. Keep your answers brief. " + "Make sure all mathematical calculations are performed using the code interpreter " + "instead of mental arithmetic." + ), + tools=[run_bash] + selected_tools, + # History will be managed by the hosting infrastructure, thus there + # is no need to store history by the service. Learn more at: + # https://developers.openai.com/api/reference/resources/responses/methods/create + default_options={"store": False}, + ) + + server = ResponsesHostServer(agent) + await server.run_async() + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/requirements.txt b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/requirements.txt new file mode 100644 index 0000000000..f7dc62f3e3 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/requirements.txt @@ -0,0 +1,2 @@ +agent-framework +agent-framework-foundry-hosting \ No newline at end of file diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/contoso_q1_2026_report.txt b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/contoso_q1_2026_report.txt new file mode 100644 index 0000000000..858192a7d3 --- /dev/null +++ b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/contoso_q1_2026_report.txt @@ -0,0 +1,121 @@ +Contoso Corporation +Quarterly Report — Q1 2026 (Three months ended March 31, 2026) + +DISCLAIMER +This document contains fictional data for sample/demo purposes only. +Contoso is a fictional company; all figures below are fabricated. + +------------------------------------------------------------ +1. EXECUTIVE SUMMARY +------------------------------------------------------------ +Contoso delivered a solid first quarter, with total revenue of +$1,482.6M, up 11.4% year-over-year. Growth was led by the Cloud +Services segment (+22.7% YoY) and continued double-digit expansion +in International markets. Operating margin expanded 140 basis points +to 23.8% on disciplined cost management and improved gross margin. + +Key highlights: + - Revenue: $1,482.6M (YoY +11.4%) + - Gross profit: $912.0M (gross margin 61.5%) + - Operating income: $352.9M (operating margin 23.8%) + - Net income: $268.4M (net margin 18.1%) + - Diluted EPS: $1.27 (vs. $1.04 prior year) + - Free cash flow: $311.5M + - Cash & equivalents: $2,140.8M + +------------------------------------------------------------ +2. INCOME STATEMENT (USD millions, unaudited) +------------------------------------------------------------ + Q1 2026 Q1 2025 YoY % +Revenue 1,482.6 1,330.7 +11.4% +Cost of revenue 570.6 538.9 +5.9% +Gross profit 912.0 791.8 +15.2% + Gross margin 61.5% 59.5% +200 bps +Operating expenses + Research & development 241.4 220.5 +9.5% + Sales & marketing 218.7 205.1 +6.6% + General & administrative 99.0 88.6 +11.7% +Total operating expenses 559.1 514.2 +8.7% +Operating income 352.9 277.6 +27.1% + Operating margin 23.8% 20.9% +290 bps +Other income / (expense), net 8.4 5.1 +Income before taxes 361.3 282.7 +Provision for income taxes 92.9 72.6 +Net income 268.4 210.1 +27.7% +Diluted EPS (USD) 1.27 1.04 +22.1% + +------------------------------------------------------------ +3. REVENUE BY SEGMENT (USD millions) +------------------------------------------------------------ +Segment Q1 2026 Q1 2025 YoY % +Cloud Services 612.4 499.1 +22.7% +Productivity Software 448.9 422.6 +6.2% +Devices & Hardware 267.0 260.4 +2.5% +Professional Services 154.3 148.6 +3.8% +Total revenue 1,482.6 1,330.7 +11.4% + +------------------------------------------------------------ +4. REVENUE BY GEOGRAPHY (USD millions) +------------------------------------------------------------ +Region Q1 2026 Q1 2025 YoY % +North America 812.1 756.0 +7.4% +EMEA 388.5 340.2 +14.2% +Asia-Pacific 221.7 183.4 +20.9% +Latin America 60.3 51.1 +18.0% +Total revenue 1,482.6 1,330.7 +11.4% + +------------------------------------------------------------ +5. SELECTED BALANCE SHEET ITEMS (USD millions) +------------------------------------------------------------ + Mar 31, Dec 31, + 2026 2025 +Cash & equivalents 2,140.8 1,902.3 +Short-term investments 845.6 820.4 +Accounts receivable, net 1,012.7 988.5 +Total current assets 4,510.2 4,190.6 +Goodwill & intangibles 2,330.1 2,338.9 +Total assets 9,884.5 9,512.0 +Total current liabilities 2,118.4 2,054.7 +Long-term debt 1,750.0 1,750.0 +Total liabilities 4,402.6 4,310.5 +Total stockholders' equity 5,481.9 5,201.5 + +------------------------------------------------------------ +6. CASH FLOW HIGHLIGHTS (USD millions) +------------------------------------------------------------ + Q1 2026 Q1 2025 +Net cash from operating activities 382.0 298.7 +Capital expenditures (70.5) (62.1) +Free cash flow 311.5 236.6 +Share repurchases (120.0) (90.0) +Dividends paid (54.2) (48.6) + +------------------------------------------------------------ +7. KEY OPERATING METRICS +------------------------------------------------------------ +Cloud paid seats (millions) 48.6 39.7 +22.4% +Cloud net revenue retention 118% 114% +Active enterprise customers 18,420 16,905 +9.0% +Headcount (end of period) 22,140 20,610 +7.4% + +------------------------------------------------------------ +8. OUTLOOK — Q2 2026 GUIDANCE +------------------------------------------------------------ +Revenue: $1,520M – $1,560M (YoY +10% to +13%) +Operating margin: 23.5% – 24.5% +Diluted EPS: $1.30 – $1.36 +Capital expenditures: ~$80M + +Management remains confident in the full-year plan and reiterates +fiscal-year 2026 revenue growth of 10–12% and operating-margin +expansion of 100–150 basis points versus FY 2025. + +------------------------------------------------------------ +9. NOTES +------------------------------------------------------------ +- All figures are unaudited and rounded to one decimal place. +- Year-over-year comparisons are versus the same period in 2025. +- "Free cash flow" is defined as net cash from operating activities + less capital expenditures, and is a non-GAAP measure. +- This sample report is intended solely for demonstration of an + agent-driven document analysis pipeline. diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/file-upload-portal.png b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/file-upload-portal.png new file mode 100644 index 0000000000..cf99e40a5f Binary files /dev/null and b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/file-upload-portal.png differ diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/session-started.png b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/session-started.png new file mode 100644 index 0000000000..240865b79e Binary files /dev/null and b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/session-started.png differ diff --git a/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/start-a-session.png b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/start-a-session.png new file mode 100644 index 0000000000..26a5f7c7a0 Binary files /dev/null and b/python/samples/04-hosting/foundry-hosted-agents/responses/06_files/resources/start-a-session.png differ