mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-07 02:59:05 +00:00
[Fixes] A2a Gateway - ensure azure foundry agents work (#17943)
* add agents v2 fixes azure * fix auth * get_azure_ad_token fix * docs foundry
This commit is contained in:
parent
232bb33fec
commit
14eed8aff7
6 changed files with 234 additions and 91 deletions
|
|
@ -9,7 +9,47 @@ Call Azure AI Foundry Agents in the OpenAI Request/Response format.
|
|||
|----------|---------|
|
||||
| Description | Azure AI Foundry Agents provides hosted agent runtimes that can execute agentic workflows with foundation models, tools, and code interpreters. |
|
||||
| Provider Route on LiteLLM | `azure_ai/agents/{AGENT_ID}` |
|
||||
| Provider Doc | [Azure AI Foundry Agents ↗](https://learn.microsoft.com/en-us/rest/api/aifoundry/aiagents/create-thread-and-run/create-thread-and-run) |
|
||||
| Provider Doc | [Azure AI Foundry Agents ↗](https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart) |
|
||||
|
||||
## Authentication
|
||||
|
||||
Azure AI Foundry Agents require **Azure AD authentication** (not API keys). You can authenticate using:
|
||||
|
||||
### Option 1: Service Principal (Recommended for Production)
|
||||
|
||||
Set these environment variables:
|
||||
|
||||
```bash
|
||||
export AZURE_TENANT_ID="your-tenant-id"
|
||||
export AZURE_CLIENT_ID="your-client-id"
|
||||
export AZURE_CLIENT_SECRET="your-client-secret"
|
||||
```
|
||||
|
||||
LiteLLM will automatically obtain an Azure AD token using these credentials.
|
||||
|
||||
### Option 2: Azure AD Token (Manual)
|
||||
|
||||
Pass a token directly via `api_key`:
|
||||
|
||||
```bash
|
||||
# Get token via Azure CLI
|
||||
az account get-access-token --resource "https://ai.azure.com" --query accessToken -o tsv
|
||||
```
|
||||
|
||||
### Required Azure Role
|
||||
|
||||
Your Service Principal or user must have the **Azure AI Developer** or **Azure AI User** role on your Azure AI Foundry project.
|
||||
|
||||
To assign via Azure CLI:
|
||||
```bash
|
||||
az role assignment create \
|
||||
--assignee-object-id "<service-principal-object-id>" \
|
||||
--assignee-principal-type "ServicePrincipal" \
|
||||
--role "Azure AI Developer" \
|
||||
--scope "/subscriptions/<sub>/resourceGroups/<rg>/providers/Microsoft.CognitiveServices/accounts/<resource>"
|
||||
```
|
||||
|
||||
Or add via **Azure AI Foundry Portal** → Your Project → **Project users** → **+ New user**.
|
||||
|
||||
## Quick Start
|
||||
|
||||
|
|
@ -34,6 +74,7 @@ You can find the Agent ID in your Azure AI Foundry portal under Agents.
|
|||
import litellm
|
||||
|
||||
# Make a completion request to your Azure AI Foundry Agent
|
||||
# Uses AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET env vars for auth
|
||||
response = litellm.completion(
|
||||
model="azure_ai/agents/asst_abc123",
|
||||
messages=[
|
||||
|
|
@ -42,8 +83,7 @@ response = litellm.completion(
|
|||
"content": "Explain machine learning in simple terms"
|
||||
}
|
||||
],
|
||||
api_base="https://your-project.services.ai.azure.com",
|
||||
api_key="your-api-key",
|
||||
api_base="https://your-resource.services.ai.azure.com/api/projects/your-project",
|
||||
)
|
||||
|
||||
print(response.choices[0].message.content)
|
||||
|
|
@ -62,8 +102,7 @@ response = await litellm.acompletion(
|
|||
"content": "What are the key principles of software architecture?"
|
||||
}
|
||||
],
|
||||
api_base="https://your-project.services.ai.azure.com",
|
||||
api_key="your-api-key",
|
||||
api_base="https://your-resource.services.ai.azure.com/api/projects/your-project",
|
||||
stream=True,
|
||||
)
|
||||
|
||||
|
|
@ -84,14 +123,18 @@ model_list:
|
|||
- model_name: azure-agent-1
|
||||
litellm_params:
|
||||
model: azure_ai/agents/asst_abc123
|
||||
api_base: https://your-project.services.ai.azure.com
|
||||
api_key: os.environ/AZURE_API_KEY
|
||||
api_base: https://your-resource.services.ai.azure.com/api/projects/your-project
|
||||
# Service Principal auth (recommended)
|
||||
tenant_id: os.environ/AZURE_TENANT_ID
|
||||
client_id: os.environ/AZURE_CLIENT_ID
|
||||
client_secret: os.environ/AZURE_CLIENT_SECRET
|
||||
|
||||
- model_name: azure-agent-math-tutor
|
||||
litellm_params:
|
||||
model: azure_ai/agents/asst_def456
|
||||
api_base: https://your-project.services.ai.azure.com
|
||||
api_key: os.environ/AZURE_API_KEY
|
||||
api_base: https://your-resource.services.ai.azure.com/api/projects/your-project
|
||||
# Or pass Azure AD token directly
|
||||
api_key: os.environ/AZURE_AD_TOKEN
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
|
|
@ -196,16 +239,16 @@ for chunk in stream:
|
|||
|
||||
## Environment Variables
|
||||
|
||||
You can set the following environment variables to configure Azure AI Foundry Agents:
|
||||
|
||||
| Variable | Description |
|
||||
|----------|-------------|
|
||||
| `AZURE_API_BASE` | The Azure AI Foundry project endpoint (e.g., `https://your-project.services.ai.azure.com`) |
|
||||
| `AZURE_API_KEY` | Your Azure AI Foundry API key |
|
||||
| `AZURE_TENANT_ID` | Azure AD tenant ID for Service Principal auth |
|
||||
| `AZURE_CLIENT_ID` | Application (client) ID of your Service Principal |
|
||||
| `AZURE_CLIENT_SECRET` | Client secret for your Service Principal |
|
||||
|
||||
```bash
|
||||
export AZURE_API_BASE="https://your-project.services.ai.azure.com"
|
||||
export AZURE_API_KEY="your-api-key"
|
||||
export AZURE_TENANT_ID="your-tenant-id"
|
||||
export AZURE_CLIENT_ID="your-client-id"
|
||||
export AZURE_CLIENT_SECRET="your-client-secret"
|
||||
```
|
||||
|
||||
## Conversation Continuity (Thread Management)
|
||||
|
|
@ -219,8 +262,7 @@ import litellm
|
|||
response1 = await litellm.acompletion(
|
||||
model="azure_ai/agents/asst_abc123",
|
||||
messages=[{"role": "user", "content": "My name is Alice"}],
|
||||
api_base="https://your-project.services.ai.azure.com",
|
||||
api_key="your-api-key",
|
||||
api_base="https://your-resource.services.ai.azure.com/api/projects/your-project",
|
||||
)
|
||||
|
||||
# Get the thread_id from the response
|
||||
|
|
@ -230,8 +272,7 @@ thread_id = response1._hidden_params.get("thread_id")
|
|||
response2 = await litellm.acompletion(
|
||||
model="azure_ai/agents/asst_abc123",
|
||||
messages=[{"role": "user", "content": "What's my name?"}],
|
||||
api_base="https://your-project.services.ai.azure.com",
|
||||
api_key="your-api-key",
|
||||
api_base="https://your-resource.services.ai.azure.com/api/projects/your-project",
|
||||
thread_id=thread_id, # Pass the thread_id to continue conversation
|
||||
)
|
||||
|
||||
|
|
@ -256,8 +297,7 @@ response = litellm.completion(
|
|||
"content": "Analyze this data and provide insights",
|
||||
}
|
||||
],
|
||||
api_base="https://your-project.services.ai.azure.com",
|
||||
api_key="your-api-key",
|
||||
api_base="https://your-resource.services.ai.azure.com/api/projects/your-project",
|
||||
thread_id="thread_abc123", # Optional: Continue existing conversation
|
||||
instructions="Be concise and focus on key insights", # Optional: Override agent instructions
|
||||
)
|
||||
|
|
@ -271,8 +311,10 @@ model_list:
|
|||
- model_name: azure-agent-analyst
|
||||
litellm_params:
|
||||
model: azure_ai/agents/asst_abc123
|
||||
api_base: https://your-project.services.ai.azure.com
|
||||
api_key: os.environ/AZURE_API_KEY
|
||||
api_base: https://your-resource.services.ai.azure.com/api/projects/your-project
|
||||
tenant_id: os.environ/AZURE_TENANT_ID
|
||||
client_id: os.environ/AZURE_CLIENT_ID
|
||||
client_secret: os.environ/AZURE_CLIENT_SECRET
|
||||
instructions: "Be concise and focus on key insights"
|
||||
```
|
||||
|
||||
|
|
|
|||
|
|
@ -294,20 +294,18 @@ def get_azure_ad_token(
|
|||
Azure AD token as string if successful, None otherwise
|
||||
"""
|
||||
# Extract parameters
|
||||
# Use `or` instead of default parameter to handle cases where key exists but value is None
|
||||
azure_ad_token_provider = litellm_params.get("azure_ad_token_provider")
|
||||
azure_ad_token = litellm_params.get("azure_ad_token", None) or get_secret_str(
|
||||
azure_ad_token = litellm_params.get("azure_ad_token") or get_secret_str(
|
||||
"AZURE_AD_TOKEN"
|
||||
)
|
||||
tenant_id = litellm_params.get("tenant_id", os.getenv("AZURE_TENANT_ID"))
|
||||
client_id = litellm_params.get("client_id", os.getenv("AZURE_CLIENT_ID"))
|
||||
client_secret = litellm_params.get(
|
||||
"client_secret", os.getenv("AZURE_CLIENT_SECRET")
|
||||
)
|
||||
azure_username = litellm_params.get("azure_username", os.getenv("AZURE_USERNAME"))
|
||||
azure_password = litellm_params.get("azure_password", os.getenv("AZURE_PASSWORD"))
|
||||
scope = litellm_params.get(
|
||||
"azure_scope",
|
||||
os.getenv("AZURE_SCOPE", "https://cognitiveservices.azure.com/.default"),
|
||||
tenant_id = litellm_params.get("tenant_id") or os.getenv("AZURE_TENANT_ID")
|
||||
client_id = litellm_params.get("client_id") or os.getenv("AZURE_CLIENT_ID")
|
||||
client_secret = litellm_params.get("client_secret") or os.getenv("AZURE_CLIENT_SECRET")
|
||||
azure_username = litellm_params.get("azure_username") or os.getenv("AZURE_USERNAME")
|
||||
azure_password = litellm_params.get("azure_password") or os.getenv("AZURE_PASSWORD")
|
||||
scope = litellm_params.get("azure_scope") or os.getenv(
|
||||
"AZURE_SCOPE", "https://cognitiveservices.azure.com/.default"
|
||||
)
|
||||
if scope is None:
|
||||
scope = "https://cognitiveservices.azure.com/.default"
|
||||
|
|
|
|||
|
|
@ -1,5 +1,5 @@
|
|||
"""
|
||||
Handler for Azure AI Agent Service API.
|
||||
Handler for Azure Foundry Agent Service API.
|
||||
|
||||
This handler executes the multi-step agent flow:
|
||||
1. Create thread (or use existing)
|
||||
|
|
@ -8,8 +8,14 @@ This handler executes the multi-step agent flow:
|
|||
4. Retrieve the assistant's response messages
|
||||
|
||||
Model format: azure_ai/agents/<agent_id>
|
||||
API Base format: https://<AIFoundryResourceName>.services.ai.azure.com/api/projects/<ProjectName>
|
||||
|
||||
Authentication: Uses Azure AD Bearer tokens (not API keys)
|
||||
Get token via: az account get-access-token --resource 'https://ai.azure.com'
|
||||
|
||||
Supports both polling-based and native streaming (SSE) modes.
|
||||
|
||||
See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
"""
|
||||
|
||||
import asyncio
|
||||
|
|
@ -60,24 +66,27 @@ class AzureAIAgentsHandler:
|
|||
# -------------------------------------------------------------------------
|
||||
# URL Builders
|
||||
# -------------------------------------------------------------------------
|
||||
# Azure Foundry Agents API uses /assistants, /threads, etc. directly
|
||||
# See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
# -------------------------------------------------------------------------
|
||||
def _build_thread_url(self, api_base: str, api_version: str) -> str:
|
||||
return f"{api_base}/openai/threads?api-version={api_version}"
|
||||
return f"{api_base}/threads?api-version={api_version}"
|
||||
|
||||
def _build_messages_url(self, api_base: str, thread_id: str, api_version: str) -> str:
|
||||
return f"{api_base}/openai/threads/{thread_id}/messages?api-version={api_version}"
|
||||
return f"{api_base}/threads/{thread_id}/messages?api-version={api_version}"
|
||||
|
||||
def _build_runs_url(self, api_base: str, thread_id: str, api_version: str) -> str:
|
||||
return f"{api_base}/openai/threads/{thread_id}/runs?api-version={api_version}"
|
||||
return f"{api_base}/threads/{thread_id}/runs?api-version={api_version}"
|
||||
|
||||
def _build_run_status_url(self, api_base: str, thread_id: str, run_id: str, api_version: str) -> str:
|
||||
return f"{api_base}/openai/threads/{thread_id}/runs/{run_id}?api-version={api_version}"
|
||||
return f"{api_base}/threads/{thread_id}/runs/{run_id}?api-version={api_version}"
|
||||
|
||||
def _build_list_messages_url(self, api_base: str, thread_id: str, api_version: str) -> str:
|
||||
return f"{api_base}/openai/threads/{thread_id}/messages?api-version={api_version}"
|
||||
return f"{api_base}/threads/{thread_id}/messages?api-version={api_version}"
|
||||
|
||||
def _build_create_thread_and_run_url(self, api_base: str, api_version: str) -> str:
|
||||
"""URL for the create-thread-and-run endpoint (supports streaming)."""
|
||||
return f"{api_base}/openai/threads/runs?api-version={api_version}"
|
||||
return f"{api_base}/threads/runs?api-version={api_version}"
|
||||
|
||||
# -------------------------------------------------------------------------
|
||||
# Response Helpers
|
||||
|
|
@ -140,12 +149,21 @@ class AzureAIAgentsHandler:
|
|||
optional_params: dict,
|
||||
headers: Optional[dict],
|
||||
) -> tuple:
|
||||
"""Prepare common parameters for completion."""
|
||||
"""Prepare common parameters for completion.
|
||||
|
||||
Azure Foundry Agents API uses Bearer token authentication:
|
||||
- Authorization: Bearer <token> (Azure AD token from 'az account get-access-token --resource https://ai.azure.com')
|
||||
|
||||
See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
"""
|
||||
if headers is None:
|
||||
headers = {}
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
# Azure Foundry Agents uses Bearer token authentication
|
||||
# The api_key here is expected to be an Azure AD token
|
||||
if api_key:
|
||||
headers["api-key"] = api_key
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
api_version = optional_params.get("api_version", self.config.DEFAULT_API_VERSION)
|
||||
agent_id = self.config._get_agent_id(model, optional_params)
|
||||
|
|
|
|||
|
|
@ -1,17 +1,24 @@
|
|||
"""
|
||||
Transformation for Azure AI Agent Service API.
|
||||
Transformation for Azure Foundry Agent Service API.
|
||||
|
||||
Azure AI Agent Service provides an Assistants-like API for running agents.
|
||||
Azure Foundry Agent Service provides an Assistants-like API for running agents.
|
||||
This follows the OpenAI Assistants pattern: create thread -> add messages -> create/poll run.
|
||||
|
||||
Model format: azure_ai/agents/<agent_id>
|
||||
|
||||
API Base format: https://<AIFoundryResourceName>.services.ai.azure.com/api/projects/<ProjectName>
|
||||
|
||||
Authentication: Uses Azure AD Bearer tokens (not API keys)
|
||||
Get token via: az account get-access-token --resource 'https://ai.azure.com'
|
||||
|
||||
The API uses these endpoints:
|
||||
- POST /openai/threads - Create a thread
|
||||
- POST /openai/threads/{thread_id}/messages - Add message to thread
|
||||
- POST /openai/threads/{thread_id}/runs - Create a run
|
||||
- GET /openai/threads/{thread_id}/runs/{run_id} - Poll run status
|
||||
- GET /openai/threads/{thread_id}/messages - List messages in thread
|
||||
- POST /threads - Create a thread
|
||||
- POST /threads/{thread_id}/messages - Add message to thread
|
||||
- POST /threads/{thread_id}/runs - Create a run
|
||||
- GET /threads/{thread_id}/runs/{run_id} - Poll run status
|
||||
- GET /threads/{thread_id}/messages - List messages in thread
|
||||
|
||||
See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
"""
|
||||
|
||||
from typing import TYPE_CHECKING, Any, Dict, List, Optional, Tuple, Union
|
||||
|
|
@ -59,8 +66,10 @@ class AzureAIAgentsConfig(BaseConfig):
|
|||
4. Retrieve the assistant's response messages
|
||||
"""
|
||||
|
||||
# Default API version for Azure AI Agent Service
|
||||
DEFAULT_API_VERSION = "2024-07-01-preview"
|
||||
# Default API version for Azure Foundry Agent Service
|
||||
# GA version: 2025-05-01, Preview: 2025-05-15-preview
|
||||
# See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
DEFAULT_API_VERSION = "2025-05-01"
|
||||
|
||||
# Polling configuration
|
||||
MAX_POLL_ATTEMPTS = 60
|
||||
|
|
@ -236,13 +245,19 @@ class AzureAIAgentsConfig(BaseConfig):
|
|||
api_base: Optional[str] = None,
|
||||
) -> dict:
|
||||
"""
|
||||
Validate and set up environment for Azure Agents requests.
|
||||
Validate and set up environment for Azure Foundry Agents requests.
|
||||
|
||||
Azure Foundry Agents uses Bearer token authentication with Azure AD tokens.
|
||||
Get token via: az account get-access-token --resource 'https://ai.azure.com'
|
||||
|
||||
See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
"""
|
||||
headers["Content-Type"] = "application/json"
|
||||
|
||||
# Add API key if provided
|
||||
# Azure Foundry Agents uses Bearer token authentication
|
||||
# The api_key here is expected to be an Azure AD token
|
||||
if api_key:
|
||||
headers["api-key"] = api_key
|
||||
headers["Authorization"] = f"Bearer {api_key}"
|
||||
|
||||
return headers
|
||||
|
||||
|
|
@ -310,15 +325,38 @@ class AzureAIAgentsConfig(BaseConfig):
|
|||
headers: Optional[dict] = None,
|
||||
) -> Any:
|
||||
"""
|
||||
Dispatch method for Azure AI Agents completion.
|
||||
Dispatch method for Azure Foundry Agents completion.
|
||||
|
||||
Routes to sync or async completion based on acompletion flag.
|
||||
Supports native streaming via SSE when stream=True and acompletion=True.
|
||||
|
||||
Authentication: Uses Azure AD Bearer tokens.
|
||||
- Pass api_key directly as an Azure AD token
|
||||
- Or set up Azure AD credentials via environment variables for automatic token retrieval:
|
||||
- AZURE_TENANT_ID, AZURE_CLIENT_ID, AZURE_CLIENT_SECRET (Service Principal)
|
||||
|
||||
See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
"""
|
||||
from litellm.llms.azure.common_utils import get_azure_ad_token
|
||||
from litellm.llms.azure_ai.agents.handler import azure_ai_agents_handler
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
# If no api_key is provided, try to get Azure AD token
|
||||
if api_key is None:
|
||||
raise ValueError("api_key is required for Azure AI Agents")
|
||||
# Try to get Azure AD token using the existing Azure auth mechanisms
|
||||
# This uses the scope for Azure AI (ai.azure.com) instead of cognitive services
|
||||
# Create a GenericLiteLLMParams with the scope override for Azure Foundry Agents
|
||||
azure_auth_params = dict(litellm_params) if litellm_params else {}
|
||||
azure_auth_params["azure_scope"] = "https://ai.azure.com/.default"
|
||||
api_key = get_azure_ad_token(GenericLiteLLMParams(**azure_auth_params))
|
||||
|
||||
if api_key is None:
|
||||
raise ValueError(
|
||||
"api_key (Azure AD token) is required for Azure Foundry Agents. "
|
||||
"Either pass api_key directly, or set AZURE_TENANT_ID, AZURE_CLIENT_ID, "
|
||||
"and AZURE_CLIENT_SECRET environment variables for Service Principal auth. "
|
||||
"Manual token: az account get-access-token --resource 'https://ai.azure.com'"
|
||||
)
|
||||
if acompletion:
|
||||
if stream:
|
||||
# Native async streaming via SSE - return the async generator directly
|
||||
|
|
|
|||
|
|
@ -93,8 +93,8 @@
|
|||
{
|
||||
"key": "api_base",
|
||||
"label": "Azure AI API Base",
|
||||
"placeholder": "https://your-project.services.ai.azure.com",
|
||||
"tooltip": "The base URL for your Azure AI Foundry project endpoint",
|
||||
"placeholder": "https://your-resource.services.ai.azure.com/api/projects/your-project",
|
||||
"tooltip": "The base URL for your Azure AI Foundry project endpoint (e.g., https://your-resource.services.ai.azure.com/api/projects/your-project)",
|
||||
"required": true,
|
||||
"field_type": "text",
|
||||
"default_value": null,
|
||||
|
|
@ -102,10 +102,40 @@
|
|||
},
|
||||
{
|
||||
"key": "api_key",
|
||||
"label": "Azure AI API Key",
|
||||
"label": "Azure AD Token",
|
||||
"placeholder": null,
|
||||
"tooltip": "API key for authenticating with your Azure AI Foundry project",
|
||||
"required": true,
|
||||
"tooltip": "Azure AD Bearer token for authentication. Optional if using Service Principal credentials below. Get via: az account get-access-token --resource https://ai.azure.com",
|
||||
"required": false,
|
||||
"field_type": "password",
|
||||
"default_value": null,
|
||||
"include_in_litellm_params": true
|
||||
},
|
||||
{
|
||||
"key": "tenant_id",
|
||||
"label": "Azure Tenant ID",
|
||||
"placeholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||
"tooltip": "Azure AD Tenant ID for Service Principal authentication. Find in Azure Portal > Azure Active Directory > Overview",
|
||||
"required": false,
|
||||
"field_type": "text",
|
||||
"default_value": null,
|
||||
"include_in_litellm_params": true
|
||||
},
|
||||
{
|
||||
"key": "client_id",
|
||||
"label": "Azure Client ID",
|
||||
"placeholder": "xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx",
|
||||
"tooltip": "Application (client) ID of your Service Principal. Find in Azure Portal > App registrations > your app",
|
||||
"required": false,
|
||||
"field_type": "text",
|
||||
"default_value": null,
|
||||
"include_in_litellm_params": true
|
||||
},
|
||||
{
|
||||
"key": "client_secret",
|
||||
"label": "Azure Client Secret",
|
||||
"placeholder": null,
|
||||
"tooltip": "Client secret for your Service Principal. Create in Azure Portal > App registrations > your app > Certificates & secrets",
|
||||
"required": false,
|
||||
"field_type": "password",
|
||||
"default_value": null,
|
||||
"include_in_litellm_params": true
|
||||
|
|
|
|||
|
|
@ -1,9 +1,9 @@
|
|||
"""
|
||||
Tests for Azure AI Agent Service integration.
|
||||
Tests for Azure Foundry Agent Service integration.
|
||||
|
||||
These tests require an Azure AI Agent Service endpoint and a pre-configured agent.
|
||||
These tests require an Azure Foundry Agent Service endpoint and a pre-configured agent.
|
||||
|
||||
The Azure AI Agent Service uses the Assistants API pattern:
|
||||
The Azure Foundry Agent Service uses the Assistants API pattern:
|
||||
1. Create a thread
|
||||
2. Add messages to the thread
|
||||
3. Create and poll a run
|
||||
|
|
@ -11,9 +11,16 @@ The Azure AI Agent Service uses the Assistants API pattern:
|
|||
|
||||
Model format: azure_ai/agents/<agent_id>
|
||||
|
||||
API Base format: https://<AIFoundryResourceName>.services.ai.azure.com/api/projects/<ProjectName>
|
||||
|
||||
Authentication: Uses Azure AD Bearer tokens (not API keys)
|
||||
Get token via: az account get-access-token --resource 'https://ai.azure.com'
|
||||
|
||||
Example environment variables:
|
||||
AZURE_AI_API_BASE=https://your-project.services.ai.azure.com
|
||||
AZURE_AI_API_KEY=your-api-key
|
||||
AZURE_AGENTS_API_BASE=https://litellm-ci-cd-prod.services.ai.azure.com/api/projects/litellm-ci-cd
|
||||
AZURE_AGENTS_API_KEY=<Azure AD Bearer token>
|
||||
|
||||
See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
"""
|
||||
|
||||
import os
|
||||
|
|
@ -29,13 +36,15 @@ import litellm
|
|||
@pytest.mark.asyncio
|
||||
async def test_azure_ai_agents_acompletion_non_streaming():
|
||||
"""
|
||||
Test non-streaming acompletion call to Azure AI Agent Service.
|
||||
Test non-streaming acompletion call to Azure Foundry Agent Service.
|
||||
Uses the multi-step flow: create thread -> add messages -> create/poll run -> get messages
|
||||
"""
|
||||
api_base = os.environ.get("AZURE_API_BASE")
|
||||
api_key = os.environ.get("AZURE_API_KEY")
|
||||
agent_id = "asst_shNRIVxMPuvSRVWP5WvVe4jE"
|
||||
api_base = os.environ.get("AZURE_AGENTS_API_BASE")
|
||||
api_key = os.environ.get("AZURE_AGENTS_API_KEY")
|
||||
agent_id = os.environ.get("AZURE_AGENTS_AGENT_ID", "asst_hbnoK9BOCcHhC3lC4MDroVGG")
|
||||
|
||||
if not api_base or not api_key:
|
||||
pytest.skip("AZURE_AGENTS_API_BASE and AZURE_AGENTS_API_KEY environment variables required")
|
||||
|
||||
response = await litellm.acompletion(
|
||||
model=f"azure_ai/agents/{agent_id}",
|
||||
|
|
@ -62,12 +71,15 @@ async def test_azure_ai_agents_acompletion_non_streaming():
|
|||
@pytest.mark.asyncio
|
||||
async def test_azure_ai_agents_acompletion_streaming():
|
||||
"""
|
||||
Test native streaming acompletion call to Azure AI Agent Service.
|
||||
Test native streaming acompletion call to Azure Foundry Agent Service.
|
||||
Uses the create-thread-and-run endpoint with stream=True for SSE streaming.
|
||||
"""
|
||||
api_base = os.environ.get("AZURE_API_BASE")
|
||||
api_key = os.environ.get("AZURE_API_KEY")
|
||||
agent_id = os.environ.get("AZURE_AGENTS_AGENT_ID", "asst_shNRIVxMPuvSRVWP5WvVe4jE")
|
||||
api_base = os.environ.get("AZURE_AGENTS_API_BASE")
|
||||
api_key = os.environ.get("AZURE_AGENTS_API_KEY")
|
||||
agent_id = os.environ.get("AZURE_AGENTS_AGENT_ID", "asst_hbnoK9BOCcHhC3lC4MDroVGG")
|
||||
|
||||
if not api_base or not api_key:
|
||||
pytest.skip("AZURE_AGENTS_API_BASE and AZURE_AGENTS_API_KEY environment variables required")
|
||||
|
||||
response = await litellm.acompletion(
|
||||
model=f"azure_ai/agents/{agent_id}",
|
||||
|
|
@ -223,7 +235,7 @@ def test_azure_ai_agents_config_transform_request():
|
|||
assert request["messages"][0]["role"] == "system"
|
||||
assert request["messages"][1]["role"] == "user"
|
||||
assert "api_version" in request
|
||||
assert request["api_version"] == "2024-07-01-preview"
|
||||
assert request["api_version"] == "2025-05-01"
|
||||
|
||||
|
||||
def test_azure_ai_agents_provider_detection():
|
||||
|
|
@ -243,7 +255,9 @@ def test_azure_ai_agents_provider_detection():
|
|||
|
||||
def test_azure_ai_agents_validate_environment():
|
||||
"""
|
||||
Test that headers are correctly set up.
|
||||
Test that headers are correctly set up with Bearer token authentication.
|
||||
|
||||
Azure Foundry Agents uses Bearer token authentication (Azure AD tokens).
|
||||
"""
|
||||
from litellm.llms.azure_ai.agents.transformation import AzureAIAgentsConfig
|
||||
|
||||
|
|
@ -255,41 +269,44 @@ def test_azure_ai_agents_validate_environment():
|
|||
messages=[],
|
||||
optional_params={},
|
||||
litellm_params={},
|
||||
api_key="test-api-key",
|
||||
api_base="https://test.services.ai.azure.com",
|
||||
api_key="test-azure-ad-token",
|
||||
api_base="https://test.services.ai.azure.com/api/projects/test-project",
|
||||
)
|
||||
|
||||
assert headers["Content-Type"] == "application/json"
|
||||
assert headers["api-key"] == "test-api-key"
|
||||
assert headers["Authorization"] == "Bearer test-azure-ad-token"
|
||||
|
||||
|
||||
def test_azure_ai_agents_handler_url_builders():
|
||||
"""
|
||||
Test the URL building methods in the handler.
|
||||
|
||||
Azure Foundry Agents API uses direct paths without /openai/ prefix.
|
||||
See: https://learn.microsoft.com/en-us/azure/ai-foundry/agents/quickstart
|
||||
"""
|
||||
from litellm.llms.azure_ai.agents.handler import AzureAIAgentsHandler
|
||||
|
||||
handler = AzureAIAgentsHandler()
|
||||
api_base = "https://test.services.ai.azure.com"
|
||||
api_version = "2024-07-01-preview"
|
||||
api_base = "https://test.services.ai.azure.com/api/projects/test-project"
|
||||
api_version = "2025-05-01"
|
||||
thread_id = "thread_abc123"
|
||||
run_id = "run_xyz789"
|
||||
|
||||
# Test thread URL - uses /openai/ prefix
|
||||
# Test thread URL - direct path without /openai/ prefix
|
||||
thread_url = handler._build_thread_url(api_base, api_version)
|
||||
assert thread_url == f"{api_base}/openai/threads?api-version={api_version}"
|
||||
assert thread_url == f"{api_base}/threads?api-version={api_version}"
|
||||
|
||||
# Test messages URL
|
||||
messages_url = handler._build_messages_url(api_base, thread_id, api_version)
|
||||
assert messages_url == f"{api_base}/openai/threads/{thread_id}/messages?api-version={api_version}"
|
||||
assert messages_url == f"{api_base}/threads/{thread_id}/messages?api-version={api_version}"
|
||||
|
||||
# Test runs URL
|
||||
runs_url = handler._build_runs_url(api_base, thread_id, api_version)
|
||||
assert runs_url == f"{api_base}/openai/threads/{thread_id}/runs?api-version={api_version}"
|
||||
assert runs_url == f"{api_base}/threads/{thread_id}/runs?api-version={api_version}"
|
||||
|
||||
# Test run status URL
|
||||
status_url = handler._build_run_status_url(api_base, thread_id, run_id, api_version)
|
||||
assert status_url == f"{api_base}/openai/threads/{thread_id}/runs/{run_id}?api-version={api_version}"
|
||||
assert status_url == f"{api_base}/threads/{thread_id}/runs/{run_id}?api-version={api_version}"
|
||||
|
||||
|
||||
def test_azure_ai_agents_extract_content_from_messages():
|
||||
|
|
@ -340,12 +357,12 @@ async def test_azure_ai_agents_conversation_continuity():
|
|||
"""
|
||||
Test that thread_id can be used for conversation continuity.
|
||||
"""
|
||||
api_base = os.environ.get("AZURE_AI_API_BASE")
|
||||
api_key = os.environ.get("AZURE_AI_API_KEY")
|
||||
agent_id = os.environ.get("AZURE_AI_AGENTS_AGENT_ID", "asst_shNRIVxMPuvSRVWP5WvVe4jE")
|
||||
api_base = os.environ.get("AZURE_AGENTS_API_BASE")
|
||||
api_key = os.environ.get("AZURE_AGENTS_API_KEY")
|
||||
agent_id = os.environ.get("AZURE_AGENTS_AGENT_ID", "asst_hbnoK9BOCcHhC3lC4MDroVGG")
|
||||
|
||||
if not api_base or not api_key:
|
||||
pytest.skip("AZURE_AI_API_BASE and AZURE_AI_API_KEY environment variables required")
|
||||
pytest.skip("AZURE_AGENTS_API_BASE and AZURE_AGENTS_API_KEY environment variables required")
|
||||
|
||||
try:
|
||||
# First message
|
||||
|
|
|
|||
Loading…
Add table
Reference in a new issue