diff --git a/python/samples/01-get-started/01_hello_agent.py b/python/samples/01-get-started/01_hello_agent.py index 1ed709f4a79..c8e1cc72297 100644 --- a/python/samples/01-get-started/01_hello_agent.py +++ b/python/samples/01-get-started/01_hello_agent.py @@ -1,7 +1,9 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os +from dotenv import load_dotenv from agent_framework import Agent from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential @@ -15,12 +17,13 @@ There are XML tags in all of the get started samples, those are used to display the same code in the docs repo. """ +load_dotenv() # Load environment variables from .env file async def main() -> None: # client = FoundryChatClient( - project_endpoint="https://your-project.services.ai.azure.com", - model="gpt-4o", + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["FOUNDRY_MODEL"], credential=AzureCliCredential(), ) diff --git a/python/samples/01-get-started/02_add_tools.py b/python/samples/01-get-started/02_add_tools.py index 7b558abeaec..5611fec9b0b 100644 --- a/python/samples/01-get-started/02_add_tools.py +++ b/python/samples/01-get-started/02_add_tools.py @@ -1,9 +1,12 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from random import randint from typing import Annotated +from dotenv import load_dotenv + from agent_framework import Agent, tool from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential @@ -16,6 +19,8 @@ and wire it into an agent so the model can call it. """ +load_dotenv() # Load environment variables from .env file + # # NOTE: approval_mode="never_require" is for sample brevity. @@ -34,8 +39,8 @@ def get_weather( async def main() -> None: client = FoundryChatClient( - project_endpoint="https://your-project.services.ai.azure.com", - model="gpt-4o", + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["FOUNDRY_MODEL"], credential=AzureCliCredential(), ) diff --git a/python/samples/01-get-started/03_multi_turn.py b/python/samples/01-get-started/03_multi_turn.py index 9e6984dada9..a3e41b9f94b 100644 --- a/python/samples/01-get-started/03_multi_turn.py +++ b/python/samples/01-get-started/03_multi_turn.py @@ -1,6 +1,9 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os + +from dotenv import load_dotenv from agent_framework import Agent from agent_framework.foundry import FoundryChatClient @@ -13,12 +16,14 @@ by reusing the same session object. """ +load_dotenv() # Load environment variables from .env file + async def main() -> None: # client = FoundryChatClient( - project_endpoint="https://your-project.services.ai.azure.com", - model="gpt-4o", + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["FOUNDRY_MODEL"], credential=AzureCliCredential(), ) diff --git a/python/samples/01-get-started/04_memory.py b/python/samples/01-get-started/04_memory.py index 7e0b1e2d5f4..3c0a749dd8b 100644 --- a/python/samples/01-get-started/04_memory.py +++ b/python/samples/01-get-started/04_memory.py @@ -1,8 +1,11 @@ # Copyright (c) Microsoft. All rights reserved. import asyncio +import os from typing import Any +from dotenv import load_dotenv + from agent_framework import Agent, AgentSession, ContextProvider, SessionContext from agent_framework.foundry import FoundryChatClient from azure.identity import AzureCliCredential @@ -15,6 +18,8 @@ responses — the name persists across turns via the session. """ +load_dotenv() # Load environment variables from .env file + # class UserMemoryProvider(ContextProvider): @@ -67,8 +72,8 @@ async def after_run( async def main() -> None: # client = FoundryChatClient( - project_endpoint="https://your-project.services.ai.azure.com", - model="gpt-4o", + project_endpoint=os.environ["FOUNDRY_PROJECT_ENDPOINT"], + model=os.environ["FOUNDRY_MODEL"], credential=AzureCliCredential(), ) diff --git a/python/samples/01-get-started/05_functional_workflow_with_agents.py b/python/samples/01-get-started/05_functional_workflow_with_agents.py index e77c1bc7431..1f30329254d 100644 --- a/python/samples/01-get-started/05_functional_workflow_with_agents.py +++ b/python/samples/01-get-started/05_functional_workflow_with_agents.py @@ -47,8 +47,8 @@ async def poem_workflow(topic: str) -> str: async def main() -> None: - workflow_instance = poem_workflow.build() - result = await workflow_instance.run("a cat learning to code") + poem = poem_workflow.build() + result = await poem.run("a cat learning to code") print(result.get_outputs()[0]) diff --git a/python/samples/01-get-started/06_functional_workflow_basics.py b/python/samples/01-get-started/06_functional_workflow_basics.py index ec6fd79f538..0262602df88 100644 --- a/python/samples/01-get-started/06_functional_workflow_basics.py +++ b/python/samples/01-get-started/06_functional_workflow_basics.py @@ -43,8 +43,8 @@ async def text_workflow(text: str) -> str: async def main() -> None: # - workflow_instance = text_workflow.build() - result = await workflow_instance.run("hello world") + pipeline = text_workflow.build() + result = await pipeline.run("hello world") print(f"Output: {result.get_outputs()}") print(f"Final state: {result.get_final_state()}") # diff --git a/python/samples/01-get-started/README.md b/python/samples/01-get-started/README.md index c4180b85fab..a013b711e65 100644 --- a/python/samples/01-get-started/README.md +++ b/python/samples/01-get-started/README.md @@ -11,13 +11,75 @@ pip install agent-framework-foundry Sample 08 additionally requires `agent-framework-azurefunctions --pre`. -Set the required environment variables: +### 1. Configure environment variables + +The samples read connection settings from environment variables (or a local +`.env` file loaded via `python-dotenv`). The project endpoint has the shape +`https://.services.ai.azure.com/api/projects/` and must +point at a Foundry **project**, not the account root. + +```bash +export FOUNDRY_PROJECT_ENDPOINT="https://.services.ai.azure.com/api/projects/" +export FOUNDRY_MODEL="" # required; must match a deployment on the account +``` + +Or drop the same keys into `python/samples/.env`: + +```dotenv +FOUNDRY_PROJECT_ENDPOINT=https://.services.ai.azure.com/api/projects/ +FOUNDRY_MODEL= +``` + +> `load_dotenv()` walks upward and loads the **first** `.env` it finds, so a +> `python/samples/.env` next to the samples shadows a `python/.env` higher up. +> If a value looks wrong at runtime, edit the nearest `.env` on the path. +> Also note that already-set shell env vars take precedence over `.env` unless +> you pass `load_dotenv(override=True)`. + +### 2. Sign in and grant data-plane access + +The samples authenticate with `AzureCliCredential`, so first run: ```bash -export FOUNDRY_PROJECT_ENDPOINT="https://your-project-endpoint" -export FOUNDRY_MODEL="gpt-4o" # optional, defaults to gpt-4o +az login ``` +Calling the Foundry project's inference endpoints (Responses, Chat +Completions, etc. under `/api/projects//openai/v1/...`) requires a +data-plane role on the AI Services account (or the project sub-resource). +The Responses path is served by the **AIServices** RBAC namespace, so the +`Azure AI Developer` role — which does not include +`Microsoft.CognitiveServices/accounts/AIServices/responses/*` — is **not** +sufficient on its own. + +Assign one of the following to your user/service principal on the account +`Microsoft.CognitiveServices/accounts/` (or the child +`.../projects/` scope): + +| Role | Grants | Notes | +|------|--------|-------| +| `Foundry Project Runtime User` | `Microsoft.CognitiveServices/accounts/AIServices/responses/*` | Minimal role for the Responses API. | +| `Foundry User` | `Microsoft.CognitiveServices/*` | Broader; covers all Foundry data-plane calls. | + +Example (account scope, broad role): + +```bash +az role assignment create \ + --assignee "" \ + --role "Foundry User" \ + --scope "/subscriptions//resourceGroups//providers/Microsoft.CognitiveServices/accounts/" +``` + +Role propagation can take up to a few minutes. Symptoms of missing/insufficient +roles: + +- `401 Unauthorized` — token audience wrong or no role at all. +- `403 PermissionDenied` on the project URL — a role is assigned but its + `dataActions` don't cover `AIServices/responses/*`. +- `404 DeploymentNotFound` — auth is OK but `FOUNDRY_MODEL` doesn't match any + deployment on the account. Verify with + `az cognitiveservices account deployment list -g -n `. + ## Samples | # | File | What you'll learn |