mirror of
https://github.com/BerriAI/litellm.git
synced 2026-10-10 03:28:53 +00:00
feat: support Azure OpenAI container API
- Register AzureOpenAIContainerConfig in ProviderConfigManager and proxy handler_factory - Add AZURE_DEFAULT_CONTAINERS_API_VERSION constant - Update custom_llm_provider type hints to accept "azure" in containers/main.py - Update containers and container_files docs with Azure examples
This commit is contained in:
parent
cec3e9e7d4
commit
29b83cb016
8 changed files with 571 additions and 66 deletions
|
|
@ -15,7 +15,7 @@ Looking for how to use Code Interpreter? See the [Code Interpreter Guide](/docs/
|
|||
|---------|-----------|
|
||||
| Cost Tracking | ✅ |
|
||||
| Logging | ✅ |
|
||||
| Supported Providers | `openai` |
|
||||
| Supported Providers | `openai`, `azure` |
|
||||
|
||||
## Endpoints
|
||||
|
||||
|
|
@ -27,12 +27,18 @@ Looking for how to use Code Interpreter? See the [Code Interpreter Guide](/docs/
|
|||
| `/v1/containers/{container_id}/files/{file_id}/content` | GET | Download file content |
|
||||
| `/v1/containers/{container_id}/files/{file_id}` | DELETE | Delete file |
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
## LiteLLM Python SDK
|
||||
|
||||
### Upload Container File
|
||||
|
||||
Upload files directly to a container session. This is useful when `/chat/completions` or `/responses` sends files to the container but the input file type is limited to PDF. This endpoint lets you work with other file types like CSV, Excel, Python scripts, etc.
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python showLineNumbers title="upload_container_file.py"
|
||||
from litellm import upload_container_file
|
||||
|
||||
|
|
@ -47,6 +53,29 @@ print(f"Uploaded: {file.id}")
|
|||
print(f"Path: {file.path}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```python showLineNumbers title="upload_container_file_azure.py"
|
||||
from litellm import upload_container_file
|
||||
import os
|
||||
|
||||
os.environ["AZURE_API_KEY"] = "your-azure-api-key"
|
||||
os.environ["AZURE_API_BASE"] = "https://your-resource.openai.azure.com"
|
||||
|
||||
file = upload_container_file(
|
||||
container_id="cntr_123...",
|
||||
file=("data.csv", open("data.csv", "rb").read(), "text/csv"),
|
||||
custom_llm_provider="azure"
|
||||
)
|
||||
|
||||
print(f"Uploaded: {file.id}")
|
||||
print(f"Path: {file.path}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Async:**
|
||||
|
||||
```python showLineNumbers title="aupload_container_file.py"
|
||||
|
|
@ -55,7 +84,7 @@ from litellm import aupload_container_file
|
|||
file = await aupload_container_file(
|
||||
container_id="cntr_123...",
|
||||
file=("script.py", b"print('hello world')", "text/x-python"),
|
||||
custom_llm_provider="openai"
|
||||
custom_llm_provider="openai" # or "azure"
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -70,6 +99,9 @@ file = await aupload_container_file(
|
|||
|
||||
### List Container Files
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python showLineNumbers title="list_container_files.py"
|
||||
from litellm import list_container_files
|
||||
|
||||
|
|
@ -82,6 +114,28 @@ for file in files.data:
|
|||
print(f" - {file.id}: {file.filename}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```python showLineNumbers title="list_container_files_azure.py"
|
||||
from litellm import list_container_files
|
||||
import os
|
||||
|
||||
os.environ["AZURE_API_KEY"] = "your-azure-api-key"
|
||||
os.environ["AZURE_API_BASE"] = "https://your-resource.openai.azure.com"
|
||||
|
||||
files = list_container_files(
|
||||
container_id="cntr_123...",
|
||||
custom_llm_provider="azure"
|
||||
)
|
||||
|
||||
for file in files.data:
|
||||
print(f" - {file.id}: {file.filename}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Async:**
|
||||
|
||||
```python showLineNumbers title="alist_container_files.py"
|
||||
|
|
@ -89,7 +143,7 @@ from litellm import alist_container_files
|
|||
|
||||
files = await alist_container_files(
|
||||
container_id="cntr_123...",
|
||||
custom_llm_provider="openai"
|
||||
custom_llm_provider="openai" # or "azure"
|
||||
)
|
||||
```
|
||||
|
||||
|
|
@ -101,7 +155,7 @@ from litellm import retrieve_container_file
|
|||
file = retrieve_container_file(
|
||||
container_id="cntr_123...",
|
||||
file_id="cfile_456...",
|
||||
custom_llm_provider="openai"
|
||||
custom_llm_provider="openai" # or "azure"
|
||||
)
|
||||
|
||||
print(f"File: {file.filename}")
|
||||
|
|
@ -116,7 +170,7 @@ from litellm import retrieve_container_file_content
|
|||
content = retrieve_container_file_content(
|
||||
container_id="cntr_123...",
|
||||
file_id="cfile_456...",
|
||||
custom_llm_provider="openai"
|
||||
custom_llm_provider="openai" # or "azure"
|
||||
)
|
||||
|
||||
# content is raw bytes
|
||||
|
|
@ -132,7 +186,7 @@ from litellm import delete_container_file
|
|||
result = delete_container_file(
|
||||
container_id="cntr_123...",
|
||||
file_id="cfile_456...",
|
||||
custom_llm_provider="openai"
|
||||
custom_llm_provider="openai" # or "azure"
|
||||
)
|
||||
|
||||
print(f"Deleted: {result.deleted}")
|
||||
|
|
@ -140,9 +194,6 @@ print(f"Deleted: {result.deleted}")
|
|||
|
||||
## LiteLLM AI Gateway (Proxy)
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
### Upload File
|
||||
|
||||
<Tabs>
|
||||
|
|
@ -377,6 +428,7 @@ curl -X DELETE "http://localhost:4000/v1/containers/cntr_123.../files/cfile_456.
|
|||
| Provider | Status |
|
||||
|----------|--------|
|
||||
| OpenAI | ✅ Supported |
|
||||
| Azure OpenAI | ✅ Supported |
|
||||
|
||||
## Related
|
||||
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
# /containers
|
||||
|
||||
Manage OpenAI code interpreter containers (sessions) for executing code in isolated environments.
|
||||
Manage code interpreter containers (sessions) for executing code in isolated environments.
|
||||
|
||||
:::tip
|
||||
Looking for how to use Code Interpreter? See the [Code Interpreter Guide](/docs/guides/code_interpreter).
|
||||
|
|
@ -13,7 +13,7 @@ Looking for how to use Code Interpreter? See the [Code Interpreter Guide](/docs/
|
|||
| Load Balancing | ✅ |
|
||||
| Proxy Server Support | ✅ Full proxy integration with virtual keys |
|
||||
| Spend Management | ✅ Budget tracking and rate limiting |
|
||||
| Supported Providers | `openai`|
|
||||
| Supported Providers | `openai`, `azure`|
|
||||
|
||||
:::tip
|
||||
|
||||
|
|
@ -23,15 +23,20 @@ Containers provide isolated execution environments for code interpreter sessions
|
|||
|
||||
## **LiteLLM Python SDK Usage**
|
||||
|
||||
import Tabs from '@theme/Tabs';
|
||||
import TabItem from '@theme/TabItem';
|
||||
|
||||
### Quick Start
|
||||
|
||||
**Create a Container**
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
import litellm
|
||||
import os
|
||||
import os
|
||||
|
||||
# setup env
|
||||
os.environ["OPENAI_API_KEY"] = "sk-.."
|
||||
|
||||
container = litellm.create_container(
|
||||
|
|
@ -47,11 +52,40 @@ print(f"Container ID: {container.id}")
|
|||
print(f"Container Name: {container.name}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```python
|
||||
import litellm
|
||||
import os
|
||||
|
||||
os.environ["AZURE_API_KEY"] = "your-azure-api-key"
|
||||
os.environ["AZURE_API_BASE"] = "https://your-resource.openai.azure.com"
|
||||
|
||||
container = litellm.create_container(
|
||||
name="My Code Interpreter Container",
|
||||
custom_llm_provider="azure",
|
||||
expires_after={
|
||||
"anchor": "last_active_at",
|
||||
"minutes": 20
|
||||
}
|
||||
)
|
||||
|
||||
print(f"Container ID: {container.id}")
|
||||
print(f"Container Name: {container.name}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### Async Usage
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import acreate_container
|
||||
import os
|
||||
import os
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-.."
|
||||
|
||||
|
|
@ -68,11 +102,40 @@ print(f"Container ID: {container.id}")
|
|||
print(f"Container Name: {container.name}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import acreate_container
|
||||
import os
|
||||
|
||||
os.environ["AZURE_API_KEY"] = "your-azure-api-key"
|
||||
os.environ["AZURE_API_BASE"] = "https://your-resource.openai.azure.com"
|
||||
|
||||
container = await acreate_container(
|
||||
name="My Code Interpreter Container",
|
||||
custom_llm_provider="azure",
|
||||
expires_after={
|
||||
"anchor": "last_active_at",
|
||||
"minutes": 20
|
||||
}
|
||||
)
|
||||
|
||||
print(f"Container ID: {container.id}")
|
||||
print(f"Container Name: {container.name}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
### List Containers
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import list_containers
|
||||
import os
|
||||
import os
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-.."
|
||||
|
||||
|
|
@ -87,13 +150,37 @@ for container in containers.data:
|
|||
print(f" - {container.id}: {container.name}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import list_containers
|
||||
import os
|
||||
|
||||
os.environ["AZURE_API_KEY"] = "your-azure-api-key"
|
||||
os.environ["AZURE_API_BASE"] = "https://your-resource.openai.azure.com"
|
||||
|
||||
containers = list_containers(
|
||||
custom_llm_provider="azure",
|
||||
limit=20,
|
||||
order="desc"
|
||||
)
|
||||
|
||||
print(f"Found {len(containers.data)} containers")
|
||||
for container in containers.data:
|
||||
print(f" - {container.id}: {container.name}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Async Usage:**
|
||||
|
||||
```python
|
||||
from litellm import alist_containers
|
||||
|
||||
containers = await alist_containers(
|
||||
custom_llm_provider="openai",
|
||||
custom_llm_provider="openai", # or "azure"
|
||||
limit=20,
|
||||
order="desc"
|
||||
)
|
||||
|
|
@ -105,9 +192,12 @@ for container in containers.data:
|
|||
|
||||
### Retrieve a Container
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import retrieve_container
|
||||
import os
|
||||
import os
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-.."
|
||||
|
||||
|
|
@ -121,6 +211,29 @@ print(f"Status: {container.status}")
|
|||
print(f"Created: {container.created_at}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import retrieve_container
|
||||
import os
|
||||
|
||||
os.environ["AZURE_API_KEY"] = "your-azure-api-key"
|
||||
os.environ["AZURE_API_BASE"] = "https://your-resource.openai.azure.com"
|
||||
|
||||
container = retrieve_container(
|
||||
container_id="cntr_123...",
|
||||
custom_llm_provider="azure"
|
||||
)
|
||||
|
||||
print(f"Container: {container.name}")
|
||||
print(f"Status: {container.status}")
|
||||
print(f"Created: {container.created_at}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Async Usage:**
|
||||
|
||||
```python
|
||||
|
|
@ -128,7 +241,7 @@ from litellm import aretrieve_container
|
|||
|
||||
container = await aretrieve_container(
|
||||
container_id="cntr_123...",
|
||||
custom_llm_provider="openai"
|
||||
custom_llm_provider="openai" # or "azure"
|
||||
)
|
||||
|
||||
print(f"Container: {container.name}")
|
||||
|
|
@ -138,9 +251,12 @@ print(f"Created: {container.created_at}")
|
|||
|
||||
### Delete a Container
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import delete_container
|
||||
import os
|
||||
import os
|
||||
|
||||
os.environ["OPENAI_API_KEY"] = "sk-.."
|
||||
|
||||
|
|
@ -153,6 +269,28 @@ print(f"Deleted: {result.deleted}")
|
|||
print(f"Container ID: {result.id}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```python
|
||||
from litellm import delete_container
|
||||
import os
|
||||
|
||||
os.environ["AZURE_API_KEY"] = "your-azure-api-key"
|
||||
os.environ["AZURE_API_BASE"] = "https://your-resource.openai.azure.com"
|
||||
|
||||
result = delete_container(
|
||||
container_id="cntr_123...",
|
||||
custom_llm_provider="azure"
|
||||
)
|
||||
|
||||
print(f"Deleted: {result.deleted}")
|
||||
print(f"Container ID: {result.id}")
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**Async Usage:**
|
||||
|
||||
```python
|
||||
|
|
@ -160,7 +298,7 @@ from litellm import adelete_container
|
|||
|
||||
result = await adelete_container(
|
||||
container_id="cntr_123...",
|
||||
custom_llm_provider="openai"
|
||||
custom_llm_provider="openai" # or "azure"
|
||||
)
|
||||
|
||||
print(f"Deleted: {result.deleted}")
|
||||
|
|
@ -177,8 +315,13 @@ LiteLLM provides OpenAI API compatible container endpoints for managing code int
|
|||
**Setup**
|
||||
|
||||
```bash
|
||||
# For OpenAI
|
||||
$ export OPENAI_API_KEY="sk-..."
|
||||
|
||||
# For Azure OpenAI
|
||||
$ export AZURE_API_KEY="your-azure-api-key"
|
||||
$ export AZURE_API_BASE="https://your-resource.openai.azure.com"
|
||||
|
||||
$ litellm
|
||||
|
||||
# RUNNING on http://0.0.0.0:4000
|
||||
|
|
@ -187,15 +330,17 @@ $ litellm
|
|||
**Custom Provider Specification**
|
||||
|
||||
You can specify the custom LLM provider in multiple ways (priority order):
|
||||
1. Header: `-H "custom-llm-provider: openai"`
|
||||
2. Query param: `?custom_llm_provider=openai`
|
||||
3. Request body: `{"custom_llm_provider": "openai", ...}`
|
||||
1. Header: `-H "custom-llm-provider: openai"` (or `azure`)
|
||||
2. Query param: `?custom_llm_provider=openai` (or `azure`)
|
||||
3. Request body: `{"custom_llm_provider": "openai", ...}` (or `"azure"`)
|
||||
4. Defaults to "openai" if not specified
|
||||
|
||||
**Create a Container**
|
||||
|
||||
<Tabs>
|
||||
<TabItem value="openai" label="OpenAI">
|
||||
|
||||
```bash
|
||||
# Default provider (openai)
|
||||
curl -X POST "http://localhost:4000/v1/containers" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
|
|
@ -208,32 +353,36 @@ curl -X POST "http://localhost:4000/v1/containers" \
|
|||
}'
|
||||
```
|
||||
|
||||
</TabItem>
|
||||
<TabItem value="azure" label="Azure OpenAI">
|
||||
|
||||
```bash
|
||||
# Via header
|
||||
curl -X POST "http://localhost:4000/v1/containers" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "custom-llm-provider: openai" \
|
||||
-H "custom-llm-provider: azure" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "My Container"
|
||||
"name": "My Container",
|
||||
"expires_after": {
|
||||
"anchor": "last_active_at",
|
||||
"minutes": 20
|
||||
}
|
||||
}'
|
||||
```
|
||||
|
||||
```bash
|
||||
# Via query parameter
|
||||
curl -X POST "http://localhost:4000/v1/containers?custom_llm_provider=openai" \
|
||||
-H "Authorization: Bearer sk-1234" \
|
||||
-H "Content-Type: application/json" \
|
||||
-d '{
|
||||
"name": "My Container"
|
||||
}'
|
||||
```
|
||||
</TabItem>
|
||||
</Tabs>
|
||||
|
||||
**List Containers**
|
||||
|
||||
```bash
|
||||
# OpenAI (default)
|
||||
curl "http://localhost:4000/v1/containers?limit=20&order=desc" \
|
||||
-H "Authorization: Bearer sk-1234"
|
||||
|
||||
# Azure OpenAI
|
||||
curl "http://localhost:4000/v1/containers?limit=20&order=desc&custom_llm_provider=azure" \
|
||||
-H "Authorization: Bearer sk-1234"
|
||||
```
|
||||
|
||||
**Retrieve a Container**
|
||||
|
|
@ -460,12 +609,7 @@ print(f"Deleted: {result.deleted}")
|
|||
| Provider | Support Status | Notes |
|
||||
|-------------|----------------|-------|
|
||||
| OpenAI | ✅ Supported | Full support for all container operations |
|
||||
|
||||
:::info
|
||||
|
||||
Currently, only OpenAI supports container management for code interpreter sessions. Support for additional providers may be added in the future.
|
||||
|
||||
:::
|
||||
| Azure OpenAI | ✅ Supported | Full support for all container operations |
|
||||
|
||||
## Related
|
||||
|
||||
|
|
|
|||
|
|
@ -10,6 +10,9 @@ DEFAULT_HEALTH_CHECK_PROMPT = str(
|
|||
AZURE_DEFAULT_RESPONSES_API_VERSION = str(
|
||||
os.getenv("AZURE_DEFAULT_RESPONSES_API_VERSION", "preview")
|
||||
)
|
||||
AZURE_DEFAULT_CONTAINERS_API_VERSION = str(
|
||||
os.getenv("AZURE_DEFAULT_CONTAINERS_API_VERSION", "preview")
|
||||
)
|
||||
ROUTER_MAX_FALLBACKS = int(os.getenv("ROUTER_MAX_FALLBACKS", 5))
|
||||
DEFAULT_BATCH_SIZE = int(os.getenv("DEFAULT_BATCH_SIZE", 512))
|
||||
DEFAULT_FLUSH_INTERVAL_SECONDS = int(os.getenv("DEFAULT_FLUSH_INTERVAL_SECONDS", 5))
|
||||
|
|
|
|||
|
|
@ -48,7 +48,7 @@ async def acreate_container(
|
|||
file_ids: Optional[List[str]] = None,
|
||||
timeout=600, # default to 10 minutes
|
||||
# LiteLLM specific params,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -122,7 +122,7 @@ def create_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
acreate_container: Literal[True],
|
||||
**kwargs,
|
||||
|
|
@ -139,7 +139,7 @@ def create_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
acreate_container: Literal[False] = False,
|
||||
**kwargs,
|
||||
|
|
@ -158,7 +158,7 @@ def create_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -275,7 +275,7 @@ async def alist_containers(
|
|||
limit: Optional[int] = None,
|
||||
order: Optional[str] = None,
|
||||
timeout=600, # default to 10 minutes
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -348,7 +348,7 @@ def list_containers(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
alist_containers: Literal[True],
|
||||
**kwargs,
|
||||
|
|
@ -365,7 +365,7 @@ def list_containers(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
alist_containers: Literal[False] = False,
|
||||
**kwargs,
|
||||
|
|
@ -384,7 +384,7 @@ def list_containers(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -481,7 +481,7 @@ def list_containers(
|
|||
async def aretrieve_container(
|
||||
container_id: str,
|
||||
timeout=600, # default to 10 minutes
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -548,7 +548,7 @@ def retrieve_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
aretrieve_container: Literal[True],
|
||||
**kwargs,
|
||||
|
|
@ -563,7 +563,7 @@ def retrieve_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
aretrieve_container: Literal[False] = False,
|
||||
**kwargs,
|
||||
|
|
@ -580,7 +580,7 @@ def retrieve_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -667,7 +667,7 @@ def retrieve_container(
|
|||
async def adelete_container(
|
||||
container_id: str,
|
||||
timeout=600, # default to 10 minutes
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -734,7 +734,7 @@ def delete_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
adelete_container: Literal[True],
|
||||
**kwargs,
|
||||
|
|
@ -749,7 +749,7 @@ def delete_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
adelete_container: Literal[False] = False,
|
||||
**kwargs,
|
||||
|
|
@ -766,7 +766,7 @@ def delete_container(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
# Use the following arguments if you need to pass additional parameters to the API that aren't available via kwargs.
|
||||
# The extra values given here take precedence over values defined on the client or passed to this method.
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -856,7 +856,7 @@ async def alist_container_files(
|
|||
limit: Optional[int] = None,
|
||||
order: Optional[str] = None,
|
||||
timeout=600, # default to 10 minutes
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
extra_query: Optional[Dict[str, Any]] = None,
|
||||
extra_body: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -930,7 +930,7 @@ def list_container_files(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
alist_container_files: Literal[True],
|
||||
**kwargs,
|
||||
|
|
@ -948,7 +948,7 @@ def list_container_files(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
alist_container_files: Literal[False] = False,
|
||||
**kwargs,
|
||||
|
|
@ -968,7 +968,7 @@ def list_container_files(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
extra_query: Optional[Dict[str, Any]] = None,
|
||||
extra_body: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -1062,7 +1062,7 @@ async def aupload_container_file(
|
|||
container_id: str,
|
||||
file: FileTypes,
|
||||
timeout=600, # default to 10 minutes
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
extra_query: Optional[Dict[str, Any]] = None,
|
||||
extra_body: Optional[Dict[str, Any]] = None,
|
||||
|
|
@ -1151,7 +1151,7 @@ def upload_container_file(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
aupload_container_file: Literal[True],
|
||||
**kwargs,
|
||||
|
|
@ -1167,7 +1167,7 @@ def upload_container_file(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
*,
|
||||
aupload_container_file: Literal[False] = False,
|
||||
**kwargs,
|
||||
|
|
@ -1185,7 +1185,7 @@ def upload_container_file(
|
|||
api_key: Optional[str] = None,
|
||||
api_base: Optional[str] = None,
|
||||
api_version: Optional[str] = None,
|
||||
custom_llm_provider: Literal["openai"] = "openai",
|
||||
custom_llm_provider: Literal["openai", "azure"] = "openai",
|
||||
extra_headers: Optional[Dict[str, Any]] = None,
|
||||
extra_query: Optional[Dict[str, Any]] = None,
|
||||
extra_body: Optional[Dict[str, Any]] = None,
|
||||
|
|
|
|||
48
litellm/llms/azure/containers/transformation.py
Normal file
48
litellm/llms/azure/containers/transformation.py
Normal file
|
|
@ -0,0 +1,48 @@
|
|||
from typing import Optional
|
||||
|
||||
from litellm.constants import AZURE_DEFAULT_CONTAINERS_API_VERSION
|
||||
from litellm.llms.azure.common_utils import BaseAzureLLM
|
||||
from litellm.llms.openai.containers.transformation import OpenAIContainerConfig
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
|
||||
|
||||
class AzureOpenAIContainerConfig(OpenAIContainerConfig):
|
||||
"""Azure OpenAI Container Config.
|
||||
|
||||
Inherits from OpenAIContainerConfig and overrides only Azure-specific methods.
|
||||
Request/response transformations are identical to OpenAI.
|
||||
"""
|
||||
|
||||
def get_complete_url(
|
||||
self,
|
||||
api_base: Optional[str],
|
||||
litellm_params: dict,
|
||||
) -> str:
|
||||
"""Get the complete URL for Azure container API.
|
||||
|
||||
Constructs Azure-specific URLs like:
|
||||
https://{resource}.openai.azure.com/openai/v1/containers?api-version=xxx
|
||||
"""
|
||||
return BaseAzureLLM._get_base_azure_url(
|
||||
api_base=api_base,
|
||||
litellm_params=litellm_params,
|
||||
route="/openai/v1/containers",
|
||||
default_api_version=AZURE_DEFAULT_CONTAINERS_API_VERSION,
|
||||
)
|
||||
|
||||
def validate_environment(
|
||||
self,
|
||||
headers: dict,
|
||||
api_key: Optional[str] = None,
|
||||
) -> dict:
|
||||
"""Validate and set up Azure authentication headers.
|
||||
|
||||
Uses Azure api-key header (not Bearer token like OpenAI).
|
||||
"""
|
||||
# Create a GenericLiteLLMParams with the api_key if provided
|
||||
litellm_params = GenericLiteLLMParams(api_key=api_key) if api_key else None
|
||||
|
||||
# Azure uses BaseAzureLLM's validation which handles api-key header
|
||||
return BaseAzureLLM._base_validate_azure_environment(
|
||||
headers=headers, litellm_params=litellm_params
|
||||
)
|
||||
|
|
@ -40,6 +40,12 @@ def _get_container_provider_config(custom_llm_provider: str):
|
|||
from litellm.llms.openai.containers.transformation import OpenAIContainerConfig
|
||||
|
||||
return OpenAIContainerConfig()
|
||||
elif custom_llm_provider == "azure":
|
||||
from litellm.llms.azure.containers.transformation import (
|
||||
AzureOpenAIContainerConfig,
|
||||
)
|
||||
|
||||
return AzureOpenAIContainerConfig()
|
||||
else:
|
||||
raise ValueError(
|
||||
f"Container API not supported for provider: {custom_llm_provider}"
|
||||
|
|
|
|||
|
|
@ -8846,6 +8846,12 @@ class ProviderConfigManager:
|
|||
)
|
||||
|
||||
return OpenAIContainerConfig()
|
||||
elif LlmProviders.AZURE == provider:
|
||||
from litellm.llms.azure.containers.transformation import (
|
||||
AzureOpenAIContainerConfig,
|
||||
)
|
||||
|
||||
return AzureOpenAIContainerConfig()
|
||||
return None
|
||||
|
||||
@staticmethod
|
||||
|
|
|
|||
|
|
@ -0,0 +1,246 @@
|
|||
import os
|
||||
import sys
|
||||
from unittest.mock import MagicMock
|
||||
|
||||
import httpx
|
||||
import pytest
|
||||
|
||||
sys.path.insert(
|
||||
0, os.path.abspath("../../..")
|
||||
) # Adds the parent directory to the system path
|
||||
|
||||
import litellm
|
||||
from litellm.llms.azure.containers.transformation import AzureOpenAIContainerConfig
|
||||
from litellm.llms.openai.containers.transformation import OpenAIContainerConfig
|
||||
from litellm.types.containers.main import (
|
||||
ContainerFileListResponse,
|
||||
ContainerListResponse,
|
||||
ContainerObject,
|
||||
DeleteContainerResult,
|
||||
)
|
||||
from litellm.litellm_core_utils.litellm_logging import Logging as LiteLLMLogging
|
||||
from litellm.types.router import GenericLiteLLMParams
|
||||
from litellm.utils import ProviderConfigManager
|
||||
|
||||
|
||||
class TestAzureContainerConfigInheritance:
|
||||
"""Test that AzureOpenAIContainerConfig properly inherits from OpenAI."""
|
||||
|
||||
def test_inherits_from_openai(self):
|
||||
config = AzureOpenAIContainerConfig()
|
||||
assert isinstance(config, OpenAIContainerConfig)
|
||||
|
||||
def test_get_supported_openai_params(self):
|
||||
config = AzureOpenAIContainerConfig()
|
||||
supported_params = config.get_supported_openai_params()
|
||||
assert "name" in supported_params
|
||||
assert "expires_after" in supported_params
|
||||
assert "file_ids" in supported_params
|
||||
|
||||
|
||||
class TestAzureContainerValidateEnvironment:
|
||||
"""Test Azure-specific authentication headers."""
|
||||
|
||||
def test_validate_environment_with_api_key(self):
|
||||
config = AzureOpenAIContainerConfig()
|
||||
headers = {}
|
||||
result = config.validate_environment(headers=headers, api_key="test-azure-key")
|
||||
assert "api-key" in result
|
||||
assert result["api-key"] == "test-azure-key"
|
||||
|
||||
def test_validate_environment_does_not_use_bearer_token(self):
|
||||
config = AzureOpenAIContainerConfig()
|
||||
headers = {}
|
||||
result = config.validate_environment(headers=headers, api_key="test-azure-key")
|
||||
assert "Authorization" not in result
|
||||
|
||||
def test_validate_environment_preserves_existing_api_key_header(self):
|
||||
config = AzureOpenAIContainerConfig()
|
||||
headers = {"api-key": "existing-key"}
|
||||
result = config.validate_environment(headers=headers, api_key="new-key")
|
||||
assert result["api-key"] == "existing-key"
|
||||
|
||||
|
||||
class TestAzureContainerGetCompleteUrl:
|
||||
"""Test Azure-specific URL construction."""
|
||||
|
||||
def test_get_complete_url_constructs_azure_url(self):
|
||||
config = AzureOpenAIContainerConfig()
|
||||
api_base = "https://my-resource.openai.azure.com"
|
||||
litellm_params = {"api_version": "2024-12-01-preview"}
|
||||
|
||||
url = config.get_complete_url(
|
||||
api_base=api_base,
|
||||
litellm_params=litellm_params,
|
||||
)
|
||||
|
||||
assert "my-resource.openai.azure.com" in url
|
||||
assert "/openai/v1/containers" in url or "/openai/containers" in url
|
||||
assert "api-version" in url
|
||||
|
||||
def test_get_complete_url_uses_default_api_version(self):
|
||||
config = AzureOpenAIContainerConfig()
|
||||
api_base = "https://my-resource.openai.azure.com"
|
||||
litellm_params = {}
|
||||
|
||||
url = config.get_complete_url(
|
||||
api_base=api_base,
|
||||
litellm_params=litellm_params,
|
||||
)
|
||||
|
||||
assert "api-version" in url
|
||||
|
||||
def test_get_complete_url_differs_from_openai(self):
|
||||
azure_config = AzureOpenAIContainerConfig()
|
||||
openai_config = OpenAIContainerConfig()
|
||||
|
||||
azure_url = azure_config.get_complete_url(
|
||||
api_base="https://my-resource.openai.azure.com",
|
||||
litellm_params={},
|
||||
)
|
||||
openai_url = openai_config.get_complete_url(
|
||||
api_base="https://api.openai.com/v1",
|
||||
litellm_params={},
|
||||
)
|
||||
|
||||
assert azure_url != openai_url
|
||||
assert "azure" in azure_url
|
||||
assert "api-version" in azure_url
|
||||
|
||||
|
||||
class TestAzureContainerTransformations:
|
||||
"""Test that request/response transformations work for Azure (inherited from OpenAI)."""
|
||||
|
||||
def setup_method(self):
|
||||
self.config = AzureOpenAIContainerConfig()
|
||||
self.logging_obj = LiteLLMLogging(
|
||||
model="",
|
||||
messages=[],
|
||||
stream=False,
|
||||
call_type="create_container",
|
||||
start_time=None,
|
||||
litellm_call_id="test_call_id",
|
||||
function_id="test_function_id",
|
||||
)
|
||||
|
||||
def test_transform_container_create_request(self):
|
||||
litellm_params = GenericLiteLLMParams()
|
||||
headers = {"api-key": "test-azure-key"}
|
||||
data = self.config.transform_container_create_request(
|
||||
name="Test Container",
|
||||
container_create_optional_request_params={
|
||||
"expires_after": {"anchor": "last_active_at", "minutes": 20},
|
||||
},
|
||||
litellm_params=litellm_params,
|
||||
headers=headers,
|
||||
)
|
||||
assert data["name"] == "Test Container"
|
||||
assert data["expires_after"]["minutes"] == 20
|
||||
|
||||
def test_transform_container_create_response(self):
|
||||
mock_response = MagicMock(spec=httpx.Response)
|
||||
mock_response.json.return_value = {
|
||||
"id": "cntr_azure_123",
|
||||
"object": "container",
|
||||
"created_at": 1747857508,
|
||||
"status": "running",
|
||||
"expires_after": {"anchor": "last_active_at", "minutes": 20},
|
||||
"last_active_at": 1747857508,
|
||||
"name": "Azure Container",
|
||||
}
|
||||
container = self.config.transform_container_create_response(
|
||||
raw_response=mock_response,
|
||||
logging_obj=self.logging_obj,
|
||||
)
|
||||
assert isinstance(container, ContainerObject)
|
||||
assert container.id == "cntr_azure_123"
|
||||
assert container.name == "Azure Container"
|
||||
|
||||
def test_transform_container_list_response(self):
|
||||
mock_response = MagicMock(spec=httpx.Response)
|
||||
mock_response.json.return_value = {
|
||||
"object": "list",
|
||||
"data": [
|
||||
{
|
||||
"id": "cntr_1",
|
||||
"object": "container",
|
||||
"created_at": 1747857508,
|
||||
"status": "running",
|
||||
"expires_after": {"anchor": "last_active_at", "minutes": 20},
|
||||
"last_active_at": 1747857508,
|
||||
"name": "Container 1",
|
||||
}
|
||||
],
|
||||
"first_id": "cntr_1",
|
||||
"last_id": "cntr_1",
|
||||
"has_more": False,
|
||||
}
|
||||
result = self.config.transform_container_list_response(
|
||||
raw_response=mock_response,
|
||||
logging_obj=self.logging_obj,
|
||||
)
|
||||
assert isinstance(result, ContainerListResponse)
|
||||
assert len(result.data) == 1
|
||||
|
||||
def test_transform_container_file_list_request(self):
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers"
|
||||
url, params = self.config.transform_container_file_list_request(
|
||||
container_id="cntr_123",
|
||||
api_base=api_base,
|
||||
litellm_params={},
|
||||
headers={"api-key": "test"},
|
||||
limit=10,
|
||||
)
|
||||
assert "cntr_123/files" in url
|
||||
assert params["limit"] == "10"
|
||||
|
||||
def test_transform_container_file_content_request(self):
|
||||
api_base = "https://my-resource.openai.azure.com/openai/v1/containers"
|
||||
url, params = self.config.transform_container_file_content_request(
|
||||
container_id="cntr_123",
|
||||
file_id="file_456",
|
||||
api_base=api_base,
|
||||
litellm_params={},
|
||||
headers={"api-key": "test"},
|
||||
)
|
||||
assert "cntr_123/files/file_456/content" in url
|
||||
|
||||
def test_transform_container_file_content_response(self):
|
||||
mock_response = MagicMock(spec=httpx.Response)
|
||||
mock_response.content = b"file content bytes"
|
||||
result = self.config.transform_container_file_content_response(
|
||||
raw_response=mock_response,
|
||||
logging_obj=self.logging_obj,
|
||||
)
|
||||
assert result == b"file content bytes"
|
||||
|
||||
def test_transform_container_delete_response(self):
|
||||
mock_response = MagicMock(spec=httpx.Response)
|
||||
mock_response.json.return_value = {
|
||||
"id": "cntr_delete_123",
|
||||
"object": "container.deleted",
|
||||
"deleted": True,
|
||||
}
|
||||
result = self.config.transform_container_delete_response(
|
||||
raw_response=mock_response,
|
||||
logging_obj=self.logging_obj,
|
||||
)
|
||||
assert isinstance(result, DeleteContainerResult)
|
||||
assert result.deleted is True
|
||||
|
||||
|
||||
class TestAzureContainerProviderRegistration:
|
||||
"""Test that Azure is properly registered as a container provider."""
|
||||
|
||||
def test_provider_config_manager_returns_azure_config(self):
|
||||
config = ProviderConfigManager.get_provider_container_config(
|
||||
provider=litellm.LlmProviders.AZURE,
|
||||
)
|
||||
assert config is not None
|
||||
assert isinstance(config, AzureOpenAIContainerConfig)
|
||||
|
||||
def test_provider_config_manager_returns_none_for_unsupported(self):
|
||||
config = ProviderConfigManager.get_provider_container_config(
|
||||
provider=litellm.LlmProviders.ANTHROPIC,
|
||||
)
|
||||
assert config is None
|
||||
Loading…
Add table
Reference in a new issue