diff --git a/docs/my-website/docs/providers/bedrock.md b/docs/my-website/docs/providers/bedrock.md
index f0b89615a0d..9e22f67527e 100644
--- a/docs/my-website/docs/providers/bedrock.md
+++ b/docs/my-website/docs/providers/bedrock.md
@@ -7,7 +7,7 @@ ALL Bedrock models (Anthropic, Meta, Deepseek, Mistral, Amazon, etc.) are Suppor
| Property | Details |
|-------|-------|
| Description | Amazon Bedrock is a fully managed service that offers a choice of high-performing foundation models (FMs). |
-| Provider Route on LiteLLM | `bedrock/`, [`bedrock/converse/`](#set-converse--invoke-route), [`bedrock/invoke/`](#set-invoke-route), [`bedrock/converse_like/`](#calling-via-internal-proxy), [`bedrock/llama/`](#deepseek-not-r1), [`bedrock/deepseek_r1/`](#deepseek-r1), [`bedrock/qwen3/`](#qwen3-imported-models) |
+| Provider Route on LiteLLM | `bedrock/`, [`bedrock/converse/`](#set-converse--invoke-route), [`bedrock/invoke/`](#set-invoke-route), [`bedrock/converse_like/`](#calling-via-internal-proxy), [`bedrock/llama/`](#deepseek-not-r1), [`bedrock/deepseek_r1/`](#deepseek-r1), [`bedrock/qwen3/`](#qwen3-imported-models), [`bedrock/openai/`](./bedrock_imported.md#openai-compatible-imported-models-qwen-25-vl-etc) |
| Provider Doc | [Amazon Bedrock ↗](https://docs.aws.amazon.com/bedrock/latest/userguide/what-is-bedrock.html) |
| Supported OpenAI Endpoints | `/chat/completions`, `/completions`, `/embeddings`, `/images/generations` |
| Rerank Endpoint | `/rerank` |
@@ -1598,206 +1598,6 @@ curl -X POST 'http://0.0.0.0:4000/chat/completions' \
-## Bedrock Imported Models (Deepseek, Deepseek R1)
-
-### Deepseek R1
-
-This is a separate route, as the chat template is different.
-
-| Property | Details |
-|----------|---------|
-| Provider Route | `bedrock/deepseek_r1/{model_arn}` |
-| Provider Documentation | [Bedrock Imported Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-import-model.html), [Deepseek Bedrock Imported Model](https://aws.amazon.com/blogs/machine-learning/deploy-deepseek-r1-distilled-llama-models-with-amazon-bedrock-custom-model-import/) |
-
-
-
-
-```python
-from litellm import completion
-import os
-
-response = completion(
- model="bedrock/deepseek_r1/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n", # bedrock/deepseek_r1/{your-model-arn}
- messages=[{"role": "user", "content": "Tell me a joke"}],
-)
-```
-
-
-
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: DeepSeek-R1-Distill-Llama-70B
- litellm_params:
- model: bedrock/deepseek_r1/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n
-
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "DeepSeek-R1-Distill-Llama-70B", # 👈 the 'model_name' in config
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
-
-### Deepseek (not R1)
-
-| Property | Details |
-|----------|---------|
-| Provider Route | `bedrock/llama/{model_arn}` |
-| Provider Documentation | [Bedrock Imported Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-import-model.html), [Deepseek Bedrock Imported Model](https://aws.amazon.com/blogs/machine-learning/deploy-deepseek-r1-distilled-llama-models-with-amazon-bedrock-custom-model-import/) |
-
-
-
-Use this route to call Bedrock Imported Models that follow the `llama` Invoke Request / Response spec
-
-
-
-
-
-```python
-from litellm import completion
-import os
-
-response = completion(
- model="bedrock/llama/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n", # bedrock/llama/{your-model-arn}
- messages=[{"role": "user", "content": "Tell me a joke"}],
-)
-```
-
-
-
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: DeepSeek-R1-Distill-Llama-70B
- litellm_params:
- model: bedrock/llama/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n
-
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "DeepSeek-R1-Distill-Llama-70B", # 👈 the 'model_name' in config
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
-### Qwen3 Imported Models
-
-| Property | Details |
-|----------|---------|
-| Provider Route | `bedrock/qwen3/{model_arn}` |
-| Provider Documentation | [Bedrock Imported Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-import-model.html), [Qwen3 Models](https://aws.amazon.com/about-aws/whats-new/2025/09/qwen3-models-fully-managed-amazon-bedrock/) |
-
-
-
-
-```python
-from litellm import completion
-import os
-
-response = completion(
- model="bedrock/qwen3/arn:aws:bedrock:us-east-1:086734376398:imported-model/your-qwen3-model", # bedrock/qwen3/{your-model-arn}
- messages=[{"role": "user", "content": "Tell me a joke"}],
- max_tokens=100,
- temperature=0.7
-)
-```
-
-
-
-
-
-**1. Add to config**
-
-```yaml
-model_list:
- - model_name: Qwen3-32B
- litellm_params:
- model: bedrock/qwen3/arn:aws:bedrock:us-east-1:086734376398:imported-model/your-qwen3-model
-
-```
-
-**2. Start proxy**
-
-```bash
-litellm --config /path/to/config.yaml
-
-# RUNNING at http://0.0.0.0:4000
-```
-
-**3. Test it!**
-
-```bash
-curl --location 'http://0.0.0.0:4000/chat/completions' \
- --header 'Authorization: Bearer sk-1234' \
- --header 'Content-Type: application/json' \
- --data '{
- "model": "Qwen3-32B", # 👈 the 'model_name' in config
- "messages": [
- {
- "role": "user",
- "content": "what llm are you"
- }
- ],
- }'
-```
-
-
-
-
### OpenAI GPT OSS
| Property | Details |
diff --git a/docs/my-website/docs/providers/bedrock_imported.md b/docs/my-website/docs/providers/bedrock_imported.md
new file mode 100644
index 00000000000..8b0dd721c3c
--- /dev/null
+++ b/docs/my-website/docs/providers/bedrock_imported.md
@@ -0,0 +1,369 @@
+import Tabs from '@theme/Tabs';
+import TabItem from '@theme/TabItem';
+
+# Bedrock Imported Models
+
+Bedrock Imported Models (Deepseek, Deepseek R1, Qwen, OpenAI-compatible models)
+
+### Deepseek R1
+
+This is a separate route, as the chat template is different.
+
+| Property | Details |
+|----------|---------|
+| Provider Route | `bedrock/deepseek_r1/{model_arn}` |
+| Provider Documentation | [Bedrock Imported Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-import-model.html), [Deepseek Bedrock Imported Model](https://aws.amazon.com/blogs/machine-learning/deploy-deepseek-r1-distilled-llama-models-with-amazon-bedrock-custom-model-import/) |
+
+
+
+
+```python
+from litellm import completion
+import os
+
+response = completion(
+ model="bedrock/deepseek_r1/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n", # bedrock/deepseek_r1/{your-model-arn}
+ messages=[{"role": "user", "content": "Tell me a joke"}],
+)
+```
+
+
+
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: DeepSeek-R1-Distill-Llama-70B
+ litellm_params:
+ model: bedrock/deepseek_r1/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n
+
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "DeepSeek-R1-Distill-Llama-70B", # 👈 the 'model_name' in config
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+
+### Deepseek (not R1)
+
+| Property | Details |
+|----------|---------|
+| Provider Route | `bedrock/llama/{model_arn}` |
+| Provider Documentation | [Bedrock Imported Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-import-model.html), [Deepseek Bedrock Imported Model](https://aws.amazon.com/blogs/machine-learning/deploy-deepseek-r1-distilled-llama-models-with-amazon-bedrock-custom-model-import/) |
+
+
+
+Use this route to call Bedrock Imported Models that follow the `llama` Invoke Request / Response spec
+
+
+
+
+
+```python
+from litellm import completion
+import os
+
+response = completion(
+ model="bedrock/llama/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n", # bedrock/llama/{your-model-arn}
+ messages=[{"role": "user", "content": "Tell me a joke"}],
+)
+```
+
+
+
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: DeepSeek-R1-Distill-Llama-70B
+ litellm_params:
+ model: bedrock/llama/arn:aws:bedrock:us-east-1:086734376398:imported-model/r4c4kewx2s0n
+
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "DeepSeek-R1-Distill-Llama-70B", # 👈 the 'model_name' in config
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+### Qwen3 Imported Models
+
+| Property | Details |
+|----------|---------|
+| Provider Route | `bedrock/qwen3/{model_arn}` |
+| Provider Documentation | [Bedrock Imported Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-import-model.html), [Qwen3 Models](https://aws.amazon.com/about-aws/whats-new/2025/09/qwen3-models-fully-managed-amazon-bedrock/) |
+
+
+
+
+```python
+from litellm import completion
+import os
+
+response = completion(
+ model="bedrock/qwen3/arn:aws:bedrock:us-east-1:086734376398:imported-model/your-qwen3-model", # bedrock/qwen3/{your-model-arn}
+ messages=[{"role": "user", "content": "Tell me a joke"}],
+ max_tokens=100,
+ temperature=0.7
+)
+```
+
+
+
+
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: Qwen3-32B
+ litellm_params:
+ model: bedrock/qwen3/arn:aws:bedrock:us-east-1:086734376398:imported-model/your-qwen3-model
+
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "Qwen3-32B", # 👈 the 'model_name' in config
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ }'
+```
+
+
+
+
+### OpenAI-Compatible Imported Models (Qwen 2.5 VL, etc.)
+
+Use this route for Bedrock imported models that follow the **OpenAI Chat Completions API spec**. This includes models like Qwen 2.5 VL that accept OpenAI-formatted messages with support for vision (images), tool calling, and other OpenAI features.
+
+| Property | Details |
+|----------|---------|
+| Provider Route | `bedrock/openai/{model_arn}` |
+| Provider Documentation | [Bedrock Imported Models](https://docs.aws.amazon.com/bedrock/latest/userguide/model-customization-import-model.html) |
+| Supported Features | Vision (images), tool calling, streaming, system messages |
+
+#### LiteLLMSDK Usage
+
+**Basic Usage**
+
+```python
+from litellm import completion
+
+response = completion(
+ model="bedrock/openai/arn:aws:bedrock:us-east-1:046319184608:imported-model/0m2lasirsp6z", # bedrock/openai/{your-model-arn}
+ messages=[{"role": "user", "content": "Tell me a joke"}],
+ max_tokens=300,
+ temperature=0.5
+)
+```
+
+**With Vision (Images)**
+
+```python
+import base64
+from litellm import completion
+
+# Load and encode image
+with open("image.jpg", "rb") as f:
+ image_base64 = base64.b64encode(f.read()).decode("utf-8")
+
+response = completion(
+ model="bedrock/openai/arn:aws:bedrock:us-east-1:046319184608:imported-model/0m2lasirsp6z",
+ messages=[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant that can analyze images."
+ },
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": "What's in this image?"},
+ {
+ "type": "image_url",
+ "image_url": {"url": f"data:image/jpeg;base64,{image_base64}"}
+ }
+ ]
+ }
+ ],
+ max_tokens=300,
+ temperature=0.5
+)
+```
+
+**Comparing Multiple Images**
+
+```python
+import base64
+from litellm import completion
+
+# Load images
+with open("image1.jpg", "rb") as f:
+ image1_base64 = base64.b64encode(f.read()).decode("utf-8")
+with open("image2.jpg", "rb") as f:
+ image2_base64 = base64.b64encode(f.read()).decode("utf-8")
+
+response = completion(
+ model="bedrock/openai/arn:aws:bedrock:us-east-1:046319184608:imported-model/0m2lasirsp6z",
+ messages=[
+ {
+ "role": "system",
+ "content": "You are a helpful assistant that can analyze images."
+ },
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": "Spot the difference between these two images?"},
+ {
+ "type": "image_url",
+ "image_url": {"url": f"data:image/jpeg;base64,{image1_base64}"}
+ },
+ {
+ "type": "image_url",
+ "image_url": {"url": f"data:image/jpeg;base64,{image2_base64}"}
+ }
+ ]
+ }
+ ],
+ max_tokens=300,
+ temperature=0.5
+)
+```
+
+#### LiteLLM Proxy Usage (AI Gateway)
+
+**1. Add to config**
+
+```yaml
+model_list:
+ - model_name: qwen-25vl-72b
+ litellm_params:
+ model: bedrock/openai/arn:aws:bedrock:us-east-1:046319184608:imported-model/0m2lasirsp6z
+```
+
+**2. Start proxy**
+
+```bash
+litellm --config /path/to/config.yaml
+
+# RUNNING at http://0.0.0.0:4000
+```
+
+**3. Test it!**
+
+Basic text request:
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "qwen-25vl-72b",
+ "messages": [
+ {
+ "role": "user",
+ "content": "what llm are you"
+ }
+ ],
+ "max_tokens": 300
+ }'
+```
+
+With vision (image):
+
+```bash
+curl --location 'http://0.0.0.0:4000/chat/completions' \
+ --header 'Authorization: Bearer sk-1234' \
+ --header 'Content-Type: application/json' \
+ --data '{
+ "model": "qwen-25vl-72b",
+ "messages": [
+ {
+ "role": "system",
+ "content": "You are a helpful assistant that can analyze images."
+ },
+ {
+ "role": "user",
+ "content": [
+ {"type": "text", "text": "What is in this image?"},
+ {
+ "type": "image_url",
+ "image_url": {"url": "data:image/jpeg;base64,/9j/4AAQSkZ..."}
+ }
+ ]
+ }
+ ],
+ "max_tokens": 300,
+ "temperature": 0.5
+ }'
+```
\ No newline at end of file
diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js
index 6fa5fdeced0..104c7541d67 100644
--- a/docs/my-website/sidebars.js
+++ b/docs/my-website/sidebars.js
@@ -530,6 +530,7 @@ const sidebars = {
items: [
"providers/bedrock",
"providers/bedrock_embedding",
+ "providers/bedrock_imported",
"providers/bedrock_image_gen",
"providers/bedrock_rerank",
"providers/bedrock_agentcore",
diff --git a/litellm/__init__.py b/litellm/__init__.py
index 768ba39a47a..0048f4c29cf 100644
--- a/litellm/__init__.py
+++ b/litellm/__init__.py
@@ -1225,6 +1225,9 @@ from .llms.bedrock.chat.invoke_transformations.amazon_titan_transformation impor
from .llms.bedrock.chat.invoke_transformations.base_invoke_transformation import (
AmazonInvokeConfig,
)
+from .llms.bedrock.chat.invoke_transformations.amazon_openai_transformation import (
+ AmazonBedrockOpenAIConfig,
+)
from .llms.bedrock.image.amazon_stability1_transformation import AmazonStabilityConfig
from .llms.bedrock.image.amazon_stability3_transformation import AmazonStability3Config
diff --git a/litellm/llms/bedrock/chat/invoke_transformations/amazon_openai_transformation.py b/litellm/llms/bedrock/chat/invoke_transformations/amazon_openai_transformation.py
new file mode 100644
index 00000000000..ee07b71ef15
--- /dev/null
+++ b/litellm/llms/bedrock/chat/invoke_transformations/amazon_openai_transformation.py
@@ -0,0 +1,186 @@
+"""
+Transformation for Bedrock imported models that use OpenAI Chat Completions format.
+
+Use this for models imported into Bedrock that accept the OpenAI API format.
+Model format: bedrock/openai/
+
+Example: bedrock/openai/arn:aws:bedrock:us-east-1:123456789012:imported-model/abc123
+"""
+
+from typing import TYPE_CHECKING, Any, List, Optional, Tuple, Union
+
+import httpx
+
+from litellm.llms.bedrock.base_aws_llm import BaseAWSLLM
+from litellm.llms.bedrock.common_utils import BedrockError
+from litellm.llms.openai.chat.gpt_transformation import OpenAIGPTConfig
+from litellm.types.llms.openai import AllMessageValues
+
+if TYPE_CHECKING:
+ from litellm.litellm_core_utils.litellm_logging import Logging as _LiteLLMLoggingObj
+
+ LiteLLMLoggingObj = _LiteLLMLoggingObj
+else:
+ LiteLLMLoggingObj = Any
+
+
+class AmazonBedrockOpenAIConfig(OpenAIGPTConfig, BaseAWSLLM):
+ """
+ Configuration for Bedrock imported models that use OpenAI Chat Completions format.
+
+ This class handles the transformation of requests and responses for Bedrock
+ imported models that accept the OpenAI API format directly.
+
+ Inherits from OpenAIGPTConfig to leverage standard OpenAI parameter handling
+ and response transformation, while adding Bedrock-specific URL generation
+ and AWS request signing.
+
+ Usage:
+ model = "bedrock/openai/arn:aws:bedrock:us-east-1:123456789012:imported-model/abc123"
+ """
+
+ def __init__(self, **kwargs):
+ OpenAIGPTConfig.__init__(self, **kwargs)
+ BaseAWSLLM.__init__(self, **kwargs)
+
+ @property
+ def custom_llm_provider(self) -> Optional[str]:
+ return "bedrock"
+
+ def _get_openai_model_id(self, model: str) -> str:
+ """
+ Extract the actual model ID from the LiteLLM model name.
+
+ Input format: bedrock/openai/
+ Returns:
+ """
+ # Remove bedrock/ prefix if present
+ if model.startswith("bedrock/"):
+ model = model[8:]
+
+ # Remove openai/ prefix
+ if model.startswith("openai/"):
+ model = model[7:]
+
+ return model
+
+ def get_complete_url(
+ self,
+ api_base: Optional[str],
+ api_key: Optional[str],
+ model: str,
+ optional_params: dict,
+ litellm_params: dict,
+ stream: Optional[bool] = None,
+ ) -> str:
+ """
+ Get the complete URL for the Bedrock invoke endpoint.
+
+ Uses the standard Bedrock invoke endpoint format.
+ """
+ model_id = self._get_openai_model_id(model)
+
+ # Get AWS region
+ aws_region_name = self._get_aws_region_name(
+ optional_params=optional_params, model=model
+ )
+
+ # Get runtime endpoint
+ aws_bedrock_runtime_endpoint = optional_params.get(
+ "aws_bedrock_runtime_endpoint", None
+ )
+ endpoint_url, proxy_endpoint_url = self.get_runtime_endpoint(
+ api_base=api_base,
+ aws_bedrock_runtime_endpoint=aws_bedrock_runtime_endpoint,
+ aws_region_name=aws_region_name,
+ )
+
+ # Build the invoke URL
+ if stream:
+ endpoint_url = f"{endpoint_url}/model/{model_id}/invoke-with-response-stream"
+ else:
+ endpoint_url = f"{endpoint_url}/model/{model_id}/invoke"
+
+ return endpoint_url
+
+ def sign_request(
+ self,
+ headers: dict,
+ optional_params: dict,
+ request_data: dict,
+ api_base: str,
+ api_key: Optional[str] = None,
+ model: Optional[str] = None,
+ stream: Optional[bool] = None,
+ fake_stream: Optional[bool] = None,
+ ) -> Tuple[dict, Optional[bytes]]:
+ """
+ Sign the request using AWS Signature Version 4.
+ """
+ return self._sign_request(
+ service_name="bedrock",
+ headers=headers,
+ optional_params=optional_params,
+ request_data=request_data,
+ api_base=api_base,
+ api_key=api_key,
+ model=model,
+ stream=stream,
+ fake_stream=fake_stream,
+ )
+
+ def transform_request(
+ self,
+ model: str,
+ messages: List[AllMessageValues],
+ optional_params: dict,
+ litellm_params: dict,
+ headers: dict,
+ ) -> dict:
+ """
+ Transform the request to OpenAI Chat Completions format for Bedrock imported models.
+
+ Removes AWS-specific params and stream param (handled separately in URL),
+ then delegates to parent class for standard OpenAI request transformation.
+ """
+ # Remove stream from optional_params as it's handled separately in URL
+ optional_params.pop("stream", None)
+
+ # Remove AWS-specific params that shouldn't be in the request body
+ inference_params = {
+ k: v
+ for k, v in optional_params.items()
+ if k not in self.aws_authentication_params
+ }
+
+ # Use parent class transform_request for OpenAI format
+ return super().transform_request(
+ model=self._get_openai_model_id(model),
+ messages=messages,
+ optional_params=inference_params,
+ litellm_params=litellm_params,
+ headers=headers,
+ )
+
+ def validate_environment(
+ self,
+ headers: dict,
+ model: str,
+ messages: List[AllMessageValues],
+ optional_params: dict,
+ litellm_params: dict,
+ api_key: Optional[str] = None,
+ api_base: Optional[str] = None,
+ ) -> dict:
+ """
+ Validate the environment and return headers.
+
+ For Bedrock, we don't need Bearer token auth since we use AWS SigV4.
+ """
+ return headers
+
+ def get_error_class(
+ self, error_message: str, status_code: int, headers: Union[dict, httpx.Headers]
+ ) -> BedrockError:
+ """Return the appropriate error class for Bedrock."""
+ return BedrockError(status_code=status_code, message=error_message)
diff --git a/litellm/llms/bedrock/common_utils.py b/litellm/llms/bedrock/common_utils.py
index baaec996535..35d3d736a1c 100644
--- a/litellm/llms/bedrock/common_utils.py
+++ b/litellm/llms/bedrock/common_utils.py
@@ -403,6 +403,9 @@ class BedrockModelInfo(BaseLLMModelInfo):
if model.startswith("invoke/"):
model = model.split("/", 1)[1]
+ if model.startswith("openai/"):
+ model = model.split("/", 1)[1]
+
return model
@staticmethod
@@ -446,12 +449,12 @@ class BedrockModelInfo(BaseLLMModelInfo):
@staticmethod
def get_bedrock_route(
model: str,
- ) -> Literal["converse", "invoke", "converse_like", "agent", "agentcore", "async_invoke"]:
+ ) -> Literal["converse", "invoke", "converse_like", "agent", "agentcore", "async_invoke", "openai"]:
"""
Get the bedrock route for the given model.
"""
route_mappings: Dict[
- str, Literal["invoke", "converse_like", "converse", "agent", "agentcore", "async_invoke"]
+ str, Literal["invoke", "converse_like", "converse", "agent", "agentcore", "async_invoke", "openai"]
] = {
"invoke/": "invoke",
"converse_like/": "converse_like",
@@ -459,6 +462,7 @@ class BedrockModelInfo(BaseLLMModelInfo):
"agent/": "agent",
"agentcore/": "agentcore",
"async_invoke/": "async_invoke",
+ "openai/": "openai",
}
# Check explicit routes first
@@ -517,6 +521,14 @@ class BedrockModelInfo(BaseLLMModelInfo):
"""
return "async_invoke/" in model
+ @staticmethod
+ def _explicit_openai_route(model: str) -> bool:
+ """
+ Check if the model is an explicit openai route.
+ Used for Bedrock imported models that use OpenAI Chat Completions format.
+ """
+ return "openai/" in model
+
@staticmethod
def get_bedrock_provider_config_for_messages_api(
model: str,
@@ -566,6 +578,8 @@ def get_bedrock_chat_config(model: str):
# Handle explicit routes first
if bedrock_route == "converse" or bedrock_route == "converse_like":
return litellm.AmazonConverseConfig()
+ elif bedrock_route == "openai":
+ return litellm.AmazonBedrockOpenAIConfig()
elif bedrock_route == "agent":
from litellm.llms.bedrock.chat.invoke_agent.transformation import (
AmazonInvokeAgentConfig,
diff --git a/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py b/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py
index daee50f30cc..23b9da4714c 100644
--- a/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py
+++ b/litellm/proxy/guardrails/guardrail_hooks/prompt_security/prompt_security.py
@@ -1,13 +1,18 @@
-import os
-import re
import asyncio
import base64
+import os
+import re
from typing import TYPE_CHECKING, Any, AsyncGenerator, Optional, Type, Union
+
from fastapi import HTTPException
+
from litellm import DualCache
from litellm._logging import verbose_proxy_logger
from litellm.integrations.custom_guardrail import CustomGuardrail
-from litellm.llms.custom_httpx.http_handler import get_async_httpx_client, httpxSpecialProvider
+from litellm.llms.custom_httpx.http_handler import (
+ get_async_httpx_client,
+ httpxSpecialProvider,
+)
from litellm.proxy._types import UserAPIKeyAuth
from litellm.types.utils import (
Choices,
@@ -15,7 +20,7 @@ from litellm.types.utils import (
EmbeddingResponse,
ImageResponse,
ModelResponse,
- ModelResponseStream
+ ModelResponseStream,
)
if TYPE_CHECKING:
@@ -267,8 +272,10 @@ class PromptSecurityGuardrail(CustomGuardrail):
content = msg.get('content', '')
# Handle both string and list content types
if isinstance(content, str):
- if content.startswith('### '): return False
- if '"follow_ups": [' in content: return False
+ if content.startswith('### '):
+ return False
+ if '"follow_ups": [' in content:
+ return False
return True
messages = list(filter(lambda msg: good_msg(msg), messages))
diff --git a/litellm/proxy/proxy_config.yaml b/litellm/proxy/proxy_config.yaml
index 014bcdc1670..26e867dc33e 100644
--- a/litellm/proxy/proxy_config.yaml
+++ b/litellm/proxy/proxy_config.yaml
@@ -1,22 +1,7 @@
model_list:
- - model_name: aws/anthropic/bedrock-claude-3-5-sonnet-v1
+ - model_name: qwen-25vl-72b
litellm_params:
- model: bedrock/us.anthropic.claude-3-5-sonnet-20240620-v1:0
- aws_region_name: us-east-1
- custom_llm_provider: bedrock
- - model_name: aws/anthropic/bedrock-claude-3-5-sonnet-v1
- litellm_params:
- model: bedrock/us.anthropic.claude-3-5-sonnet-20240620-v1:0
- aws_region_name: us-west-2
- custom_llm_provider: bedrock
- - model_name: bedrock/*
- litellm_params:
- model: bedrock/*
- custom_llm_provider: bedrock
- aws_region_name: us-west-2
- - model_name: runwayml/*
- litellm_params:
- model: runwayml/*
+ model: bedrock/openai/arn:aws:bedrock:us-east-1:046319184608:imported-model/0m2lasirsp6z
diff --git a/litellm/utils.py b/litellm/utils.py
index f683a59f501..1b4df689959 100644
--- a/litellm/utils.py
+++ b/litellm/utils.py
@@ -3719,7 +3719,17 @@ def get_optional_params( # noqa: PLR0915
else False
),
)
-
+ elif bedrock_route == "openai":
+ optional_params = litellm.AmazonBedrockOpenAIConfig().map_openai_params(
+ model=model,
+ non_default_params=non_default_params,
+ optional_params=optional_params,
+ drop_params=(
+ drop_params
+ if drop_params is not None and isinstance(drop_params, bool)
+ else False
+ ),
+ )
elif "anthropic" in bedrock_base_model and bedrock_route == "invoke":
if bedrock_base_model.startswith("anthropic.claude-3"):
optional_params = (
diff --git a/tests/llm_translation/test_bedrock_completion.py b/tests/llm_translation/test_bedrock_completion.py
index 9242950daac..f43e939c681 100644
--- a/tests/llm_translation/test_bedrock_completion.py
+++ b/tests/llm_translation/test_bedrock_completion.py
@@ -3434,3 +3434,100 @@ async def test_bedrock_streaming_passthrough_test1(monkeypatch):
print(mock_callback.call_args.kwargs.keys())
assert "standard_logging_object" in mock_callback.call_args.kwargs["kwargs"]
assert "response_cost" in mock_callback.call_args.kwargs["kwargs"]
+
+
+def test_bedrock_openai_imported_model():
+ """
+ Test that Bedrock imported models using OpenAI format work correctly.
+
+ This test validates:
+ 1. The request body follows OpenAI Chat Completions format
+ 2. The URL is correctly constructed for Bedrock invoke endpoint
+ 3. Messages with system, user roles and image_url content are preserved
+ """
+ from litellm.llms.custom_httpx.http_handler import HTTPHandler
+
+ client = HTTPHandler()
+
+ # Sample base64 image data (truncated for test)
+ sample_base64 = "iVBORw0KGgoAAAANSUhEUgAAAAEAAAABCAYAAAAfFcSJAAAADUlEQVR42mNk+M9QDwADhgGAWjR9awAAAABJRU5ErkJggg=="
+
+ messages = [
+ {
+ "role": "system",
+ "content": "You are a helpful assistant that can analyze images.",
+ },
+ {
+ "role": "user",
+ "content": [
+ {
+ "type": "text",
+ "text": "Spot the difference between the two images?",
+ },
+ {
+ "type": "image_url",
+ "image_url": {"url": f"data:image/jpeg;base64,{sample_base64}"},
+ },
+ {
+ "type": "image_url",
+ "image_url": {"url": f"data:image/jpeg;base64,{sample_base64}"},
+ },
+ ],
+ },
+ ]
+
+ with patch.object(client, "post") as mock_post:
+ try:
+ response = completion(
+ model="bedrock/openai/arn:aws:bedrock:us-east-1:117159858402:imported-model/m4gc1mrfuddy",
+ messages=messages,
+ max_tokens=300,
+ temperature=0.5,
+ client=client,
+ )
+ except Exception as e:
+ print(f"Exception (expected during mock): {e}")
+
+ mock_post.assert_called_once()
+
+ # Validate URL
+ url = mock_post.call_args.kwargs["url"]
+ print(f"URL: {url}")
+ assert "bedrock-runtime.us-east-1.amazonaws.com" in url
+ assert "arn:aws:bedrock:us-east-1:117159858402:imported-model/m4gc1mrfuddy" in url
+ assert "/invoke" in url
+
+ # Validate request body follows OpenAI format
+ request_body = json.loads(mock_post.call_args.kwargs["data"])
+ print(f"Request body: {json.dumps(request_body, indent=2)}")
+
+ # Check messages structure
+ assert "messages" in request_body
+ assert len(request_body["messages"]) == 2
+
+ # Check system message
+ system_msg = request_body["messages"][0]
+ assert system_msg["role"] == "system"
+ assert "helpful assistant" in system_msg["content"]
+
+ # Check user message with image content
+ user_msg = request_body["messages"][1]
+ assert user_msg["role"] == "user"
+ assert isinstance(user_msg["content"], list)
+ assert len(user_msg["content"]) == 3
+
+ # Check text content
+ assert user_msg["content"][0]["type"] == "text"
+ assert "Spot the difference" in user_msg["content"][0]["text"]
+
+ # Check image_url content
+ assert user_msg["content"][1]["type"] == "image_url"
+ assert "image_url" in user_msg["content"][1]
+ assert user_msg["content"][1]["image_url"]["url"].startswith("data:image/jpeg;base64,")
+
+ assert user_msg["content"][2]["type"] == "image_url"
+ assert "image_url" in user_msg["content"][2]
+
+ # Check max_tokens and temperature
+ assert request_body["max_tokens"] == 300
+ assert request_body["temperature"] == 0.5