diff --git a/docs/my-website/docs/providers/watsonx.md b/docs/my-website/docs/providers/watsonx.md deleted file mode 100644 index 23d8d259ac..0000000000 --- a/docs/my-website/docs/providers/watsonx.md +++ /dev/null @@ -1,287 +0,0 @@ -import Tabs from '@theme/Tabs'; -import TabItem from '@theme/TabItem'; - -# IBM watsonx.ai - -LiteLLM supports all IBM [watsonx.ai](https://watsonx.ai/) foundational models and embeddings. - -## Environment Variables -```python -os.environ["WATSONX_URL"] = "" # (required) Base URL of your WatsonX instance -# (required) either one of the following: -os.environ["WATSONX_APIKEY"] = "" # IBM cloud API key -os.environ["WATSONX_TOKEN"] = "" # IAM auth token -# optional - can also be passed as params to completion() or embedding() -os.environ["WATSONX_PROJECT_ID"] = "" # Project ID of your WatsonX instance -os.environ["WATSONX_DEPLOYMENT_SPACE_ID"] = "" # ID of your deployment space to use deployed models -os.environ["WATSONX_ZENAPIKEY"] = "" # Zen API key (use for long-term api token) -``` - -See [here](https://cloud.ibm.com/apidocs/watsonx-ai#api-authentication) for more information on how to get an access token to authenticate to watsonx.ai. - -## Usage - - - Open In Colab - - -```python -import os -from litellm import completion - -os.environ["WATSONX_URL"] = "" -os.environ["WATSONX_APIKEY"] = "" - -## Call WATSONX `/text/chat` endpoint - supports function calling -response = completion( - model="watsonx/meta-llama/llama-3-1-8b-instruct", - messages=[{ "content": "what is your favorite colour?","role": "user"}], - project_id="" # or pass with os.environ["WATSONX_PROJECT_ID"] -) - -## Call WATSONX `/text/generation` endpoint - not all models support /chat route. -response = completion( - model="watsonx/ibm/granite-13b-chat-v2", - messages=[{ "content": "what is your favorite colour?","role": "user"}], - project_id="" -) -``` - -## Usage - Streaming -```python -import os -from litellm import completion - -os.environ["WATSONX_URL"] = "" -os.environ["WATSONX_APIKEY"] = "" -os.environ["WATSONX_PROJECT_ID"] = "" - -response = completion( - model="watsonx/meta-llama/llama-3-1-8b-instruct", - messages=[{ "content": "what is your favorite colour?","role": "user"}], - stream=True -) -for chunk in response: - print(chunk) -``` - -#### Example Streaming Output Chunk -```json -{ - "choices": [ - { - "finish_reason": null, - "index": 0, - "delta": { - "content": "I don't have a favorite color, but I do like the color blue. What's your favorite color?" - } - } - ], - "created": null, - "model": "watsonx/ibm/granite-13b-chat-v2", - "usage": { - "prompt_tokens": null, - "completion_tokens": null, - "total_tokens": null - } -} -``` - -## Usage - Models in deployment spaces - -Models that have been deployed to a deployment space (e.g.: tuned models) can be called using the `deployment/` format (where `` is the ID of the deployed model in your deployment space). - -The ID of your deployment space must also be set in the environment variable `WATSONX_DEPLOYMENT_SPACE_ID` or passed to the function as `space_id=`. - -```python -import litellm -response = litellm.completion( - model="watsonx/deployment/", - messages=[{"content": "Hello, how are you?", "role": "user"}], - space_id="" -) -``` - -## Usage - Embeddings - -LiteLLM also supports making requests to IBM watsonx.ai embedding models. The credential needed for this is the same as for completion. - -```python -from litellm import embedding - -response = embedding( - model="watsonx/ibm/slate-30m-english-rtrvr", - input=["What is the capital of France?"], - project_id="" -) -print(response) -# EmbeddingResponse(model='ibm/slate-30m-english-rtrvr', data=[{'object': 'embedding', 'index': 0, 'embedding': [-0.037463713, -0.02141933, -0.02851813, 0.015519324, ..., -0.0021367231, -0.01704561, -0.001425816, 0.0035238306]}], object='list', usage=Usage(prompt_tokens=8, total_tokens=8)) -``` - -## OpenAI Proxy Usage - -Here's how to call IBM watsonx.ai with the LiteLLM Proxy Server - -### 1. Save keys in your environment - -```bash -export WATSONX_URL="" -export WATSONX_APIKEY="" -export WATSONX_PROJECT_ID="" -``` - -### 2. Start the proxy - - - - -```bash -$ litellm --model watsonx/meta-llama/llama-3-8b-instruct - -# Server running on http://0.0.0.0:4000 -``` - - - - -```yaml -model_list: - - model_name: llama-3-8b - litellm_params: - # all params accepted by litellm.completion() - model: watsonx/meta-llama/llama-3-8b-instruct - api_key: "os.environ/WATSONX_API_KEY" # does os.getenv("WATSONX_API_KEY") -``` - - - -### 3. Test it - - - - - -```shell -curl --location 'http://0.0.0.0:4000/chat/completions' \ ---header 'Content-Type: application/json' \ ---data ' { - "model": "llama-3-8b", - "messages": [ - { - "role": "user", - "content": "what is your favorite colour?" - } - ] - } -' -``` - - - -```python -import openai -client = openai.OpenAI( - api_key="anything", - base_url="http://0.0.0.0:4000" -) - -# request sent to model set on litellm proxy, `litellm --model` -response = client.chat.completions.create(model="llama-3-8b", messages=[ - { - "role": "user", - "content": "what is your favorite colour?" - } -]) - -print(response) - -``` - - - -```python -from langchain.chat_models import ChatOpenAI -from langchain.prompts.chat import ( - ChatPromptTemplate, - HumanMessagePromptTemplate, - SystemMessagePromptTemplate, -) -from langchain.schema import HumanMessage, SystemMessage - -chat = ChatOpenAI( - openai_api_base="http://0.0.0.0:4000", # set openai_api_base to the LiteLLM Proxy - model = "llama-3-8b", - temperature=0.1 -) - -messages = [ - SystemMessage( - content="You are a helpful assistant that im using to make a test request to." - ), - HumanMessage( - content="test from litellm. tell me why it's amazing in 1 sentence" - ), -] -response = chat(messages) - -print(response) -``` - - - - -## Authentication - -### Passing credentials as parameters - -You can also pass the credentials as parameters to the completion and embedding functions. - -```python -import os -from litellm import completion - -response = completion( - model="watsonx/ibm/granite-13b-chat-v2", - messages=[{ "content": "What is your favorite color?","role": "user"}], - url="", - api_key="", - project_id="" -) -``` - - -## Supported IBM watsonx.ai Models - -Here are some examples of models available in IBM watsonx.ai that you can use with LiteLLM: - -| Mode Name | Command | -|------------------------------------|------------------------------------------------------------------------------------------| -| Flan T5 XXL | `completion(model=watsonx/google/flan-t5-xxl, messages=messages)` | -| Flan Ul2 | `completion(model=watsonx/google/flan-ul2, messages=messages)` | -| Mt0 XXL | `completion(model=watsonx/bigscience/mt0-xxl, messages=messages)` | -| Gpt Neox | `completion(model=watsonx/eleutherai/gpt-neox-20b, messages=messages)` | -| Mpt 7B Instruct2 | `completion(model=watsonx/ibm/mpt-7b-instruct2, messages=messages)` | -| Starcoder | `completion(model=watsonx/bigcode/starcoder, messages=messages)` | -| Llama 2 70B Chat | `completion(model=watsonx/meta-llama/llama-2-70b-chat, messages=messages)` | -| Llama 2 13B Chat | `completion(model=watsonx/meta-llama/llama-2-13b-chat, messages=messages)` | -| Granite 13B Instruct | `completion(model=watsonx/ibm/granite-13b-instruct-v1, messages=messages)` | -| Granite 13B Chat | `completion(model=watsonx/ibm/granite-13b-chat-v1, messages=messages)` | -| Flan T5 XL | `completion(model=watsonx/google/flan-t5-xl, messages=messages)` | -| Granite 13B Chat V2 | `completion(model=watsonx/ibm/granite-13b-chat-v2, messages=messages)` | -| Granite 13B Instruct V2 | `completion(model=watsonx/ibm/granite-13b-instruct-v2, messages=messages)` | -| Elyza Japanese Llama 2 7B Instruct | `completion(model=watsonx/elyza/elyza-japanese-llama-2-7b-instruct, messages=messages)` | -| Mixtral 8X7B Instruct V01 Q | `completion(model=watsonx/ibm-mistralai/mixtral-8x7b-instruct-v01-q, messages=messages)` | - - -For a list of all available models in watsonx.ai, see [here](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx&locale=en&audience=wdp). - - -## Supported IBM watsonx.ai Embedding Models - -| Model Name | Function Call | -|------------|------------------------------------------------------------------------| -| Slate 30m | `embedding(model="watsonx/ibm/slate-30m-english-rtrvr", input=input)` | -| Slate 125m | `embedding(model="watsonx/ibm/slate-125m-english-rtrvr", input=input)` | - - -For a list of all available embedding models in watsonx.ai, see [here](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models-embed.html?context=wx). \ No newline at end of file diff --git a/docs/my-website/docs/providers/watsonx/audio_transcription.md b/docs/my-website/docs/providers/watsonx/audio_transcription.md new file mode 100644 index 0000000000..37b4bb438a --- /dev/null +++ b/docs/my-website/docs/providers/watsonx/audio_transcription.md @@ -0,0 +1,57 @@ +# WatsonX Audio Transcription + +## Overview + +| Property | Details | +|----------|---------| +| Description | WatsonX audio transcription using Whisper models for speech-to-text | +| Provider Route on LiteLLM | `watsonx/` | +| Supported Operations | `/v1/audio/transcriptions` | +| Link to Provider Doc | [IBM WatsonX.ai ↗](https://www.ibm.com/watsonx) | + +## Quick Start + +### **LiteLLM SDK** + +```python showLineNumbers title="transcription.py" +import litellm + +response = litellm.transcription( + model="watsonx/whisper-large-v3-turbo", + file=open("audio.mp3", "rb"), + api_base="https://us-south.ml.cloud.ibm.com", + api_key="your-api-key", + project_id="your-project-id" +) +print(response.text) +``` + +### **LiteLLM Proxy** + +```yaml showLineNumbers title="config.yaml" +model_list: + - model_name: whisper-large-v3-turbo + litellm_params: + model: watsonx/whisper-large-v3-turbo + api_key: os.environ/WATSONX_APIKEY + api_base: os.environ/WATSONX_URL + project_id: os.environ/WATSONX_PROJECT_ID +``` + +```bash title="Request" +curl http://localhost:4000/v1/audio/transcriptions \ + -H "Authorization: Bearer sk-1234" \ + -F file="@audio.mp3" \ + -F model="whisper-large-v3-turbo" +``` + +## Supported Parameters + +| Parameter | Type | Description | +|-----------|------|-------------| +| `model` | string | Model ID (e.g., `watsonx/whisper-large-v3-turbo`) | +| `file` | file | Audio file to transcribe | +| `language` | string | Language code (e.g., `en`) | +| `prompt` | string | Optional prompt to guide transcription | +| `temperature` | float | Sampling temperature (0-1) | +| `response_format` | string | `json`, `text`, `srt`, `verbose_json`, `vtt` | diff --git a/docs/my-website/docs/providers/watsonx/index.md b/docs/my-website/docs/providers/watsonx/index.md new file mode 100644 index 0000000000..279d2d1024 --- /dev/null +++ b/docs/my-website/docs/providers/watsonx/index.md @@ -0,0 +1,177 @@ +import Tabs from '@theme/Tabs'; +import TabItem from '@theme/TabItem'; + +# IBM watsonx.ai + +LiteLLM supports all IBM [watsonx.ai](https://watsonx.ai/) foundational models and embeddings. + +## Environment Variables +```python +os.environ["WATSONX_URL"] = "" # (required) Base URL of your WatsonX instance +# (required) either one of the following: +os.environ["WATSONX_APIKEY"] = "" # IBM cloud API key +os.environ["WATSONX_TOKEN"] = "" # IAM auth token +# optional - can also be passed as params to completion() or embedding() +os.environ["WATSONX_PROJECT_ID"] = "" # Project ID of your WatsonX instance +os.environ["WATSONX_DEPLOYMENT_SPACE_ID"] = "" # ID of your deployment space to use deployed models +os.environ["WATSONX_ZENAPIKEY"] = "" # Zen API key (use for long-term api token) +``` + +See [here](https://cloud.ibm.com/apidocs/watsonx-ai#api-authentication) for more information on how to get an access token to authenticate to watsonx.ai. + +## Usage + + + Open In Colab + + +```python showLineNumbers title="Chat Completion" +import os +from litellm import completion + +os.environ["WATSONX_URL"] = "" +os.environ["WATSONX_APIKEY"] = "" + +response = completion( + model="watsonx/meta-llama/llama-3-1-8b-instruct", + messages=[{ "content": "what is your favorite colour?","role": "user"}], + project_id="" +) +``` + +## Usage - Streaming +```python showLineNumbers title="Streaming" +import os +from litellm import completion + +os.environ["WATSONX_URL"] = "" +os.environ["WATSONX_APIKEY"] = "" +os.environ["WATSONX_PROJECT_ID"] = "" + +response = completion( + model="watsonx/meta-llama/llama-3-1-8b-instruct", + messages=[{ "content": "what is your favorite colour?","role": "user"}], + stream=True +) +for chunk in response: + print(chunk) +``` + +## Usage - Models in deployment spaces + +Models deployed to a deployment space (e.g.: tuned models) can be called using the `deployment/` format. + +```python showLineNumbers title="Deployment Space" +import litellm + +response = litellm.completion( + model="watsonx/deployment/", + messages=[{"content": "Hello, how are you?", "role": "user"}], + space_id="" +) +``` + +## Usage - Embeddings + +```python showLineNumbers title="Embeddings" +from litellm import embedding + +response = embedding( + model="watsonx/ibm/slate-30m-english-rtrvr", + input=["What is the capital of France?"], + project_id="" +) +``` + +## LiteLLM Proxy Usage + +### 1. Save keys in your environment + +```bash +export WATSONX_URL="" +export WATSONX_APIKEY="" +export WATSONX_PROJECT_ID="" +``` + +### 2. Start the proxy + + + + +```bash +$ litellm --model watsonx/meta-llama/llama-3-8b-instruct +``` + + + + +```yaml showLineNumbers title="config.yaml" +model_list: + - model_name: llama-3-8b + litellm_params: + model: watsonx/meta-llama/llama-3-8b-instruct + api_key: "os.environ/WATSONX_API_KEY" +``` + + + +### 3. Test it + + + + + +```shell +curl --location 'http://0.0.0.0:4000/chat/completions' \ +--header 'Content-Type: application/json' \ +--data '{ + "model": "llama-3-8b", + "messages": [ + { + "role": "user", + "content": "what is your favorite colour?" + } + ] + }' +``` + + + +```python showLineNumbers +import openai + +client = openai.OpenAI( + api_key="anything", + base_url="http://0.0.0.0:4000" +) + +response = client.chat.completions.create( + model="llama-3-8b", + messages=[{"role": "user", "content": "what is your favorite colour?"}] +) +print(response) +``` + + + + +## Supported Models + +| Model Name | Command | +|------------------------------------|------------------------------------------------------------------------------------------| +| Llama 3.1 8B Instruct | `completion(model="watsonx/meta-llama/llama-3-1-8b-instruct", messages=messages)` | +| Llama 2 70B Chat | `completion(model="watsonx/meta-llama/llama-2-70b-chat", messages=messages)` | +| Granite 13B Chat V2 | `completion(model="watsonx/ibm/granite-13b-chat-v2", messages=messages)` | +| Mixtral 8X7B Instruct | `completion(model="watsonx/ibm-mistralai/mixtral-8x7b-instruct-v01-q", messages=messages)` | + +For all available models, see [watsonx.ai documentation](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models.html?context=wx). + +## Supported Embedding Models + +| Model Name | Function Call | +|------------|------------------------------------------------------------------------| +| Slate 30m | `embedding(model="watsonx/ibm/slate-30m-english-rtrvr", input=input)` | +| Slate 125m | `embedding(model="watsonx/ibm/slate-125m-english-rtrvr", input=input)` | + +For all available embedding models, see [watsonx.ai embedding documentation](https://dataplatform.cloud.ibm.com/docs/content/wsj/analyze-data/fm-models-embed.html?context=wx). + diff --git a/docs/my-website/sidebars.js b/docs/my-website/sidebars.js index e5eba82e32..b40a533337 100644 --- a/docs/my-website/sidebars.js +++ b/docs/my-website/sidebars.js @@ -633,7 +633,14 @@ const sidebars = { "providers/volcano", "providers/voyage", "providers/wandb_inference", - "providers/watsonx", + { + type: "category", + label: "WatsonX", + items: [ + "providers/watsonx/index", + "providers/watsonx/audio_transcription", + ] + }, "providers/xai", "providers/xinference", ], diff --git a/litellm/__init__.py b/litellm/__init__.py index 6431d78a53..3a715ff1df 100644 --- a/litellm/__init__.py +++ b/litellm/__init__.py @@ -1357,6 +1357,9 @@ from .llms.azure.chat.o_series_transformation import AzureOpenAIO1Config from .llms.watsonx.completion.transformation import IBMWatsonXAIConfig from .llms.watsonx.chat.transformation import IBMWatsonXChatConfig from .llms.watsonx.embed.transformation import IBMWatsonXEmbeddingConfig +from .llms.watsonx.audio_transcription.transformation import ( + IBMWatsonXAudioTranscriptionConfig, +) from .llms.github_copilot.chat.transformation import GithubCopilotConfig from .llms.github_copilot.responses.transformation import ( GithubCopilotResponsesAPIConfig, diff --git a/litellm/llms/watsonx/audio_transcription/__init__.py b/litellm/llms/watsonx/audio_transcription/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/litellm/llms/watsonx/audio_transcription/transformation.py b/litellm/llms/watsonx/audio_transcription/transformation.py new file mode 100644 index 0000000000..8c8324cb72 --- /dev/null +++ b/litellm/llms/watsonx/audio_transcription/transformation.py @@ -0,0 +1,78 @@ +""" +Translates from OpenAI's `/v1/audio/transcriptions` to IBM WatsonX's `/ml/v1/audio/transcriptions` + +WatsonX follows the OpenAI spec for audio transcription. +""" + +from typing import List, Optional + +import litellm +from litellm.types.llms.openai import OpenAIAudioTranscriptionOptionalParams + +from ...openai.transcriptions.whisper_transformation import ( + OpenAIWhisperAudioTranscriptionConfig, +) +from ..common_utils import IBMWatsonXMixin, _get_api_params + + +class IBMWatsonXAudioTranscriptionConfig( + IBMWatsonXMixin, OpenAIWhisperAudioTranscriptionConfig +): + """ + IBM WatsonX Audio Transcription Config + + WatsonX follows the OpenAI spec for audio transcription, so this class + inherits from OpenAIWhisperAudioTranscriptionConfig and uses IBMWatsonXMixin + for authentication and URL construction. + """ + + def get_supported_openai_params( + self, model: str + ) -> List[OpenAIAudioTranscriptionOptionalParams]: + """ + Get the supported OpenAI params for WatsonX audio transcription. + """ + return [ + "language", + "prompt", + "response_format", + "temperature", + "timestamp_granularities", + ] + + 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: + """ + Construct the complete URL for WatsonX audio transcription. + + URL format: {api_base}/ml/v1/audio/transcriptions?version={version}&project_id={project_id} + """ + # Get base URL + url = self._get_base_url(api_base=api_base) + url = url.rstrip("/") + + # Add the audio transcription endpoint + url = f"{url}/ml/v1/audio/transcriptions" + + # Get API params for project_id + api_params = _get_api_params(params=optional_params.copy()) + + # Add version parameter + api_version = optional_params.pop( + "api_version", None + ) or litellm.WATSONX_DEFAULT_API_VERSION + url = f"{url}?version={api_version}" + + # Add project_id parameter + project_id = api_params.get("project_id") + if project_id: + url = f"{url}&project_id={project_id}" + + return url diff --git a/litellm/model_prices_and_context_window_backup.json b/litellm/model_prices_and_context_window_backup.json index 1b1ebf936a..243b5318a3 100644 --- a/litellm/model_prices_and_context_window_backup.json +++ b/litellm/model_prices_and_context_window_backup.json @@ -26209,6 +26209,15 @@ "supports_parallel_function_calling": false, "supports_vision": false }, + "watsonx/whisper-large-v3-turbo": { + "input_cost_per_second": 0.0001, + "output_cost_per_second": 0.0001, + "litellm_provider": "watsonx", + "mode": "audio_transcription", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ] + }, "whisper-1": { "input_cost_per_second": 0.0001, "litellm_provider": "openai", diff --git a/litellm/utils.py b/litellm/utils.py index 1d8f40af41..053368a3e0 100644 --- a/litellm/utils.py +++ b/litellm/utils.py @@ -7380,6 +7380,12 @@ class ProviderConfigManager: ) return HostedVLLMAudioTranscriptionConfig() + elif litellm.LlmProviders.WATSONX == provider: + from litellm.llms.watsonx.audio_transcription.transformation import ( + IBMWatsonXAudioTranscriptionConfig, + ) + + return IBMWatsonXAudioTranscriptionConfig() return None @staticmethod diff --git a/model_prices_and_context_window.json b/model_prices_and_context_window.json index 1b1ebf936a..243b5318a3 100644 --- a/model_prices_and_context_window.json +++ b/model_prices_and_context_window.json @@ -26209,6 +26209,15 @@ "supports_parallel_function_calling": false, "supports_vision": false }, + "watsonx/whisper-large-v3-turbo": { + "input_cost_per_second": 0.0001, + "output_cost_per_second": 0.0001, + "litellm_provider": "watsonx", + "mode": "audio_transcription", + "supported_endpoints": [ + "/v1/audio/transcriptions" + ] + }, "whisper-1": { "input_cost_per_second": 0.0001, "litellm_provider": "openai", diff --git a/provider_endpoints_support.json b/provider_endpoints_support.json index ff5017c9fc..e05d6d8d00 100644 --- a/provider_endpoints_support.json +++ b/provider_endpoints_support.json @@ -855,7 +855,7 @@ "responses": true, "embeddings": true, "image_generations": false, - "audio_transcriptions": false, + "audio_transcriptions": true, "audio_speech": false, "moderations": false, "batches": false, diff --git a/tests/test_litellm/llms/watsonx/__init__.py b/tests/test_litellm/llms/watsonx/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_litellm/llms/watsonx/audio_transcription/__init__.py b/tests/test_litellm/llms/watsonx/audio_transcription/__init__.py new file mode 100644 index 0000000000..e69de29bb2 diff --git a/tests/test_litellm/llms/watsonx/audio_transcription/test_watsonx_audio_transcription_transformation.py b/tests/test_litellm/llms/watsonx/audio_transcription/test_watsonx_audio_transcription_transformation.py new file mode 100644 index 0000000000..84a9d25d98 --- /dev/null +++ b/tests/test_litellm/llms/watsonx/audio_transcription/test_watsonx_audio_transcription_transformation.py @@ -0,0 +1,102 @@ +""" +Tests for IBM WatsonX Audio Transcription. + +Validates that litellm.transcription transforms requests correctly for WatsonX. +""" + +import os +import sys +from unittest.mock import AsyncMock, MagicMock, patch + +import pytest + +sys.path.insert(0, os.path.abspath("../../../../..")) + +import litellm + + +class TestWatsonXAudioTranscription: + """Tests for WatsonX audio transcription via litellm.transcription.""" + + @pytest.mark.asyncio + async def test_watsonx_transcription_url_and_headers(self): + """ + Test that litellm.transcription sends request to correct WatsonX URL with proper headers. + """ + captured_request = {} + + async def mock_post(*args, **kwargs): + captured_request["url"] = str(kwargs.get("url", args[0] if args else None)) + captured_request["headers"] = kwargs.get("headers", {}) + captured_request["data"] = kwargs.get("data", {}) + + mock_response = MagicMock() + mock_response.json.return_value = { + "text": "test transcription", + "duration": 1.0, + } + mock_response.status_code = 200 + return mock_response + + with patch("litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", new=mock_post): + try: + await litellm.atranscription( + model="watsonx/whisper-large-v3-turbo", + file=b"fake_audio_data", + api_base="https://us-south.ml.cloud.ibm.com", + api_key="test-api-key", + project_id="test-project-123", + token="test-bearer-token", + ) + except Exception: + pass # We just want to capture the request + + # Validate URL contains WatsonX audio transcription endpoint + assert "/ml/v1/audio/transcriptions" in captured_request["url"] + assert "version=" in captured_request["url"] + assert "project_id=test-project-123" in captured_request["url"] + + # Validate headers contain WatsonX auth + assert "Authorization" in captured_request["headers"] + assert "Bearer test-bearer-token" in captured_request["headers"]["Authorization"] + + @pytest.mark.asyncio + async def test_watsonx_transcription_request_body(self): + """ + Test that litellm.transcription sends correct request body for WatsonX. + """ + captured_request = {} + + async def mock_post(*args, **kwargs): + captured_request["data"] = kwargs.get("data", {}) + captured_request["files"] = kwargs.get("files", {}) + + mock_response = MagicMock() + mock_response.json.return_value = { + "text": "test transcription", + "duration": 1.0, + } + mock_response.status_code = 200 + return mock_response + + with patch("litellm.llms.custom_httpx.http_handler.AsyncHTTPHandler.post", new=mock_post): + try: + await litellm.atranscription( + model="watsonx/whisper-large-v3-turbo", + file=b"fake_audio_data", + api_base="https://us-south.ml.cloud.ibm.com", + api_key="test-api-key", + project_id="test-project-123", + token="test-bearer-token", + language="en", + temperature=0.5, + ) + except Exception: + pass # We just want to capture the request + + # Validate request body contains expected fields + data = captured_request.get("data", {}) + assert data.get("model") == "whisper-large-v3-turbo" + assert data.get("language") == "en" + assert data.get("temperature") == 0.5 + assert data.get("response_format") == "verbose_json" # Default for cost calculation