diff --git a/.github/scripts/discover_hosted_samples.py b/.github/scripts/discover_hosted_samples.py new file mode 100644 index 000000000..f8e47519f --- /dev/null +++ b/.github/scripts/discover_hosted_samples.py @@ -0,0 +1,75 @@ +#!/usr/bin/env python3 +""" +Discover valid hosted agent samples for GitHub Actions matrix. + +A valid hosted agent sample is a directory containing: +- agent.yaml (defines the hosted agent configuration) +- main.py (entry point) +- requirements.txt (dependencies) + +Outputs JSON array of sample paths relative to repository root. +""" + +import json +import sys +from pathlib import Path + + +def find_hosted_samples(base_path: Path) -> list[dict]: + """Find all valid hosted agent sample directories. + + Args: + base_path: Path to the hosted-agents directory + + Returns: + List of dicts with sample info (path, name, framework) + """ + samples = [] + + for agent_yaml in base_path.rglob("agent.yaml"): + sample_dir = agent_yaml.parent + + # Validate required files exist + if not (sample_dir / "main.py").exists(): + continue + if not (sample_dir / "requirements.txt").exists(): + continue + + # Determine framework based on parent directory + rel_path = sample_dir.relative_to(base_path) + parts = rel_path.parts + framework = parts[0] if len(parts) > 1 else "unknown" + + # Get path relative to repo root + repo_root = base_path.parent.parent.parent + sample_path = str(sample_dir.relative_to(repo_root)).replace("\\", "/") + + samples.append({ + "path": sample_path, + "name": sample_dir.name, + "framework": framework + }) + + return sorted(samples, key=lambda x: x["path"]) + + +def main(): + # Default path relative to repo root + repo_root = Path(__file__).parent.parent.parent + hosted_agents_path = repo_root / "samples" / "python" / "hosted-agents" + + if not hosted_agents_path.exists(): + print(f"Error: Path not found: {hosted_agents_path}", file=sys.stderr) + sys.exit(1) + + samples = find_hosted_samples(hosted_agents_path) + + if not samples: + print("Warning: No valid hosted agent samples found", file=sys.stderr) + + # Output JSON for GitHub Actions + print(json.dumps(samples)) + + +if __name__ == "__main__": + main() diff --git a/.github/scripts/test_hosted_sample.py b/.github/scripts/test_hosted_sample.py new file mode 100644 index 000000000..0ae53f81f --- /dev/null +++ b/.github/scripts/test_hosted_sample.py @@ -0,0 +1,225 @@ +#!/usr/bin/env python3 +""" +Test a hosted agent sample by starting the server and sending a test request. + +Usage: + python test_hosted_sample.py + +The script will: +1. Install dependencies from requirements.txt +2. Start the server (python main.py) +3. Wait for server to be ready (up to 30 seconds) +4. Send a test request to /responses endpoint +5. Validate the response +6. Report success/failure with details +""" + +import argparse +import json +import subprocess +import sys +import time +from pathlib import Path + +import requests +import yaml + + +DEFAULT_TEST_INPUT = "Hello, please introduce yourself briefly." +SERVER_PORT = 8088 +SERVER_URL = f"http://localhost:{SERVER_PORT}" +STARTUP_TIMEOUT = 30 # seconds +REQUEST_TIMEOUT = 120 # seconds + + +def extract_test_input(agent_yaml_path: Path) -> str: + """Extract test input from agent.yaml metadata.example if available.""" + try: + with open(agent_yaml_path) as f: + config = yaml.safe_load(f) + + examples = config.get("metadata", {}).get("example", []) + if examples and isinstance(examples, list): + for example in examples: + if example.get("role") == "user" and example.get("content"): + return example["content"] + except Exception as e: + print(f"Warning: Could not parse agent.yaml for test input: {e}") + + return DEFAULT_TEST_INPUT + + +def wait_for_server(timeout: int = STARTUP_TIMEOUT) -> bool: + """Wait for the server to be ready to accept connections.""" + start_time = time.time() + + while time.time() - start_time < timeout: + try: + # Try to connect to the server + response = requests.get(SERVER_URL, timeout=2) + # Any response (even 404) means server is up + return True + except requests.exceptions.ConnectionError: + time.sleep(1) + except requests.exceptions.Timeout: + time.sleep(1) + + return False + + +def send_test_request(test_input: str) -> dict: + """Send a test request to the /responses endpoint.""" + payload = { + "input": test_input, + "stream": False + } + + response = requests.post( + f"{SERVER_URL}/responses", + json=payload, + headers={"Content-Type": "application/json"}, + timeout=REQUEST_TIMEOUT + ) + + return { + "status_code": response.status_code, + "body": response.json() if response.headers.get("content-type", "").startswith("application/json") else response.text, + "success": response.status_code == 200 + } + + +def run_test(sample_path: Path) -> dict: + """Run the full test for a sample.""" + result = { + "sample": str(sample_path), + "name": sample_path.name, + "success": False, + "error": None, + "details": {} + } + + agent_yaml_path = sample_path / "agent.yaml" + main_py_path = sample_path / "main.py" + requirements_path = sample_path / "requirements.txt" + + # Validate required files + if not agent_yaml_path.exists(): + result["error"] = "agent.yaml not found" + return result + + if not main_py_path.exists(): + result["error"] = "main.py not found" + return result + + # Extract test input + test_input = extract_test_input(agent_yaml_path) + result["details"]["test_input"] = test_input[:100] + "..." if len(test_input) > 100 else test_input + + # Install dependencies + print(f"Installing dependencies from {requirements_path}...") + try: + subprocess.run( + [sys.executable, "-m", "pip", "install", "-r", str(requirements_path), "-q"], + check=True, + capture_output=True, + text=True + ) + except subprocess.CalledProcessError as e: + result["error"] = f"Failed to install dependencies: {e.stderr}" + return result + + # Start the server + print(f"Starting server for {sample_path.name}...") + server_process = subprocess.Popen( + [sys.executable, str(main_py_path)], + cwd=str(sample_path), + stdout=subprocess.PIPE, + stderr=subprocess.PIPE, + text=True + ) + + try: + # Wait for server to be ready + print(f"Waiting for server to start (timeout: {STARTUP_TIMEOUT}s)...") + if not wait_for_server(STARTUP_TIMEOUT): + # Check if process died + if server_process.poll() is not None: + stdout, stderr = server_process.communicate() + result["error"] = f"Server process exited unexpectedly. stderr: {stderr[:500]}" + else: + result["error"] = f"Server did not start within {STARTUP_TIMEOUT} seconds" + return result + + result["details"]["server_started"] = True + print("Server is ready. Sending test request...") + + # Send test request + try: + response = send_test_request(test_input) + result["details"]["response_status"] = response["status_code"] + + if response["success"]: + result["success"] = True + # Extract output text if available + body = response["body"] + if isinstance(body, dict): + output_text = body.get("output_text", body.get("output", str(body))) + result["details"]["response_preview"] = str(output_text)[:200] + print(f"✅ Test passed! Status: {response['status_code']}") + else: + result["error"] = f"Request failed with status {response['status_code']}: {response['body']}" + print(f"❌ Test failed! Status: {response['status_code']}") + + except requests.exceptions.Timeout: + result["error"] = f"Request timed out after {REQUEST_TIMEOUT} seconds" + except requests.exceptions.RequestException as e: + result["error"] = f"Request failed: {str(e)}" + + finally: + # Clean up server process + print("Shutting down server...") + server_process.terminate() + try: + server_process.wait(timeout=5) + except subprocess.TimeoutExpired: + server_process.kill() + server_process.wait() + + return result + + +def main(): + parser = argparse.ArgumentParser(description="Test a hosted agent sample") + parser.add_argument("sample_path", help="Path to the sample directory") + parser.add_argument("--output", "-o", help="Output file for JSON result") + args = parser.parse_args() + + sample_path = Path(args.sample_path) + if not sample_path.is_absolute(): + sample_path = Path.cwd() / sample_path + + if not sample_path.exists(): + print(f"Error: Sample path not found: {sample_path}") + sys.exit(1) + + print(f"=" * 60) + print(f"Testing sample: {sample_path.name}") + print(f"Path: {sample_path}") + print(f"=" * 60) + + result = run_test(sample_path) + + # Output result + result_json = json.dumps(result, indent=2) + print(f"\nResult:\n{result_json}") + + if args.output: + with open(args.output, "w") as f: + f.write(result_json) + + # Exit with appropriate code + sys.exit(0 if result["success"] else 1) + + +if __name__ == "__main__": + main() diff --git a/.github/workflows/test-hosted-agents.yml b/.github/workflows/test-hosted-agents.yml new file mode 100644 index 000000000..7ee5c191d --- /dev/null +++ b/.github/workflows/test-hosted-agents.yml @@ -0,0 +1,187 @@ +name: Test Hosted Agent Samples + +on: + push: + branches: + - main + paths: + - 'samples/python/hosted-agents/**' + pull_request: + branches: + - main + paths: + - 'samples/python/hosted-agents/**' + workflow_dispatch: + +permissions: + id-token: write + contents: read + +jobs: + discover: + name: Discover Samples + runs-on: ubuntu-latest + outputs: + samples: ${{ steps.discover.outputs.samples }} + sample_count: ${{ steps.discover.outputs.sample_count }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Discover hosted agent samples + id: discover + run: | + samples=$(python .github/scripts/discover_hosted_samples.py) + echo "samples=$samples" >> $GITHUB_OUTPUT + sample_count=$(echo "$samples" | python -c "import sys, json; print(len(json.load(sys.stdin)))") + echo "sample_count=$sample_count" >> $GITHUB_OUTPUT + echo "Found $sample_count samples:" + echo "$samples" | python -c "import sys, json; [print(f\" - {s['name']} ({s['framework']})\" ) for s in json.load(sys.stdin)]" + + test: + name: Test ${{ matrix.sample.name }} + needs: discover + if: needs.discover.outputs.sample_count > 0 + runs-on: ubuntu-latest + environment: hosted-agents-test + env: + # Environment variables from GitHub Environment + AZURE_AI_PROJECT_ENDPOINT: ${{ vars.AZURE_AI_PROJECT_ENDPOINT }} + AZURE_OPENAI_ENDPOINT: ${{ vars.AZURE_OPENAI_ENDPOINT }} + AZURE_AI_MODEL_DEPLOYMENT_NAME: ${{ vars.AZURE_AI_MODEL_DEPLOYMENT_NAME }} + AZURE_OPENAI_CHAT_DEPLOYMENT_NAME: ${{ vars.AZURE_OPENAI_CHAT_DEPLOYMENT_NAME }} + BING_GROUNDING_CONNECTION_ID: ${{ vars.BING_GROUNDING_CONNECTION_ID }} + AZURE_AI_PROJECT_TOOL_CONNECTION_ID: ${{ vars.AZURE_AI_PROJECT_TOOL_CONNECTION_ID }} + OPENAI_API_VERSION: ${{ vars.OPENAI_API_VERSION }} + strategy: + fail-fast: false + matrix: + sample: ${{ fromJson(needs.discover.outputs.samples) }} + steps: + - name: Checkout repository + uses: actions/checkout@v4 + + - name: Set up Python + uses: actions/setup-python@v5 + with: + python-version: '3.11' + + - name: Azure Login (OIDC) + uses: azure/login@v2 + with: + client-id: ${{ vars.AZURE_CLIENT_ID }} + tenant-id: ${{ vars.AZURE_TENANT_ID }} + subscription-id: ${{ vars.AZURE_SUBSCRIPTION_ID }} + + - name: Install test dependencies + run: | + pip install requests pyyaml + + - name: Run sample test + id: test + run: | + python .github/scripts/test_hosted_sample.py "${{ matrix.sample.path }}" -o result.json + continue-on-error: true + + - name: Upload test result + uses: actions/upload-artifact@v4 + with: + name: result-${{ matrix.sample.name }} + path: result.json + if-no-files-found: ignore + + - name: Report test status + run: | + if [ -f result.json ]; then + echo "### Test Result for ${{ matrix.sample.name }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + + success=$(python -c "import json; r=json.load(open('result.json')); print('true' if r.get('success') else 'false')") + if [ "$success" == "true" ]; then + echo "✅ **Status: PASSED**" >> $GITHUB_STEP_SUMMARY + else + echo "❌ **Status: FAILED**" >> $GITHUB_STEP_SUMMARY + error=$(python -c "import json; r=json.load(open('result.json')); print(r.get('error', 'Unknown error'))") + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Error:** $error" >> $GITHUB_STEP_SUMMARY + fi + + echo "" >> $GITHUB_STEP_SUMMARY + echo "
Full Result" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo '```json' >> $GITHUB_STEP_SUMMARY + cat result.json >> $GITHUB_STEP_SUMMARY + echo '```' >> $GITHUB_STEP_SUMMARY + echo "
" >> $GITHUB_STEP_SUMMARY + else + echo "### Test Result for ${{ matrix.sample.name }}" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "❌ **Status: FAILED** - No result file generated" >> $GITHUB_STEP_SUMMARY + fi + + - name: Fail if test failed + if: steps.test.outcome == 'failure' + run: exit 1 + + summary: + name: Test Summary + needs: [discover, test] + if: always() && needs.discover.outputs.sample_count > 0 + runs-on: ubuntu-latest + steps: + - name: Download all results + uses: actions/download-artifact@v4 + with: + path: results + pattern: result-* + + - name: Generate summary + run: | + echo "# Hosted Agent Samples Test Summary" >> $GITHUB_STEP_SUMMARY + echo "" >> $GITHUB_STEP_SUMMARY + echo "| Sample | Framework | Status |" >> $GITHUB_STEP_SUMMARY + echo "|--------|-----------|--------|" >> $GITHUB_STEP_SUMMARY + + passed=0 + failed=0 + + for dir in results/result-*; do + if [ -d "$dir" ] && [ -f "$dir/result.json" ]; then + name=$(python -c "import json; print(json.load(open('$dir/result.json')).get('name', 'unknown'))") + success=$(python -c "import json; r=json.load(open('$dir/result.json')); print('true' if r.get('success') else 'false')") + + # Try to determine framework from path + sample_path=$(python -c "import json; print(json.load(open('$dir/result.json')).get('sample', ''))") + if [[ "$sample_path" == *"agent-framework"* ]]; then + framework="agent-framework" + elif [[ "$sample_path" == *"langgraph"* ]]; then + framework="langgraph" + elif [[ "$sample_path" == *"custom"* ]]; then + framework="custom" + else + framework="unknown" + fi + + if [ "$success" == "true" ]; then + echo "| $name | $framework | ✅ Passed |" >> $GITHUB_STEP_SUMMARY + passed=$((passed + 1)) + else + error=$(python -c "import json; r=json.load(open('$dir/result.json')); print(r.get('error', 'Unknown')[:50])") + echo "| $name | $framework | ❌ Failed |" >> $GITHUB_STEP_SUMMARY + failed=$((failed + 1)) + fi + fi + done + + echo "" >> $GITHUB_STEP_SUMMARY + echo "**Total:** $passed passed, $failed failed" >> $GITHUB_STEP_SUMMARY + + if [ $failed -gt 0 ]; then + echo "" >> $GITHUB_STEP_SUMMARY + echo "⚠️ Some tests failed. Check individual job logs for details." >> $GITHUB_STEP_SUMMARY + fi diff --git a/samples/python/hosted-agents/agent-framework/agent-with-foundry-tools/requirements.txt b/samples/python/hosted-agents/agent-framework/agent-with-foundry-tools/requirements.txt index bb03ffea3..cf0d93148 100644 --- a/samples/python/hosted-agents/agent-framework/agent-with-foundry-tools/requirements.txt +++ b/samples/python/hosted-agents/agent-framework/agent-with-foundry-tools/requirements.txt @@ -1 +1 @@ -azure_ai_agentserver_agentframework-1.0.0b9 \ No newline at end of file +azure-ai-agentserver-agentframework==1.0.0b10 \ No newline at end of file diff --git a/samples/python/hosted-agents/agent-framework/agent-with-text-search-rag/requirements.txt b/samples/python/hosted-agents/agent-framework/agent-with-text-search-rag/requirements.txt index adac06de2..cf0d93148 100644 --- a/samples/python/hosted-agents/agent-framework/agent-with-text-search-rag/requirements.txt +++ b/samples/python/hosted-agents/agent-framework/agent-with-text-search-rag/requirements.txt @@ -1 +1 @@ -azure-ai-agentserver-agentframework==1.0.0b9 \ No newline at end of file +azure-ai-agentserver-agentframework==1.0.0b10 \ No newline at end of file diff --git a/samples/python/hosted-agents/agent-framework/agents-in-workflow/requirements.txt b/samples/python/hosted-agents/agent-framework/agents-in-workflow/requirements.txt index adac06de2..cf0d93148 100644 --- a/samples/python/hosted-agents/agent-framework/agents-in-workflow/requirements.txt +++ b/samples/python/hosted-agents/agent-framework/agents-in-workflow/requirements.txt @@ -1 +1 @@ -azure-ai-agentserver-agentframework==1.0.0b9 \ No newline at end of file +azure-ai-agentserver-agentframework==1.0.0b10 \ No newline at end of file diff --git a/samples/python/hosted-agents/agent-framework/echo-agent/requirements.txt b/samples/python/hosted-agents/agent-framework/echo-agent/requirements.txt index b33c027aa..d4a17f84c 100644 --- a/samples/python/hosted-agents/agent-framework/echo-agent/requirements.txt +++ b/samples/python/hosted-agents/agent-framework/echo-agent/requirements.txt @@ -1,4 +1,4 @@ -azure-ai-agentserver-agentframework==1.0.0b9 +azure-ai-agentserver-agentframework==1.0.0b10 pytest==8.4.2 python-dotenv==1.1.1 azure-monitor-opentelemetry==1.8.1 diff --git a/samples/python/hosted-agents/agent-framework/web-search-agent/README.md b/samples/python/hosted-agents/agent-framework/web-search-agent/README.md index 110e5eacc..bc658df69 100644 --- a/samples/python/hosted-agents/agent-framework/web-search-agent/README.md +++ b/samples/python/hosted-agents/agent-framework/web-search-agent/README.md @@ -1,3 +1,90 @@ +**IMPORTANT!** All samples and other resources made available in this GitHub repository ("samples") are designed to assist in accelerating development of agents, solutions, and agent workflows for various scenarios. Review all provided resources and carefully test output behavior in the context of your use case. AI responses may be inaccurate and AI actions should be monitored with human oversight. Learn more in the transparency documents for [Agent Service](https://learn.microsoft.com/en-us/azure/ai-foundry/responsible-ai/agents/transparency-note) and [Agent Framework](https://github.com/microsoft/agent-framework/blob/main/TRANSPARENCY_FAQ.md). + +Agents, solutions, or other output you create may be subject to legal and regulatory requirements, may require licenses, or may not be suitable for all industries, scenarios, or use cases. By using any sample, you are acknowledging that any output created using those samples are solely your responsibility, and that you will comply with all applicable laws, regulations, and relevant safety standards, terms of service, and codes of conduct. + +Third-party samples contained in this folder are subject to their own designated terms, and they have not been tested or verified by Microsoft or its affiliates. + +Microsoft has no responsibility to you or others with respect to any of these samples or any resulting output. + +# What this sample demonstrates + +This sample demonstrates how to build a web search agent using Bing Grounding, hosted using +[Azure AI AgentServer SDK](https://pypi.org/project/azure-ai-agentserver-agentframework/) and +deploy it to Microsoft Foundry using the Azure Developer CLI [ai agent](https://aka.ms/azdaiagent/docs) extension. + +## How It Works + +### Web Search Agent + +The agent uses Bing Grounding to search the web for current information and provide accurate, well-sourced answers. This demonstrates: + +- How to integrate Bing Grounding as a tool in an AI agent +- How to use the `HostedWebSearchTool` from the Agent Framework + +### Agent Hosting + +The agent is hosted using the [Azure AI AgentServer SDK](https://pypi.org/project/azure-ai-agentserver-agentframework/), +which provisions a REST API endpoint compatible with the OpenAI Responses protocol. This allows interaction with the agent using OpenAI Responses compatible clients. + +### Agent Deployment + +The hosted agent can be seamlessly deployed to Microsoft Foundry using the Azure Developer CLI [ai agent](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/concepts/hosted-agents?view=foundry&tabs=cli#create-a-hosted-agent) extension. +The extension builds a container image into Azure Container Registry (ACR), and creates a hosted agent version and deployment on Microsoft Foundry. + +## Running the Agent Locally + +### Prerequisites + +Before running this sample, ensure you have: + +1. An Azure AI Foundry project configured +2. A deployment of a chat model (e.g., `gpt-4.1-mini`) +3. A Bing Grounding connection in your project +4. Azure CLI installed and authenticated (`az login`) +5. Python 3.10+ installed + +### Environment Variables + +Create a `.env` file with the following environment variables: + +> **Note:** The `.env` file is for local development only. When deploying to Azure AI Foundry, remove the `.env` file and configure environment variables in `agent.yaml` instead. + +```bash +AZURE_AI_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/ +AZURE_AI_MODEL_DEPLOYMENT_NAME= # e.g., gpt-4.1-mini +BING_GROUNDING_CONNECTION_ID=/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts//projects//connections/ +``` + +### Installing Dependencies + +Install the required Python dependencies using pip: + +```bash +pip install -r requirements.txt +``` + +### Running the Sample + +To run the agent, execute the following command in your terminal: + +```bash +python main.py +``` + +This will start the hosted agent locally on `http://localhost:8088/`. + +### Interacting with the Agent + +```bash +curl -X POST http://localhost:8088/responses \ + -H "Content-Type: application/json" \ + -d '{"input": "What is the latest news in AI?"}' | jq . +``` + +### Deploying the Agent to Microsoft Foundry + +To deploy your agent to Microsoft Foundry, follow the comprehensive deployment guide at https://aka.ms/azdaiagent/docs + ## Troubleshooting ### Images built on Apple Silicon or other ARM64 machines do not work on our service @@ -14,4 +101,4 @@ Use this command to build the image locally: docker build --platform=linux/amd64 -t image . ``` -This forces the image to be built for the required `amd64` architecture. +This forces the image to be built for the required `amd64` architecture. \ No newline at end of file diff --git a/samples/python/hosted-agents/agent-framework/web-search-agent/requirements.txt b/samples/python/hosted-agents/agent-framework/web-search-agent/requirements.txt index b33c027aa..d4a17f84c 100644 --- a/samples/python/hosted-agents/agent-framework/web-search-agent/requirements.txt +++ b/samples/python/hosted-agents/agent-framework/web-search-agent/requirements.txt @@ -1,4 +1,4 @@ -azure-ai-agentserver-agentframework==1.0.0b9 +azure-ai-agentserver-agentframework==1.0.0b10 pytest==8.4.2 python-dotenv==1.1.1 azure-monitor-opentelemetry==1.8.1 diff --git a/samples/python/hosted-agents/custom/system-utility-agent/requirements.txt b/samples/python/hosted-agents/custom/system-utility-agent/requirements.txt index 591d558e0..e54f2ac67 100644 --- a/samples/python/hosted-agents/custom/system-utility-agent/requirements.txt +++ b/samples/python/hosted-agents/custom/system-utility-agent/requirements.txt @@ -1,6 +1,6 @@ azure-identity==1.25.1 azure-ai-projects==2.0.0b2 -azure-ai-agentserver-core==1.0.0b9 +azure-ai-agentserver-core==1.0.0b10 openai==2.14.0 python-dotenv==1.0.0 psutil==5.9.4 diff --git a/samples/python/hosted-agents/langgraph/calculator-agent/requirements.txt b/samples/python/hosted-agents/langgraph/calculator-agent/requirements.txt index a95dd7516..01e3547ab 100644 --- a/samples/python/hosted-agents/langgraph/calculator-agent/requirements.txt +++ b/samples/python/hosted-agents/langgraph/calculator-agent/requirements.txt @@ -1 +1 @@ -azure-ai-agentserver-langgraph==1.0.0b9 +azure-ai-agentserver-langgraph==1.0.0b10 diff --git a/samples/python/hosted-agents/langgraph/react-agent-with-foundry-tools/requirements.txt b/samples/python/hosted-agents/langgraph/react-agent-with-foundry-tools/requirements.txt index dd1df1627..446acb1d6 100644 --- a/samples/python/hosted-agents/langgraph/react-agent-with-foundry-tools/requirements.txt +++ b/samples/python/hosted-agents/langgraph/react-agent-with-foundry-tools/requirements.txt @@ -1 +1 @@ -azure-ai-agentserver-langgraph==1.0.0b9 \ No newline at end of file +azure-ai-agentserver-langgraph==1.0.0b10 \ No newline at end of file