2024-06-19 10:00:35 +08:00
import asyncio
2023-08-19 02:05:05 +08:00
import os
2024-06-19 10:00:35 +08:00
import subprocess
2023-08-02 06:01:23 +08:00
import sys
import traceback
2024-06-09 05:32:43 +08:00
from typing import Any
2023-08-19 02:05:05 +08:00
2024-06-19 10:00:35 +08:00
from openai import AuthenticationError , BadRequestError , OpenAIError , RateLimitError
2024-09-28 13:52:57 +08:00
from litellm . llms . custom_httpx . http_handler import AsyncHTTPHandler , HTTPHandler
2023-08-19 02:05:05 +08:00
sys . path . insert (
0 , os . path . abspath ( " ../.. " )
) # Adds the parent directory to the system path
2024-06-19 10:00:35 +08:00
from concurrent . futures import ThreadPoolExecutor
from unittest . mock import MagicMock , patch
import pytest
2023-08-02 06:01:23 +08:00
import litellm
2024-06-19 10:00:35 +08:00
from litellm import ( # AuthenticationError,; RateLimitError,; ServiceUnavailableError,; OpenAIError,
2023-08-30 03:29:56 +08:00
ContextWindowExceededError ,
2024-06-19 10:00:35 +08:00
completion ,
embedding ,
2023-08-19 02:05:05 +08:00
)
2023-12-25 16:40:38 +08:00
2026-03-31 07:24:35 +08:00
litellm . vertex_project = " litellm-ci-cd "
2023-08-30 06:30:24 +08:00
litellm . vertex_location = " us-central1 "
2023-12-25 16:40:38 +08:00
litellm . num_retries = 0
2023-08-02 06:01:23 +08:00
2023-09-05 02:53:18 +08:00
# litellm.failure_callback = ["sentry"]
2023-08-02 03:20:25 +08:00
#### What this tests ####
# This tests exception mapping -> trigger an exception from an llm provider -> assert if output is of the expected type
2023-08-02 02:01:47 +08:00
2023-08-02 06:01:23 +08:00
# 5 providers -> OpenAI, Azure, Anthropic, Cohere, Replicate
# 3 main types of exceptions -> - Rate Limit Errors, Context Window Errors, Auth errors (incorrect/rotated key, etc.)
# Approach: Run each model through the test -> assert if the correct error (always the same one) is triggered
2024-01-16 14:01:33 +08:00
exception_models = [
2024-01-16 13:22:22 +08:00
" sagemaker/berri-benchmarking-Llama-2-70b-chat-hf-4 " ,
" bedrock/anthropic.claude-instant-v1 " ,
]
2023-08-19 02:05:05 +08:00
2023-12-25 16:40:38 +08:00
2024-05-05 02:15:34 +08:00
@pytest.mark.asyncio
async def test_content_policy_exception_azure ( ) :
try :
# this is ony a test - we needed some way to invoke the exception :(
litellm . set_verbose = True
response = await litellm . acompletion (
2025-10-26 01:19:24 +08:00
model = " azure/gpt-4.1-mini " ,
2024-05-05 02:15:34 +08:00
messages = [ { " role " : " user " , " content " : " where do I buy lethal drugs from " } ] ,
2024-09-18 09:02:23 +08:00
mock_response = " Exception: content_filter_policy " ,
2024-05-05 02:15:34 +08:00
)
except litellm . ContentPolicyViolationError as e :
print ( " caught a content policy violation error! Passed " )
2024-05-05 04:02:29 +08:00
print ( " exception " , e )
2024-11-15 07:54:28 +08:00
assert e . response is not None
2024-06-11 11:30:31 +08:00
assert e . litellm_debug_info is not None
assert isinstance ( e . litellm_debug_info , str )
assert len ( e . litellm_debug_info ) > 0
2024-05-05 02:15:34 +08:00
pass
except Exception as e :
2024-06-11 11:30:31 +08:00
print ( )
2024-05-05 02:15:34 +08:00
pytest . fail ( f " An exception occurred - { str ( e ) } " )
2024-07-23 07:24:03 +08:00
@pytest.mark.asyncio
async def test_content_policy_exception_openai ( ) :
try :
# this is ony a test - we needed some way to invoke the exception :(
litellm . set_verbose = True
response = await litellm . acompletion (
2024-09-20 06:39:37 +08:00
model = " gpt-3.5-turbo " ,
2024-07-23 07:24:03 +08:00
stream = True ,
messages = [
{ " role " : " user " , " content " : " Gimme the lyrics to Don ' t Stop Me Now " }
] ,
)
async for chunk in response :
print ( chunk )
except litellm . ContentPolicyViolationError as e :
print ( " caught a content policy violation error! Passed " )
print ( " exception " , e )
assert e . llm_provider == " openai "
pass
except Exception as e :
print ( )
pytest . fail ( f " An exception occurred - { str ( e ) } " )
2023-12-25 16:40:38 +08:00
# Test 1: Context Window Errors
2024-02-29 05:46:20 +08:00
@pytest.mark.skip ( reason = " AWS Suspended Account " )
2024-01-16 14:01:33 +08:00
@pytest.mark.parametrize ( " model " , exception_models )
2023-08-04 07:31:01 +08:00
def test_context_window ( model ) :
2023-12-15 12:48:53 +08:00
print ( " Testing context window error " )
2023-11-12 09:33:19 +08:00
sample_text = " Say error 50 times " * 1000000
2023-08-04 07:31:01 +08:00
messages = [ { " content " : sample_text , " role " : " user " } ]
2023-11-04 09:11:50 +08:00
try :
2024-01-16 13:22:22 +08:00
litellm . set_verbose = False
print ( " Testing model= " , model )
2023-11-12 07:32:14 +08:00
response = completion ( model = model , messages = messages )
print ( f " response: { response } " )
print ( " FAILED! " )
2023-11-04 09:11:50 +08:00
pytest . fail ( f " An exception occurred " )
2023-11-12 07:32:14 +08:00
except ContextWindowExceededError as e :
print ( f " Worked! " )
2023-11-04 09:11:50 +08:00
except RateLimitError :
2023-11-12 07:32:14 +08:00
print ( " RateLimited! " )
2023-12-25 16:40:38 +08:00
except Exception as e :
2023-11-04 09:11:50 +08:00
print ( f " { e } " )
pytest . fail ( f " An error occcurred - { e } " )
2023-12-25 16:40:38 +08:00
2024-01-16 14:01:33 +08:00
models = [ " command-nightly " ]
2024-07-20 09:51:50 +08:00
@pytest.mark.skip ( reason = " duplicate test. " )
2023-11-01 13:32:29 +08:00
@pytest.mark.parametrize ( " model " , models )
def test_context_window_with_fallbacks ( model ) :
2023-12-25 16:40:38 +08:00
ctx_window_fallback_dict = {
2024-03-05 01:06:42 +08:00
" command-nightly " : " claude-2.1 " ,
2023-12-25 16:40:38 +08:00
" gpt-3.5-turbo-instruct " : " gpt-3.5-turbo-16k " ,
2025-10-26 01:19:24 +08:00
" azure/gpt-4.1-mini " : " gpt-3.5-turbo-16k " ,
2023-12-25 16:40:38 +08:00
}
2023-11-01 13:32:29 +08:00
sample_text = " how does a court case get to the Supreme Court? " * 1000
messages = [ { " content " : sample_text , " role " : " user " } ]
2024-02-16 10:19:52 +08:00
try :
completion (
model = model ,
messages = messages ,
context_window_fallback_dict = ctx_window_fallback_dict ,
)
except litellm . ServiceUnavailableError as e :
pass
2024-03-27 09:06:49 +08:00
except litellm . APIConnectionError as e :
pass
2023-12-25 16:40:38 +08:00
2023-11-01 13:32:29 +08:00
2023-11-04 09:11:50 +08:00
# for model in litellm.models_by_provider["bedrock"]:
# test_context_window(model=model)
2023-11-24 09:35:26 +08:00
# test_context_window(model="chat-bison")
2023-11-12 09:33:19 +08:00
# test_context_window_with_fallbacks(model="command-nightly")
2023-08-06 00:52:01 +08:00
# Test 2: InvalidAuth Errors
@pytest.mark.parametrize ( " model " , models )
2023-08-19 02:05:05 +08:00
def invalid_auth ( model ) : # set the model key to an invalid key, depending on the model
messages = [ { " content " : " Hello, how are you? " , " role " : " user " } ]
2023-08-06 00:52:01 +08:00
temporary_key = None
2023-08-19 02:05:05 +08:00
try :
2023-11-09 10:39:56 +08:00
if model == " gpt-3.5-turbo " or model == " gpt-3.5-turbo-instruct " :
2023-08-06 00:52:01 +08:00
temporary_key = os . environ [ " OPENAI_API_KEY " ]
os . environ [ " OPENAI_API_KEY " ] = " bad-key "
2023-11-12 09:33:19 +08:00
elif " bedrock " in model :
2023-11-04 09:25:34 +08:00
temporary_aws_access_key = os . environ [ " AWS_ACCESS_KEY_ID " ]
os . environ [ " AWS_ACCESS_KEY_ID " ] = " bad-key "
temporary_aws_region_name = os . environ [ " AWS_REGION_NAME " ]
os . environ [ " AWS_REGION_NAME " ] = " bad-key "
temporary_secret_key = os . environ [ " AWS_SECRET_ACCESS_KEY " ]
os . environ [ " AWS_SECRET_ACCESS_KEY " ] = " bad-key "
2025-10-26 01:19:24 +08:00
elif model == " azure/gpt-4.1-mini " :
2026-03-29 10:17:38 +08:00
temporary_key = os . environ [ " AZURE_AI_API_KEY " ]
os . environ [ " AZURE_AI_API_KEY " ] = " bad-key "
LiteLLM Minor Fixes & Improvements (11/05/2024) (#6590)
* fix(pattern_matching_router.py): update model name using correct function
* fix(langfuse.py): metadata deepcopy can cause unhandled error (#6563)
Co-authored-by: seva <seva@inita.com>
* fix(stream_chunk_builder_utils.py): correctly set prompt tokens + log correct streaming usage
Closes https://github.com/BerriAI/litellm/issues/6488
* build(deps): bump cookie and express in /docs/my-website (#6566)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.
Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)
Updates `express` from 4.20.0 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1)
---
updated-dependencies:
- dependency-name: cookie
dependency-type: indirect
- dependency-name: express
dependency-type: indirect
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* docs(virtual_keys.md): update Dockerfile reference (#6554)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
* (proxy fix) - call connect on prisma client when running setup (#6534)
* critical fix - call connect on prisma client when running setup
* fix test_proxy_server_prisma_setup
* fix test_proxy_server_prisma_setup
* Add 3.5 haiku (#6588)
* feat: add claude-3-5-haiku-20241022 entries
* feat: add claude-3-5-haiku-20241022 and vertex_ai/claude-3-5-haiku@20241022 models
* add missing entries, remove vision
* remove image token costs
* Litellm perf improvements 3 (#6573)
* perf: move writing key to cache, to background task
* perf(litellm_pre_call_utils.py): add otel tracing for pre-call utils
adds 200ms on calls with pgdb connected
* fix(litellm_pre_call_utils.py'): rename call_type to actual call used
* perf(proxy_server.py): remove db logic from _get_config_from_file
was causing db calls to occur on every llm request, if team_id was set on key
* fix(auth_checks.py): add check for reducing db calls if user/team id does not exist in db
reduces latency/call by ~100ms
* fix(proxy_server.py): minor fix on existing_settings not incl alerting
* fix(exception_mapping_utils.py): map databricks exception string
* fix(auth_checks.py): fix auth check logic
* test: correctly mark flaky test
* fix(utils.py): handle auth token error for tokenizers.from_pretrained
* build: fix map
* build: fix map
* build: fix json for model map
* fix ImageObject conversion (#6584)
* (fix) litellm.text_completion raises a non-blocking error on simple usage (#6546)
* unit test test_huggingface_text_completion_logprobs
* fix return TextCompletionHandler convert_chat_to_text_completion
* fix hf rest api
* fix test_huggingface_text_completion_logprobs
* fix linting errors
* fix importLiteLLMResponseObjectHandler
* fix test for LiteLLMResponseObjectHandler
* fix test text completion
* fix allow using 15 seconds for premium license check
* testing fix bedrock deprecated cohere.command-text-v14
* (feat) add `Predicted Outputs` for OpenAI (#6594)
* bump openai to openai==1.54.0
* add 'prediction' param
* testing fix bedrock deprecated cohere.command-text-v14
* test test_openai_prediction_param.py
* test_openai_prediction_param_with_caching
* doc Predicted Outputs
* doc Predicted Output
* (fix) Vertex Improve Performance when using `image_url` (#6593)
* fix transformation vertex
* test test_process_gemini_image
* test_image_completion_request
* testing fix - bedrock has deprecated cohere.command-text-v14
* fix vertex pdf
* bump: version 1.51.5 → 1.52.0
* fix(lowest_tpm_rpm_routing.py): fix parallel rate limit check (#6577)
* fix(lowest_tpm_rpm_routing.py): fix parallel rate limit check
* fix(lowest_tpm_rpm_v2.py): return headers in correct format
* test: update test
* build(deps): bump cookie and express in /docs/my-website (#6566)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.
Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)
Updates `express` from 4.20.0 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1)
---
updated-dependencies:
- dependency-name: cookie
dependency-type: indirect
- dependency-name: express
dependency-type: indirect
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* docs(virtual_keys.md): update Dockerfile reference (#6554)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
* (proxy fix) - call connect on prisma client when running setup (#6534)
* critical fix - call connect on prisma client when running setup
* fix test_proxy_server_prisma_setup
* fix test_proxy_server_prisma_setup
* Add 3.5 haiku (#6588)
* feat: add claude-3-5-haiku-20241022 entries
* feat: add claude-3-5-haiku-20241022 and vertex_ai/claude-3-5-haiku@20241022 models
* add missing entries, remove vision
* remove image token costs
* Litellm perf improvements 3 (#6573)
* perf: move writing key to cache, to background task
* perf(litellm_pre_call_utils.py): add otel tracing for pre-call utils
adds 200ms on calls with pgdb connected
* fix(litellm_pre_call_utils.py'): rename call_type to actual call used
* perf(proxy_server.py): remove db logic from _get_config_from_file
was causing db calls to occur on every llm request, if team_id was set on key
* fix(auth_checks.py): add check for reducing db calls if user/team id does not exist in db
reduces latency/call by ~100ms
* fix(proxy_server.py): minor fix on existing_settings not incl alerting
* fix(exception_mapping_utils.py): map databricks exception string
* fix(auth_checks.py): fix auth check logic
* test: correctly mark flaky test
* fix(utils.py): handle auth token error for tokenizers.from_pretrained
* build: fix map
* build: fix map
* build: fix json for model map
* test: remove eol model
* fix(proxy_server.py): fix db config loading logic
* fix(proxy_server.py): fix order of config / db updates, to ensure fields not overwritten
* test: skip test if required env var is missing
* test: fix test
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com>
Co-authored-by: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com>
* test: mark flaky test
* test: handle anthropic api instability
* test(test_proxy_utils.py): add testing for db config update logic
* Update setuptools in docker and fastapi to latest verison, in order to upgrade starlette version (#6597)
* build(deps): bump cookie and express in /docs/my-website (#6566)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.
Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)
Updates `express` from 4.20.0 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1)
---
updated-dependencies:
- dependency-name: cookie
dependency-type: indirect
- dependency-name: express
dependency-type: indirect
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* docs(virtual_keys.md): update Dockerfile reference (#6554)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
* (proxy fix) - call connect on prisma client when running setup (#6534)
* critical fix - call connect on prisma client when running setup
* fix test_proxy_server_prisma_setup
* fix test_proxy_server_prisma_setup
* Add 3.5 haiku (#6588)
* feat: add claude-3-5-haiku-20241022 entries
* feat: add claude-3-5-haiku-20241022 and vertex_ai/claude-3-5-haiku@20241022 models
* add missing entries, remove vision
* remove image token costs
* Litellm perf improvements 3 (#6573)
* perf: move writing key to cache, to background task
* perf(litellm_pre_call_utils.py): add otel tracing for pre-call utils
adds 200ms on calls with pgdb connected
* fix(litellm_pre_call_utils.py'): rename call_type to actual call used
* perf(proxy_server.py): remove db logic from _get_config_from_file
was causing db calls to occur on every llm request, if team_id was set on key
* fix(auth_checks.py): add check for reducing db calls if user/team id does not exist in db
reduces latency/call by ~100ms
* fix(proxy_server.py): minor fix on existing_settings not incl alerting
* fix(exception_mapping_utils.py): map databricks exception string
* fix(auth_checks.py): fix auth check logic
* test: correctly mark flaky test
* fix(utils.py): handle auth token error for tokenizers.from_pretrained
* build: fix map
* build: fix map
* build: fix json for model map
* fix ImageObject conversion (#6584)
* (fix) litellm.text_completion raises a non-blocking error on simple usage (#6546)
* unit test test_huggingface_text_completion_logprobs
* fix return TextCompletionHandler convert_chat_to_text_completion
* fix hf rest api
* fix test_huggingface_text_completion_logprobs
* fix linting errors
* fix importLiteLLMResponseObjectHandler
* fix test for LiteLLMResponseObjectHandler
* fix test text completion
* fix allow using 15 seconds for premium license check
* testing fix bedrock deprecated cohere.command-text-v14
* (feat) add `Predicted Outputs` for OpenAI (#6594)
* bump openai to openai==1.54.0
* add 'prediction' param
* testing fix bedrock deprecated cohere.command-text-v14
* test test_openai_prediction_param.py
* test_openai_prediction_param_with_caching
* doc Predicted Outputs
* doc Predicted Output
* (fix) Vertex Improve Performance when using `image_url` (#6593)
* fix transformation vertex
* test test_process_gemini_image
* test_image_completion_request
* testing fix - bedrock has deprecated cohere.command-text-v14
* fix vertex pdf
* bump: version 1.51.5 → 1.52.0
* Update setuptools in docker and fastapi to latest verison, in order to upgrade starlette version
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com>
Co-authored-by: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com>
Co-authored-by: Krish Dholakia <krrishdholakia@gmail.com>
Co-authored-by: Jacob Hagstedt <wcgs@novonordisk.com>
* fix(langfuse.py): fix linting errors
* fix: fix linting errors
* fix: fix casting error
* fix: fix typing error
* fix: add more tests
* fix(utils.py): fix return_processed_chunk_logic
* Revert "Update setuptools in docker and fastapi to latest verison, in order t…" (#6615)
This reverts commit 1a7f7bdfb75df0efbc930b7f2e39febc80e97d5a.
* docs fix clarify team_id on team based logging
* doc fix team based logging with langfuse
* fix flake8 checks
* test: bump sleep time
* refactor: replace claude-instant-1.2 with haiku in testing
* fix(proxy_server.py): move to using sl payload in track_cost_callback
* fix(proxy_server.py): fix linting errors
* fix(proxy_server.py): fallback to kwargs(response_cost) if given
* test: remove claude-instant-1 from tests
* test: fix claude test
* docs fix clarify team_id on team based logging
* doc fix team based logging with langfuse
* build: remove lint.yml
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Vsevolod Karvetskiy <56288164+karvetskiy@users.noreply.github.com>
Co-authored-by: seva <seva@inita.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com>
Co-authored-by: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com>
Co-authored-by: Jacob Hagstedt P Suorra <Jacobh2@users.noreply.github.com>
Co-authored-by: Jacob Hagstedt <wcgs@novonordisk.com>
2024-11-07 06:47:05 +08:00
elif model == " claude-3-5-haiku-20241022 " :
2023-08-06 00:52:01 +08:00
temporary_key = os . environ [ " ANTHROPIC_API_KEY " ]
os . environ [ " ANTHROPIC_API_KEY " ] = " bad-key "
elif model == " command-nightly " :
temporary_key = os . environ [ " COHERE_API_KEY " ]
os . environ [ " COHERE_API_KEY " ] = " bad-key "
2023-08-30 04:32:20 +08:00
elif " j2 " in model :
temporary_key = os . environ [ " AI21_API_KEY " ]
os . environ [ " AI21_API_KEY " ] = " bad-key "
2023-08-30 03:29:56 +08:00
elif " togethercomputer " in model :
temporary_key = os . environ [ " TOGETHERAI_API_KEY " ]
2026-03-29 10:17:38 +08:00
os . environ [ " TOGETHERAI_API_KEY " ] = " sk-test-togetherai-key-808 "
2023-08-30 06:30:24 +08:00
elif model in litellm . openrouter_models :
temporary_key = os . environ [ " OPENROUTER_API_KEY " ]
os . environ [ " OPENROUTER_API_KEY " ] = " bad-key "
2023-09-12 09:33:54 +08:00
elif model in litellm . aleph_alpha_models :
temporary_key = os . environ [ " ALEPH_ALPHA_API_KEY " ]
os . environ [ " ALEPH_ALPHA_API_KEY " ] = " bad-key "
2023-09-15 00:19:32 +08:00
elif model in litellm . nlp_cloud_models :
os . environ [ " NLP_CLOUD_API_KEY " ] = " bad-key "
2023-08-19 02:05:05 +08:00
elif (
model
== " replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1 "
) :
temporary_key = os . environ [ " REPLICATE_API_KEY " ]
2023-08-06 00:52:01 +08:00
os . environ [ " REPLICATE_API_KEY " ] = " bad-key "
print ( f " model: { model } " )
2023-12-25 16:40:38 +08:00
response = completion ( model = model , messages = messages )
2023-08-06 00:52:01 +08:00
print ( f " response: { response } " )
except AuthenticationError as e :
2023-11-04 09:25:34 +08:00
print ( f " AuthenticationError Caught Exception - { str ( e ) } " )
2023-08-19 02:05:05 +08:00
except (
OpenAIError
2023-11-04 09:25:34 +08:00
) as e : # is at least an openai error -> in case of random model errors - e.g. overloaded server
2023-08-06 00:52:01 +08:00
print ( f " OpenAIError Caught Exception - { e } " )
except Exception as e :
print ( type ( e ) )
2023-08-30 06:30:24 +08:00
print ( type ( AuthenticationError ) )
2023-08-06 00:52:01 +08:00
print ( e . __class__ . __name__ )
print ( f " Uncaught Exception - { e } " )
pytest . fail ( f " Error occurred: { e } " )
2023-08-19 02:05:05 +08:00
if temporary_key != None : # reset the key
2023-08-06 00:52:01 +08:00
if model == " gpt-3.5-turbo " :
os . environ [ " OPENAI_API_KEY " ] = temporary_key
elif model == " chatgpt-test " :
2026-03-29 10:17:38 +08:00
os . environ [ " AZURE_AI_API_KEY " ] = temporary_key
2023-08-06 00:52:01 +08:00
azure = True
LiteLLM Minor Fixes & Improvements (11/05/2024) (#6590)
* fix(pattern_matching_router.py): update model name using correct function
* fix(langfuse.py): metadata deepcopy can cause unhandled error (#6563)
Co-authored-by: seva <seva@inita.com>
* fix(stream_chunk_builder_utils.py): correctly set prompt tokens + log correct streaming usage
Closes https://github.com/BerriAI/litellm/issues/6488
* build(deps): bump cookie and express in /docs/my-website (#6566)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.
Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)
Updates `express` from 4.20.0 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1)
---
updated-dependencies:
- dependency-name: cookie
dependency-type: indirect
- dependency-name: express
dependency-type: indirect
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* docs(virtual_keys.md): update Dockerfile reference (#6554)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
* (proxy fix) - call connect on prisma client when running setup (#6534)
* critical fix - call connect on prisma client when running setup
* fix test_proxy_server_prisma_setup
* fix test_proxy_server_prisma_setup
* Add 3.5 haiku (#6588)
* feat: add claude-3-5-haiku-20241022 entries
* feat: add claude-3-5-haiku-20241022 and vertex_ai/claude-3-5-haiku@20241022 models
* add missing entries, remove vision
* remove image token costs
* Litellm perf improvements 3 (#6573)
* perf: move writing key to cache, to background task
* perf(litellm_pre_call_utils.py): add otel tracing for pre-call utils
adds 200ms on calls with pgdb connected
* fix(litellm_pre_call_utils.py'): rename call_type to actual call used
* perf(proxy_server.py): remove db logic from _get_config_from_file
was causing db calls to occur on every llm request, if team_id was set on key
* fix(auth_checks.py): add check for reducing db calls if user/team id does not exist in db
reduces latency/call by ~100ms
* fix(proxy_server.py): minor fix on existing_settings not incl alerting
* fix(exception_mapping_utils.py): map databricks exception string
* fix(auth_checks.py): fix auth check logic
* test: correctly mark flaky test
* fix(utils.py): handle auth token error for tokenizers.from_pretrained
* build: fix map
* build: fix map
* build: fix json for model map
* fix ImageObject conversion (#6584)
* (fix) litellm.text_completion raises a non-blocking error on simple usage (#6546)
* unit test test_huggingface_text_completion_logprobs
* fix return TextCompletionHandler convert_chat_to_text_completion
* fix hf rest api
* fix test_huggingface_text_completion_logprobs
* fix linting errors
* fix importLiteLLMResponseObjectHandler
* fix test for LiteLLMResponseObjectHandler
* fix test text completion
* fix allow using 15 seconds for premium license check
* testing fix bedrock deprecated cohere.command-text-v14
* (feat) add `Predicted Outputs` for OpenAI (#6594)
* bump openai to openai==1.54.0
* add 'prediction' param
* testing fix bedrock deprecated cohere.command-text-v14
* test test_openai_prediction_param.py
* test_openai_prediction_param_with_caching
* doc Predicted Outputs
* doc Predicted Output
* (fix) Vertex Improve Performance when using `image_url` (#6593)
* fix transformation vertex
* test test_process_gemini_image
* test_image_completion_request
* testing fix - bedrock has deprecated cohere.command-text-v14
* fix vertex pdf
* bump: version 1.51.5 → 1.52.0
* fix(lowest_tpm_rpm_routing.py): fix parallel rate limit check (#6577)
* fix(lowest_tpm_rpm_routing.py): fix parallel rate limit check
* fix(lowest_tpm_rpm_v2.py): return headers in correct format
* test: update test
* build(deps): bump cookie and express in /docs/my-website (#6566)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.
Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)
Updates `express` from 4.20.0 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1)
---
updated-dependencies:
- dependency-name: cookie
dependency-type: indirect
- dependency-name: express
dependency-type: indirect
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* docs(virtual_keys.md): update Dockerfile reference (#6554)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
* (proxy fix) - call connect on prisma client when running setup (#6534)
* critical fix - call connect on prisma client when running setup
* fix test_proxy_server_prisma_setup
* fix test_proxy_server_prisma_setup
* Add 3.5 haiku (#6588)
* feat: add claude-3-5-haiku-20241022 entries
* feat: add claude-3-5-haiku-20241022 and vertex_ai/claude-3-5-haiku@20241022 models
* add missing entries, remove vision
* remove image token costs
* Litellm perf improvements 3 (#6573)
* perf: move writing key to cache, to background task
* perf(litellm_pre_call_utils.py): add otel tracing for pre-call utils
adds 200ms on calls with pgdb connected
* fix(litellm_pre_call_utils.py'): rename call_type to actual call used
* perf(proxy_server.py): remove db logic from _get_config_from_file
was causing db calls to occur on every llm request, if team_id was set on key
* fix(auth_checks.py): add check for reducing db calls if user/team id does not exist in db
reduces latency/call by ~100ms
* fix(proxy_server.py): minor fix on existing_settings not incl alerting
* fix(exception_mapping_utils.py): map databricks exception string
* fix(auth_checks.py): fix auth check logic
* test: correctly mark flaky test
* fix(utils.py): handle auth token error for tokenizers.from_pretrained
* build: fix map
* build: fix map
* build: fix json for model map
* test: remove eol model
* fix(proxy_server.py): fix db config loading logic
* fix(proxy_server.py): fix order of config / db updates, to ensure fields not overwritten
* test: skip test if required env var is missing
* test: fix test
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com>
Co-authored-by: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com>
* test: mark flaky test
* test: handle anthropic api instability
* test(test_proxy_utils.py): add testing for db config update logic
* Update setuptools in docker and fastapi to latest verison, in order to upgrade starlette version (#6597)
* build(deps): bump cookie and express in /docs/my-website (#6566)
Bumps [cookie](https://github.com/jshttp/cookie) and [express](https://github.com/expressjs/express). These dependencies needed to be updated together.
Updates `cookie` from 0.6.0 to 0.7.1
- [Release notes](https://github.com/jshttp/cookie/releases)
- [Commits](https://github.com/jshttp/cookie/compare/v0.6.0...v0.7.1)
Updates `express` from 4.20.0 to 4.21.1
- [Release notes](https://github.com/expressjs/express/releases)
- [Changelog](https://github.com/expressjs/express/blob/4.21.1/History.md)
- [Commits](https://github.com/expressjs/express/compare/4.20.0...4.21.1)
---
updated-dependencies:
- dependency-name: cookie
dependency-type: indirect
- dependency-name: express
dependency-type: indirect
...
Signed-off-by: dependabot[bot] <support@github.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
* docs(virtual_keys.md): update Dockerfile reference (#6554)
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
* (proxy fix) - call connect on prisma client when running setup (#6534)
* critical fix - call connect on prisma client when running setup
* fix test_proxy_server_prisma_setup
* fix test_proxy_server_prisma_setup
* Add 3.5 haiku (#6588)
* feat: add claude-3-5-haiku-20241022 entries
* feat: add claude-3-5-haiku-20241022 and vertex_ai/claude-3-5-haiku@20241022 models
* add missing entries, remove vision
* remove image token costs
* Litellm perf improvements 3 (#6573)
* perf: move writing key to cache, to background task
* perf(litellm_pre_call_utils.py): add otel tracing for pre-call utils
adds 200ms on calls with pgdb connected
* fix(litellm_pre_call_utils.py'): rename call_type to actual call used
* perf(proxy_server.py): remove db logic from _get_config_from_file
was causing db calls to occur on every llm request, if team_id was set on key
* fix(auth_checks.py): add check for reducing db calls if user/team id does not exist in db
reduces latency/call by ~100ms
* fix(proxy_server.py): minor fix on existing_settings not incl alerting
* fix(exception_mapping_utils.py): map databricks exception string
* fix(auth_checks.py): fix auth check logic
* test: correctly mark flaky test
* fix(utils.py): handle auth token error for tokenizers.from_pretrained
* build: fix map
* build: fix map
* build: fix json for model map
* fix ImageObject conversion (#6584)
* (fix) litellm.text_completion raises a non-blocking error on simple usage (#6546)
* unit test test_huggingface_text_completion_logprobs
* fix return TextCompletionHandler convert_chat_to_text_completion
* fix hf rest api
* fix test_huggingface_text_completion_logprobs
* fix linting errors
* fix importLiteLLMResponseObjectHandler
* fix test for LiteLLMResponseObjectHandler
* fix test text completion
* fix allow using 15 seconds for premium license check
* testing fix bedrock deprecated cohere.command-text-v14
* (feat) add `Predicted Outputs` for OpenAI (#6594)
* bump openai to openai==1.54.0
* add 'prediction' param
* testing fix bedrock deprecated cohere.command-text-v14
* test test_openai_prediction_param.py
* test_openai_prediction_param_with_caching
* doc Predicted Outputs
* doc Predicted Output
* (fix) Vertex Improve Performance when using `image_url` (#6593)
* fix transformation vertex
* test test_process_gemini_image
* test_image_completion_request
* testing fix - bedrock has deprecated cohere.command-text-v14
* fix vertex pdf
* bump: version 1.51.5 → 1.52.0
* Update setuptools in docker and fastapi to latest verison, in order to upgrade starlette version
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com>
Co-authored-by: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com>
Co-authored-by: Krish Dholakia <krrishdholakia@gmail.com>
Co-authored-by: Jacob Hagstedt <wcgs@novonordisk.com>
* fix(langfuse.py): fix linting errors
* fix: fix linting errors
* fix: fix casting error
* fix: fix typing error
* fix: add more tests
* fix(utils.py): fix return_processed_chunk_logic
* Revert "Update setuptools in docker and fastapi to latest verison, in order t…" (#6615)
This reverts commit 1a7f7bdfb75df0efbc930b7f2e39febc80e97d5a.
* docs fix clarify team_id on team based logging
* doc fix team based logging with langfuse
* fix flake8 checks
* test: bump sleep time
* refactor: replace claude-instant-1.2 with haiku in testing
* fix(proxy_server.py): move to using sl payload in track_cost_callback
* fix(proxy_server.py): fix linting errors
* fix(proxy_server.py): fallback to kwargs(response_cost) if given
* test: remove claude-instant-1 from tests
* test: fix claude test
* docs fix clarify team_id on team based logging
* doc fix team based logging with langfuse
* build: remove lint.yml
---------
Signed-off-by: dependabot[bot] <support@github.com>
Signed-off-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Vsevolod Karvetskiy <56288164+karvetskiy@users.noreply.github.com>
Co-authored-by: seva <seva@inita.com>
Co-authored-by: dependabot[bot] <49699333+dependabot[bot]@users.noreply.github.com>
Co-authored-by: Emmanuel Ferdman <emmanuelferdman@gmail.com>
Co-authored-by: Ishaan Jaff <ishaanjaffer0324@gmail.com>
Co-authored-by: paul-gauthier <69695708+paul-gauthier@users.noreply.github.com>
Co-authored-by: Jacob Hagstedt P Suorra <Jacobh2@users.noreply.github.com>
Co-authored-by: Jacob Hagstedt <wcgs@novonordisk.com>
2024-11-07 06:47:05 +08:00
elif model == " claude-3-5-haiku-20241022 " :
2023-08-06 00:52:01 +08:00
os . environ [ " ANTHROPIC_API_KEY " ] = temporary_key
elif model == " command-nightly " :
os . environ [ " COHERE_API_KEY " ] = temporary_key
2023-08-19 02:05:05 +08:00
elif (
model
== " replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1 "
) :
2023-08-06 00:52:01 +08:00
os . environ [ " REPLICATE_API_KEY " ] = temporary_key
2023-08-30 04:32:20 +08:00
elif " j2 " in model :
os . environ [ " AI21_API_KEY " ] = temporary_key
2023-12-25 16:40:38 +08:00
elif " togethercomputer " in model :
2023-08-30 03:29:56 +08:00
os . environ [ " TOGETHERAI_API_KEY " ] = temporary_key
2023-09-12 09:33:54 +08:00
elif model in litellm . aleph_alpha_models :
os . environ [ " ALEPH_ALPHA_API_KEY " ] = temporary_key
2023-09-15 00:19:32 +08:00
elif model in litellm . nlp_cloud_models :
2026-03-29 11:38:54 +08:00
os . environ . pop ( " NLP_CLOUD_API_KEY " , None )
2023-12-25 16:40:38 +08:00
elif " bedrock " in model :
2023-11-04 09:25:34 +08:00
os . environ [ " AWS_ACCESS_KEY_ID " ] = temporary_aws_access_key
os . environ [ " AWS_REGION_NAME " ] = temporary_aws_region_name
os . environ [ " AWS_SECRET_ACCESS_KEY " ] = temporary_secret_key
2023-08-06 00:52:01 +08:00
return
2023-08-19 02:05:05 +08:00
2023-12-25 16:40:38 +08:00
2023-11-09 08:52:18 +08:00
# for model in litellm.models_by_provider["bedrock"]:
# invalid_auth(model=model)
2023-11-12 09:33:19 +08:00
# invalid_auth(model="command-nightly")
2023-11-04 09:25:34 +08:00
2023-12-25 16:40:38 +08:00
# Test 3: Invalid Request Error
2023-09-12 09:33:54 +08:00
@pytest.mark.parametrize ( " model " , models )
def test_invalid_request_error ( model ) :
messages = [ { " content " : " hey, how ' s it going? " , " role " : " user " } ]
2023-11-12 07:32:14 +08:00
with pytest . raises ( BadRequestError ) :
2023-09-12 09:33:54 +08:00
completion ( model = model , messages = messages , max_tokens = " hello world " )
2023-11-24 07:19:31 +08:00
def test_completion_azure_exception ( ) :
try :
import openai
2023-12-25 16:40:38 +08:00
2023-11-24 07:19:31 +08:00
print ( " azure gpt-3.5 test \n \n " )
2023-12-25 16:40:38 +08:00
litellm . set_verbose = True
2023-11-24 07:19:31 +08:00
## Test azure call
2026-03-29 10:17:38 +08:00
old_azure_key = os . environ [ " AZURE_AI_API_KEY " ]
os . environ [ " AZURE_AI_API_KEY " ] = " good morning "
2023-11-24 07:19:31 +08:00
response = completion (
2025-10-26 01:19:24 +08:00
model = " azure/gpt-4.1-mini " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " hello " } ] ,
2023-11-24 07:19:31 +08:00
)
2026-03-29 10:17:38 +08:00
os . environ [ " AZURE_AI_API_KEY " ] = old_azure_key
2023-11-24 07:19:31 +08:00
print ( f " response: { response } " )
print ( response )
2023-11-26 07:33:44 +08:00
except openai . AuthenticationError as e :
2026-03-29 10:17:38 +08:00
os . environ [ " AZURE_AI_API_KEY " ] = old_azure_key
2023-11-24 07:19:31 +08:00
print ( " good job got the correct error for azure when key not set " )
except Exception as e :
pytest . fail ( f " Error occurred: { e } " )
2023-12-25 16:40:38 +08:00
2023-12-14 21:27:39 +08:00
# test_completion_azure_exception()
2023-11-24 07:19:31 +08:00
2023-12-25 16:40:38 +08:00
2024-07-02 12:19:47 +08:00
def test_azure_embedding_exceptions ( ) :
try :
response = litellm . embedding (
2025-09-28 03:41:35 +08:00
model = " azure/text-embedding-ada-002 " ,
2024-07-02 12:19:47 +08:00
input = " hello " ,
2025-06-03 22:24:13 +08:00
mock_response = " error " ,
2024-07-02 12:19:47 +08:00
)
pytest . fail ( f " Bad request this should have failed but got { response } " )
except Exception as e :
print ( vars ( e ) )
# CRUCIAL Test - Ensures our exceptions are readable and not overly complicated. some users have complained exceptions will randomly have another exception raised in our exception mapping
2025-06-03 22:24:13 +08:00
assert str ( e ) == " Mock error "
2024-07-02 12:19:47 +08:00
2023-11-26 07:46:07 +08:00
async def asynctest_completion_azure_exception ( ) :
2023-11-26 07:33:44 +08:00
try :
import openai
2024-06-19 10:00:35 +08:00
2023-11-26 07:33:44 +08:00
import litellm
2023-12-25 16:40:38 +08:00
2023-11-26 07:33:44 +08:00
print ( " azure gpt-3.5 test \n \n " )
2023-12-25 16:40:38 +08:00
litellm . set_verbose = True
2023-11-26 07:33:44 +08:00
## Test azure call
2026-03-29 10:17:38 +08:00
old_azure_key = os . environ [ " AZURE_AI_API_KEY " ]
os . environ [ " AZURE_AI_API_KEY " ] = " good morning "
2023-11-26 07:33:44 +08:00
response = await litellm . acompletion (
2025-10-26 01:19:24 +08:00
model = " azure/gpt-4.1-mini " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " hello " } ] ,
2023-11-26 07:33:44 +08:00
)
print ( f " response: { response } " )
print ( response )
except openai . AuthenticationError as e :
2026-03-29 10:17:38 +08:00
os . environ [ " AZURE_AI_API_KEY " ] = old_azure_key
2023-11-26 07:33:44 +08:00
print ( " good job got the correct error for azure when key not set " )
print ( e )
except Exception as e :
print ( " Got wrong exception " )
print ( " exception " , e )
pytest . fail ( f " Error occurred: { e } " )
2023-12-25 16:40:38 +08:00
2023-11-29 09:24:49 +08:00
# import asyncio
# asyncio.run(
# asynctest_completion_azure_exception()
# )
2023-11-26 07:33:44 +08:00
2023-11-26 07:43:46 +08:00
2023-12-15 12:23:38 +08:00
def asynctest_completion_openai_exception_bad_model ( ) :
try :
2024-06-19 10:00:35 +08:00
import asyncio
2023-12-15 12:23:38 +08:00
import openai
2024-06-19 10:00:35 +08:00
import litellm
2023-12-25 16:40:38 +08:00
2023-12-15 12:23:38 +08:00
print ( " azure exception bad model \n \n " )
2023-12-25 16:40:38 +08:00
litellm . set_verbose = True
2023-12-15 12:23:38 +08:00
## Test azure call
async def test ( ) :
response = await litellm . acompletion (
model = " openai/gpt-6 " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " hello " } ] ,
2023-12-15 12:23:38 +08:00
)
2023-12-25 16:40:38 +08:00
2023-12-15 12:23:38 +08:00
asyncio . run ( test ( ) )
2023-12-15 12:48:53 +08:00
except openai . NotFoundError :
print ( " Good job this is a NotFoundError for a model that does not exist! " )
2023-12-15 12:23:38 +08:00
print ( " Passed " )
except Exception as e :
print ( " Raised wrong type of exception " , type ( e ) )
assert isinstance ( e , openai . BadRequestError )
pytest . fail ( f " Error occurred: { e } " )
2023-12-25 16:40:38 +08:00
# asynctest_completion_openai_exception_bad_model()
2023-12-15 12:23:38 +08:00
def asynctest_completion_azure_exception_bad_model ( ) :
try :
2024-06-19 10:00:35 +08:00
import asyncio
2023-12-15 12:23:38 +08:00
import openai
2024-06-19 10:00:35 +08:00
import litellm
2023-12-25 16:40:38 +08:00
2023-12-15 12:23:38 +08:00
print ( " azure exception bad model \n \n " )
2023-12-25 16:40:38 +08:00
litellm . set_verbose = True
2023-12-15 12:23:38 +08:00
## Test azure call
async def test ( ) :
response = await litellm . acompletion (
model = " azure/gpt-12 " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " hello " } ] ,
2023-12-15 12:23:38 +08:00
)
2023-12-25 16:40:38 +08:00
2023-12-15 12:23:38 +08:00
asyncio . run ( test ( ) )
2023-12-15 12:48:53 +08:00
except openai . NotFoundError :
print ( " Good job this is a NotFoundError for a model that does not exist! " )
2023-12-15 12:23:38 +08:00
print ( " Passed " )
except Exception as e :
print ( " Raised wrong type of exception " , type ( e ) )
pytest . fail ( f " Error occurred: { e } " )
2023-12-25 16:40:38 +08:00
2023-12-15 12:23:38 +08:00
# asynctest_completion_azure_exception_bad_model()
2023-12-25 16:40:38 +08:00
2023-11-26 07:43:46 +08:00
def test_completion_openai_exception ( ) :
# test if openai:gpt raises openai.AuthenticationError
try :
import openai
2023-12-25 16:40:38 +08:00
2023-11-26 07:43:46 +08:00
print ( " openai gpt-3.5 test \n \n " )
2023-12-25 16:40:38 +08:00
litellm . set_verbose = True
2023-11-26 07:43:46 +08:00
## Test azure call
old_azure_key = os . environ [ " OPENAI_API_KEY " ]
os . environ [ " OPENAI_API_KEY " ] = " good morning "
response = completion (
model = " gpt-4 " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " hello " } ] ,
2023-11-26 07:43:46 +08:00
)
print ( f " response: { response } " )
print ( response )
except openai . AuthenticationError as e :
os . environ [ " OPENAI_API_KEY " ] = old_azure_key
2023-12-15 12:48:53 +08:00
print ( " OpenAI: good job got the correct error for openai when key not set " )
2023-11-26 07:43:46 +08:00
except Exception as e :
pytest . fail ( f " Error occurred: { e } " )
2023-12-25 16:40:38 +08:00
2023-11-26 07:43:46 +08:00
# test_completion_openai_exception()
2023-12-25 16:40:38 +08:00
2024-07-17 11:44:40 +08:00
def test_anthropic_openai_exception ( ) :
# test if anthropic raises litellm.AuthenticationError
try :
litellm . set_verbose = True
## Test azure call
old_azure_key = os . environ [ " ANTHROPIC_API_KEY " ]
os . environ . pop ( " ANTHROPIC_API_KEY " )
response = completion (
2025-07-23 09:28:36 +08:00
model = " anthropic/claude-3-sonnet-20240229 " ,
2024-07-17 11:44:40 +08:00
messages = [ { " role " : " user " , " content " : " hello " } ] ,
)
print ( f " response: { response } " )
print ( response )
except litellm . AuthenticationError as e :
os . environ [ " ANTHROPIC_API_KEY " ] = old_azure_key
print ( " Exception vars= " , vars ( e ) )
assert (
" Missing Anthropic API Key - A call is being made to anthropic but no key is set either in the environment variables or via params "
in e . message
)
print (
" ANTHROPIC_API_KEY: good job got the correct error for ANTHROPIC_API_KEY when key not set "
)
except Exception as e :
pytest . fail ( f " Error occurred: { e } " )
2023-12-14 21:27:39 +08:00
def test_completion_mistral_exception ( ) :
# test if mistral/mistral-tiny raises openai.AuthenticationError
try :
import openai
2023-12-25 16:40:38 +08:00
2023-12-14 21:27:39 +08:00
print ( " Testing mistral ai exception mapping " )
2023-12-25 16:40:38 +08:00
litellm . set_verbose = True
2023-12-14 21:27:39 +08:00
## Test azure call
old_azure_key = os . environ [ " MISTRAL_API_KEY " ]
os . environ [ " MISTRAL_API_KEY " ] = " good morning "
response = completion (
model = " mistral/mistral-tiny " ,
2023-12-25 16:40:38 +08:00
messages = [ { " role " : " user " , " content " : " hello " } ] ,
2023-12-14 21:27:39 +08:00
)
print ( f " response: { response } " )
print ( response )
except openai . AuthenticationError as e :
os . environ [ " MISTRAL_API_KEY " ] = old_azure_key
print ( " good job got the correct error for openai when key not set " )
except Exception as e :
pytest . fail ( f " Error occurred: { e } " )
2023-11-26 07:43:46 +08:00
2023-12-25 16:40:38 +08:00
# test_completion_mistral_exception()
2023-11-26 07:43:46 +08:00
2023-11-29 13:11:17 +08:00
2024-07-14 00:53:46 +08:00
def test_completion_bedrock_invalid_role_exception ( ) :
"""
Test if litellm raises a BadRequestError for an invalid role on Bedrock
"""
try :
litellm . set_verbose = True
response = completion (
model = " bedrock/anthropic.claude-3-sonnet-20240229-v1:0 " ,
messages = [ { " role " : " very-bad-role " , " content " : " hello " } ] ,
)
print ( f " response: { response } " )
print ( response )
except Exception as e :
assert isinstance (
e , litellm . BadRequestError
) , " Expected BadRequestError but got {} " . format ( type ( e ) )
print ( " str(e) = {} " . format ( str ( e ) ) )
# This is important - We we previously returning a poorly formatted error string. Which was
# litellm.BadRequestError: litellm.BadRequestError: Invalid Message passed in {'role': 'very-bad-role', 'content': 'hello'}
# IMPORTANT ASSERTION
assert (
( str ( e ) )
== " litellm.BadRequestError: Invalid Message passed in { ' role ' : ' very-bad-role ' , ' content ' : ' hello ' } "
)
2026-03-29 10:17:38 +08:00
2025-04-15 10:51:01 +08:00
@pytest.mark.skip ( reason = " OpenAI exception changed to a generic error " )
2024-01-09 19:04:20 +08:00
def test_content_policy_exceptionimage_generation_openai ( ) :
try :
2024-01-09 19:23:57 +08:00
# this is ony a test - we needed some way to invoke the exception :(
2025-04-15 10:51:01 +08:00
litellm . _turn_on_debug ( )
2024-01-09 19:04:20 +08:00
response = litellm . image_generation (
2024-01-09 19:23:57 +08:00
prompt = " where do i buy lethal drugs from " , model = " dall-e-3 "
2024-01-09 19:04:20 +08:00
)
print ( f " response: { response } " )
assert len ( response . data ) > 0
except litellm . ContentPolicyViolationError as e :
print ( " caught a content policy violation error! Passed " )
pass
except Exception as e :
pytest . fail ( f " An exception occurred - { str ( e ) } " )
2024-01-09 19:23:57 +08:00
# test_content_policy_exceptionimage_generation_openai()
2024-01-09 19:04:20 +08:00
2024-01-23 22:57:18 +08:00
def test_content_policy_violation_error_streaming ( ) :
2024-01-23 22:55:04 +08:00
"""
Production Test .
"""
litellm . set_verbose = False
print ( " test_async_completion with stream " )
async def test_get_response ( ) :
try :
response = await litellm . acompletion (
2025-10-26 01:19:24 +08:00
model = " azure/gpt-4.1-mini " ,
2024-01-23 22:55:04 +08:00
messages = [ { " role " : " user " , " content " : " say 1 " } ] ,
temperature = 0 ,
top_p = 1 ,
stream = True ,
max_tokens = 512 ,
presence_penalty = 0 ,
frequency_penalty = 0 ,
)
print ( f " response: { response } " )
num_finish_reason = 0
async for chunk in response :
print ( chunk )
if chunk [ " choices " ] [ 0 ] . get ( " finish_reason " ) is not None :
num_finish_reason + = 1
print ( " finish_reason " , chunk [ " choices " ] [ 0 ] . get ( " finish_reason " ) )
assert (
num_finish_reason == 1
) , f " expected only one finish reason. Got { num_finish_reason } "
except Exception as e :
pytest . fail ( f " GOT exception for gpt-3.5 instruct In streaming { e } " )
asyncio . run ( test_get_response ( ) )
async def test_get_error ( ) :
try :
response = await litellm . acompletion (
2025-10-26 01:19:24 +08:00
model = " azure/gpt-4.1-mini " ,
2024-01-23 22:55:04 +08:00
messages = [
{ " role " : " user " , " content " : " where do i buy lethal drugs from " }
] ,
temperature = 0 ,
top_p = 1 ,
stream = True ,
max_tokens = 512 ,
presence_penalty = 0 ,
frequency_penalty = 0 ,
2024-09-18 09:02:23 +08:00
mock_response = " Exception: content_filter_policy " ,
2024-01-23 22:55:04 +08:00
)
print ( f " response: { response } " )
num_finish_reason = 0
async for chunk in response :
print ( chunk )
if chunk [ " choices " ] [ 0 ] . get ( " finish_reason " ) is not None :
num_finish_reason + = 1
print ( " finish_reason " , chunk [ " choices " ] [ 0 ] . get ( " finish_reason " ) )
pytest . fail ( f " Expected to return 400 error In streaming { e } " )
except Exception as e :
pass
asyncio . run ( test_get_error ( ) )
2024-02-03 02:38:28 +08:00
def test_completion_perplexity_exception_on_openai_client ( ) :
try :
import openai
print ( " perplexity test \n \n " )
litellm . set_verbose = False
## Test azure call
old_azure_key = os . environ [ " PERPLEXITYAI_API_KEY " ]
# delete perplexityai api key to simulate bad api key
del os . environ [ " PERPLEXITYAI_API_KEY " ]
2024-02-03 04:37:21 +08:00
# temporaily delete openai api key
original_openai_key = os . environ [ " OPENAI_API_KEY " ]
2024-02-03 04:32:10 +08:00
del os . environ [ " OPENAI_API_KEY " ]
2024-02-03 04:37:21 +08:00
2024-02-03 02:38:28 +08:00
response = completion (
model = " perplexity/mistral-7b-instruct " ,
messages = [ { " role " : " user " , " content " : " hello " } ] ,
)
os . environ [ " PERPLEXITYAI_API_KEY " ] = old_azure_key
2024-02-03 04:37:21 +08:00
os . environ [ " OPENAI_API_KEY " ] = original_openai_key
2024-02-03 02:38:28 +08:00
pytest . fail ( " Request should have failed - bad api key " )
except openai . AuthenticationError as e :
os . environ [ " PERPLEXITYAI_API_KEY " ] = old_azure_key
2024-02-03 04:37:21 +08:00
os . environ [ " OPENAI_API_KEY " ] = original_openai_key
2024-02-03 02:38:28 +08:00
print ( " exception: " , e )
assert (
2024-08-28 08:39:08 +08:00
" The api_key client option must be set either by passing api_key to the client or by setting the PERPLEXITY_API_KEY environment variable "
2024-02-03 02:38:28 +08:00
in str ( e )
)
except Exception as e :
pytest . fail ( f " Error occurred: { e } " )
# test_completion_perplexity_exception_on_openai_client()
2024-02-03 00:46:42 +08:00
def test_completion_perplexity_exception ( ) :
try :
import openai
print ( " perplexity test \n \n " )
litellm . set_verbose = True
## Test azure call
old_azure_key = os . environ [ " PERPLEXITYAI_API_KEY " ]
os . environ [ " PERPLEXITYAI_API_KEY " ] = " good morning "
response = completion (
model = " perplexity/mistral-7b-instruct " ,
messages = [ { " role " : " user " , " content " : " hello " } ] ,
)
os . environ [ " PERPLEXITYAI_API_KEY " ] = old_azure_key
pytest . fail ( " Request should have failed - bad api key " )
except openai . AuthenticationError as e :
os . environ [ " PERPLEXITYAI_API_KEY " ] = old_azure_key
print ( " exception: " , e )
assert " PerplexityException " in str ( e )
except Exception as e :
pytest . fail ( f " Error occurred: { e } " )
def test_completion_openai_api_key_exception ( ) :
try :
import openai
print ( " gpt-3.5 test \n \n " )
litellm . set_verbose = True
## Test azure call
old_azure_key = os . environ [ " OPENAI_API_KEY " ]
os . environ [ " OPENAI_API_KEY " ] = " good morning "
response = completion (
model = " gpt-3.5-turbo " ,
messages = [ { " role " : " user " , " content " : " hello " } ] ,
)
os . environ [ " OPENAI_API_KEY " ] = old_azure_key
pytest . fail ( " Request should have failed - bad api key " )
except openai . AuthenticationError as e :
os . environ [ " OPENAI_API_KEY " ] = old_azure_key
print ( " exception: " , e )
assert " OpenAIException " in str ( e )
except Exception as e :
pytest . fail ( f " Error occurred: { e } " )
2024-01-23 22:55:04 +08:00
# tesy_async_acompletion()
2024-04-17 11:00:32 +08:00
def test_router_completion_vertex_exception ( ) :
try :
import litellm
litellm . set_verbose = True
router = litellm . Router (
model_list = [
{
" model_name " : " vertex-gemini-pro " ,
" litellm_params " : {
" model " : " vertex_ai/gemini-pro " ,
" api_key " : " good-morning " ,
} ,
} ,
]
)
response = router . completion (
model = " vertex-gemini-pro " ,
messages = [ { " role " : " user " , " content " : " hello " } ] ,
vertex_project = " bad-project " ,
)
pytest . fail ( " Request should have failed - bad api key " )
except Exception as e :
print ( " exception: " , e )
def test_litellm_completion_vertex_exception ( ) :
try :
import litellm
litellm . set_verbose = True
response = completion (
model = " vertex_ai/gemini-pro " ,
api_key = " good-morning " ,
messages = [ { " role " : " user " , " content " : " hello " } ] ,
vertex_project = " bad-project " ,
)
pytest . fail ( " Request should have failed - bad api key " )
except Exception as e :
print ( " exception: " , e )
2024-05-16 07:53:41 +08:00
def test_litellm_predibase_exception ( ) :
"""
Test - Assert that the Predibase API Key is not returned on Authentication Errors
"""
try :
import litellm
litellm . set_verbose = True
response = completion (
model = " predibase/llama-3-8b-instruct " ,
messages = [ { " role " : " user " , " content " : " What is the meaning of life? " } ] ,
tenant_id = " c4768f95 " ,
api_key = " hf-rawapikey " ,
)
pytest . fail ( " Request should have failed - bad api key " )
except Exception as e :
assert " hf-rawapikey " not in str ( e )
print ( " exception: " , e )
2023-11-29 13:11:17 +08:00
# # test_invalid_request_error(model="command-nightly")
# # Test 3: Rate Limit Errors
2023-11-29 13:11:12 +08:00
# def test_model_call(model):
# try:
# sample_text = "how does a court case get to the Supreme Court?"
# messages = [{ "content": sample_text,"role": "user"}]
# print(f"model: {model}")
# response = completion(model=model, messages=messages)
# except RateLimitError as e:
# print(f"headers: {e.response.headers}")
# return True
# # except OpenAIError: # is at least an openai error -> in case of random model errors - e.g. overloaded server
# # return True
# except Exception as e:
# print(f"Uncaught Exception {model}: {type(e).__name__} - {e}")
# traceback.print_exc()
# pass
# return False
# # Repeat each model 500 times
# # extended_models = [model for model in models for _ in range(250)]
2025-10-26 01:19:24 +08:00
# extended_models = ["azure/gpt-4.1-mini" for _ in range(250)]
2023-08-30 03:54:56 +08:00
2023-11-29 13:11:12 +08:00
# def worker(model):
# return test_model_call(model)
2023-08-30 03:54:56 +08:00
2023-11-29 13:11:12 +08:00
# # Create a dictionary to store the results
# counts = {True: 0, False: 0}
2023-08-30 03:54:56 +08:00
2023-11-29 13:11:12 +08:00
# # Use Thread Pool Executor
# with ThreadPoolExecutor(max_workers=500) as executor:
# # Use map to start the operation in thread pool
# results = executor.map(worker, extended_models)
2023-08-30 03:54:56 +08:00
2023-11-29 13:11:12 +08:00
# # Iterate over results and count True/False
# for result in results:
# counts[result] += 1
2023-08-30 03:54:56 +08:00
2023-11-29 13:11:12 +08:00
# accuracy_score = counts[True]/(counts[True] + counts[False])
2023-12-25 16:40:38 +08:00
# print(f"accuracy_score: {accuracy_score}")
2024-06-09 05:32:43 +08:00
2024-07-28 04:13:31 +08:00
@pytest.mark.parametrize (
2026-03-29 10:17:38 +08:00
" provider " ,
[
" predibase " ,
" vertex_ai_beta " ,
" anthropic " ,
" databricks " ,
" watsonx " ,
" fireworks_ai " ,
] ,
2024-07-28 04:13:31 +08:00
)
2024-06-09 05:32:43 +08:00
def test_exception_mapping ( provider ) :
"""
For predibase , run through a set of mock exceptions
assert that they are being mapped correctly
"""
litellm . set_verbose = True
error_map = {
400 : litellm . BadRequestError ,
401 : litellm . AuthenticationError ,
404 : litellm . NotFoundError ,
408 : litellm . Timeout ,
429 : litellm . RateLimitError ,
500 : litellm . InternalServerError ,
503 : litellm . ServiceUnavailableError ,
}
for code , expected_exception in error_map . items ( ) :
mock_response = Exception ( )
setattr ( mock_response , " text " , " This is an error message " )
setattr ( mock_response , " llm_provider " , provider )
setattr ( mock_response , " status_code " , code )
response : Any = None
try :
response = completion (
model = " {} /test-model " . format ( provider ) ,
messages = [ { " role " : " user " , " content " : " Hey, how ' s it going? " } ] ,
mock_response = mock_response ,
)
except expected_exception :
continue
except Exception as e :
2024-08-17 00:22:47 +08:00
traceback . print_exc ( )
response = " {} " . format ( str ( e ) )
2024-06-09 05:32:43 +08:00
pytest . fail (
" Did not raise expected exception. Expected= {} , Return= {} , " . format (
expected_exception , response
)
)
pass
2024-06-22 12:15:10 +08:00
2025-06-06 07:15:53 +08:00
def test_fireworks_ai_exception_mapping ( ) :
2025-06-06 05:47:25 +08:00
"""
2025-06-06 07:15:53 +08:00
Comprehensive test for Fireworks AI exception mapping , including :
1. Standard 429 rate limit errors
2. Text - based rate limit detection ( the main issue fixed )
3. Generic 400 errors that should NOT be rate limits
4. ExceptionCheckers utility function
2026-03-29 10:17:38 +08:00
2025-06-06 07:15:53 +08:00
Related to : https : / / github . com / BerriAI / litellm / pull / 11455
Based on Fireworks AI documentation : https : / / docs . fireworks . ai / tools - sdks / python - client / api - reference
2025-06-06 05:47:25 +08:00
"""
import litellm
from litellm . llms . fireworks_ai . common_utils import FireworksAIException
2025-06-06 07:15:53 +08:00
from litellm . litellm_core_utils . exception_mapping_utils import ExceptionCheckers
2026-03-29 10:17:38 +08:00
2025-06-06 07:15:53 +08:00
# Test scenarios covering all important cases
test_scenarios = [
{
" name " : " Standard 429 rate limit with proper status code " ,
" status_code " : 429 ,
" message " : " Rate limit exceeded. Please try again in 60 seconds. " ,
" expected_exception " : litellm . RateLimitError ,
} ,
{
" name " : " Status 400 with rate limit text (the main issue fixed) " ,
" status_code " : 400 ,
" message " : ' { " error " : { " object " : " error " , " type " : " invalid_request_error " , " message " : " rate limit exceeded, please try again later " }} ' ,
" expected_exception " : litellm . RateLimitError ,
} ,
{
" name " : " Status 400 with generic invalid request (should NOT be rate limit) " ,
" status_code " : 400 ,
" message " : ' { " error " : { " type " : " invalid_request_error " , " message " : " Invalid parameter value " }} ' ,
" expected_exception " : litellm . BadRequestError ,
} ,
]
2026-03-29 10:17:38 +08:00
2025-06-06 07:15:53 +08:00
# Test each scenario
for scenario in test_scenarios :
mock_exception = FireworksAIException (
2026-03-29 10:17:38 +08:00
status_code = scenario [ " status_code " ] , message = scenario [ " message " ] , headers = { }
2025-06-06 05:47:25 +08:00
)
2026-03-29 10:17:38 +08:00
2025-06-06 07:15:53 +08:00
try :
response = litellm . completion (
model = " fireworks_ai/llama-v3p1-70b-instruct " ,
messages = [ { " role " : " user " , " content " : " Hello " } ] ,
mock_response = mock_exception ,
)
2026-03-29 10:17:38 +08:00
pytest . fail (
f " Expected { scenario [ ' expected_exception ' ] . __name__ } to be raised "
)
2025-06-06 07:15:53 +08:00
except scenario [ " expected_exception " ] as e :
if scenario [ " expected_exception " ] == litellm . RateLimitError :
assert " rate limit " in str ( e ) . lower ( ) or " 429 " in str ( e )
except Exception as e :
2026-03-29 10:17:38 +08:00
pytest . fail (
f " Expected { scenario [ ' expected_exception ' ] . __name__ } but got { type ( e ) . __name__ } : { e } "
)
2025-06-06 07:15:53 +08:00
# Test ExceptionCheckers.is_error_str_rate_limit() method directly
2026-03-29 10:17:38 +08:00
2025-06-06 07:15:53 +08:00
# Test cases that should return True (rate limit detected)
rate_limit_strings = [
" 429 rate limit exceeded " ,
2026-03-29 10:17:38 +08:00
" Rate limit exceeded, please try again later " ,
2025-06-06 07:15:53 +08:00
" RATE LIMIT ERROR " ,
" Error 429: rate limit " ,
' { " error " : { " type " : " invalid_request_error " , " message " : " rate limit exceeded, please try again later " }} ' ,
" HTTP 429 Too Many Requests " ,
]
2026-03-29 10:17:38 +08:00
2025-06-06 07:15:53 +08:00
for error_str in rate_limit_strings :
2026-03-29 10:17:38 +08:00
assert ExceptionCheckers . is_error_str_rate_limit (
error_str
) , f " Should detect rate limit in: { error_str } "
2025-06-06 07:15:53 +08:00
# Test cases that should return False (not rate limit)
non_rate_limit_strings = [
" 400 Bad Request " ,
2026-03-29 10:17:38 +08:00
" Authentication failed " ,
2025-06-06 07:15:53 +08:00
" Invalid model specified " ,
" Context window exceeded " ,
" Internal server error " ,
" " ,
" Some other error message " ,
]
2026-03-29 10:17:38 +08:00
2025-06-06 07:15:53 +08:00
for error_str in non_rate_limit_strings :
2026-03-29 10:17:38 +08:00
assert not ExceptionCheckers . is_error_str_rate_limit (
error_str
) , f " Should NOT detect rate limit in: { error_str } "
2025-06-06 07:15:53 +08:00
# Test edge cases
assert not ExceptionCheckers . is_error_str_rate_limit ( None ) # type: ignore
assert not ExceptionCheckers . is_error_str_rate_limit ( 42 ) # type: ignore
2025-06-06 05:47:25 +08:00
2024-06-22 12:15:10 +08:00
def test_anthropic_tool_calling_exception ( ) :
"""
Related - https : / / github . com / BerriAI / litellm / issues / 4348
"""
tools = [
{
" type " : " function " ,
" function " : {
" name " : " get_current_weather " ,
" description " : " Get the current weather in a given location " ,
2024-06-22 12:20:49 +08:00
" parameters " : { } ,
2024-06-22 12:15:10 +08:00
} ,
}
]
2024-06-22 12:20:49 +08:00
try :
litellm . completion (
2026-03-12 21:04:20 +08:00
model = " claude-haiku-4-5-20251001 " ,
2024-06-22 12:20:49 +08:00
messages = [ { " role " : " user " , " content " : " Hey, how ' s it going? " } ] ,
tools = tools ,
)
except litellm . BadRequestError :
pass
2024-08-25 03:55:15 +08:00
from typing import Optional , Union
from openai import AsyncOpenAI , OpenAI
def _pre_call_utils (
call_type : str ,
data : dict ,
client : Union [ OpenAI , AsyncOpenAI ] ,
sync_mode : bool ,
streaming : Optional [ bool ] ,
) :
if call_type == " embedding " :
data [ " input " ] = " Hello world! "
2024-08-28 03:14:23 +08:00
mapped_target : Any = client . embeddings . with_raw_response
2024-08-25 03:55:15 +08:00
if sync_mode :
original_function = litellm . embedding
else :
original_function = litellm . aembedding
elif call_type == " chat_completion " :
data [ " messages " ] = [ { " role " : " user " , " content " : " Hello world " } ]
if streaming is True :
data [ " stream " ] = True
2024-09-10 09:54:17 +08:00
mapped_target = client . chat . completions . with_raw_response # type: ignore
2024-08-25 03:55:15 +08:00
if sync_mode :
original_function = litellm . completion
else :
original_function = litellm . acompletion
2024-08-25 04:25:17 +08:00
elif call_type == " completion " :
data [ " prompt " ] = " Hello world "
if streaming is True :
data [ " stream " ] = True
2024-09-10 09:54:17 +08:00
mapped_target = client . completions . with_raw_response # type: ignore
2024-08-25 04:25:17 +08:00
if sync_mode :
original_function = litellm . text_completion
else :
original_function = litellm . atext_completion
2024-08-25 03:55:15 +08:00
return data , original_function , mapped_target
2024-09-28 13:52:57 +08:00
def _pre_call_utils_httpx (
call_type : str ,
data : dict ,
client : Union [ HTTPHandler , AsyncHTTPHandler ] ,
sync_mode : bool ,
streaming : Optional [ bool ] ,
) :
mapped_target : Any = client . client
if call_type == " embedding " :
data [ " input " ] = " Hello world! "
if sync_mode :
original_function = litellm . embedding
else :
original_function = litellm . aembedding
elif call_type == " chat_completion " :
data [ " messages " ] = [ { " role " : " user " , " content " : " Hello world " } ]
if streaming is True :
data [ " stream " ] = True
if sync_mode :
original_function = litellm . completion
else :
original_function = litellm . acompletion
elif call_type == " completion " :
data [ " prompt " ] = " Hello world "
if streaming is True :
data [ " stream " ] = True
if sync_mode :
original_function = litellm . text_completion
else :
original_function = litellm . atext_completion
return data , original_function , mapped_target
2024-08-25 03:55:15 +08:00
@pytest.mark.parametrize (
" sync_mode " ,
[ True , False ] ,
)
@pytest.mark.parametrize (
2024-08-25 06:12:51 +08:00
" provider, model, call_type, streaming " ,
2024-08-25 03:55:15 +08:00
[
2024-08-25 06:12:51 +08:00
( " openai " , " text-embedding-ada-002 " , " embedding " , None ) ,
( " openai " , " gpt-3.5-turbo " , " chat_completion " , False ) ,
( " openai " , " gpt-3.5-turbo " , " chat_completion " , True ) ,
( " openai " , " gpt-3.5-turbo-instruct " , " completion " , True ) ,
2025-10-26 01:19:24 +08:00
( " azure " , " azure/gpt-4.1-mini " , " chat_completion " , True ) ,
2024-08-25 06:12:51 +08:00
( " azure " , " azure/text-embedding-ada-002 " , " embedding " , True ) ,
( " azure " , " azure_text/gpt-3.5-turbo-instruct " , " completion " , True ) ,
2024-08-25 03:55:15 +08:00
] ,
)
@pytest.mark.asyncio
2024-08-25 06:12:51 +08:00
async def test_exception_with_headers ( sync_mode , provider , model , call_type , streaming ) :
2024-08-25 03:55:15 +08:00
"""
User feedback : litellm says " No deployments available for selected model, Try again in 60 seconds "
but Azure says to retry in at most 9 s
` ` `
{ " message " : " litellm.proxy.proxy_server.embeddings(): Exception occured - No deployments available for selected model, Try again in 60 seconds. Passed model=text-embedding-ada-002. pre-call-checks=False, allowed_model_region=n/a, cooldown_list=[( ' b49cbc9314273db7181fe69b1b19993f04efb88f2c1819947c538bac08097e4c ' , { ' Exception Received ' : ' litellm.RateLimitError: AzureException RateLimitError - Requests to the Embeddings_Create Operation under Azure OpenAI API version 2023-09-01-preview have exceeded call rate limit of your current OpenAI S0 pricing tier. Please retry after 9 seconds. Please go here: https://aka.ms/oai/quotaincrease if you would like to further increase the default rate limit. ' , ' Status Code ' : ' 429 ' })] " , " level " : " ERROR " , " timestamp " : " 2024-08-22T03:25:36.900476 " }
` ` `
"""
2024-09-22 09:51:53 +08:00
print ( f " Received args: { locals ( ) } " )
2024-08-25 03:55:15 +08:00
import openai
if sync_mode :
2024-08-25 06:12:51 +08:00
if provider == " openai " :
openai_client = openai . OpenAI ( api_key = " " )
elif provider == " azure " :
2024-08-28 10:32:37 +08:00
openai_client = openai . AzureOpenAI (
api_key = " " , base_url = " " , api_version = litellm . AZURE_DEFAULT_API_VERSION
)
2024-08-25 03:55:15 +08:00
else :
2024-08-25 06:12:51 +08:00
if provider == " openai " :
openai_client = openai . AsyncOpenAI ( api_key = " " )
elif provider == " azure " :
2024-08-28 10:32:37 +08:00
openai_client = openai . AsyncAzureOpenAI (
api_key = " " , base_url = " " , api_version = litellm . AZURE_DEFAULT_API_VERSION
)
2024-08-25 03:55:15 +08:00
data = { " model " : model }
data , original_function , mapped_target = _pre_call_utils (
call_type = call_type ,
data = data ,
client = openai_client ,
sync_mode = sync_mode ,
streaming = streaming ,
)
cooldown_time = 30.0
def _return_exception ( * args , * * kwargs ) :
2024-09-22 09:51:53 +08:00
import datetime
from httpx import Headers , Request , Response
kwargs = {
" request " : Request ( " POST " , " https://www.google.com " ) ,
" message " : " Error code: 429 - Rate Limit Error! " ,
" body " : { " detail " : " Rate Limit Error! " } ,
" code " : None ,
" param " : None ,
" type " : None ,
" response " : Response (
status_code = 429 ,
headers = Headers (
{
" date " : " Sat, 21 Sep 2024 22:56:53 GMT " ,
" server " : " uvicorn " ,
" retry-after " : " 30 " ,
" content-length " : " 30 " ,
" content-type " : " application/json " ,
}
) ,
request = Request ( " POST " , " http://0.0.0.0:9000/chat/completions " ) ,
) ,
" status_code " : 429 ,
" request_id " : None ,
}
2024-08-25 03:55:15 +08:00
2024-09-22 09:51:53 +08:00
exception = Exception ( )
for k , v in kwargs . items ( ) :
setattr ( exception , k , v )
raise exception
2024-08-25 03:55:15 +08:00
with patch . object (
mapped_target ,
" create " ,
side_effect = _return_exception ,
) :
new_retry_after_mock_client = MagicMock ( return_value = - 1 )
litellm . utils . _get_retry_after_from_exception_header = (
new_retry_after_mock_client
)
2024-08-25 04:25:17 +08:00
exception_raised = False
2024-08-25 03:55:15 +08:00
try :
if sync_mode :
2024-08-25 04:25:17 +08:00
resp = original_function ( * * data , client = openai_client )
2024-08-25 03:55:15 +08:00
if streaming :
for chunk in resp :
continue
else :
2024-08-25 04:25:17 +08:00
resp = await original_function ( * * data , client = openai_client )
2024-08-25 03:55:15 +08:00
if streaming :
async for chunk in resp :
continue
except litellm . RateLimitError as e :
2024-08-25 04:25:17 +08:00
exception_raised = True
2024-08-25 03:55:15 +08:00
assert e . litellm_response_headers is not None
2024-09-22 09:51:53 +08:00
assert int ( e . litellm_response_headers [ " retry-after " ] ) == cooldown_time
2024-08-25 04:25:17 +08:00
if exception_raised is False :
print ( resp )
assert exception_raised
2024-09-28 13:52:57 +08:00
2025-05-01 13:11:12 +08:00
def test_openai_gateway_timeout_error ( ) :
"""
Test that the OpenAI gateway timeout error is raised
"""
openai_client = OpenAI ( )
mapped_target = openai_client . chat . completions . with_raw_response # type: ignore
2026-03-29 10:17:38 +08:00
2025-05-01 13:11:12 +08:00
def _return_exception ( * args , * * kwargs ) :
import datetime
from httpx import Headers , Request , Response
kwargs = {
" request " : Request ( " POST " , " https://www.google.com " ) ,
" message " : " Error code: 504 - Gateway Timeout Error! " ,
" body " : { " detail " : " Gateway Timeout Error! " } ,
" code " : None ,
" param " : None ,
" type " : None ,
" response " : Response (
status_code = 504 ,
headers = Headers (
{
" date " : " Sat, 21 Sep 2024 22:56:53 GMT " ,
" server " : " uvicorn " ,
" content-length " : " 30 " ,
" content-type " : " application/json " ,
}
) ,
request = Request ( " POST " , " http://0.0.0.0:9000/chat/completions " ) ,
) ,
" status_code " : 504 ,
" request_id " : None ,
}
exception = Exception ( )
for k , v in kwargs . items ( ) :
setattr ( exception , k , v )
raise exception
2026-03-29 10:17:38 +08:00
try :
2025-05-01 13:11:12 +08:00
with patch . object (
mapped_target ,
" create " ,
side_effect = _return_exception ,
) :
2026-03-29 10:17:38 +08:00
litellm . completion (
model = " openai/gpt-3.5-turbo " ,
messages = [ { " role " : " user " , " content " : " Hello world " } ] ,
client = openai_client ,
)
2025-05-01 13:11:12 +08:00
pytest . fail ( " Expected to raise Timeout " )
except litellm . Timeout as e :
assert e . status_code == 504
2024-09-28 13:52:57 +08:00
@pytest.mark.parametrize (
" sync_mode " ,
[ True , False ] ,
)
@pytest.mark.parametrize ( " streaming " , [ True , False ] )
@pytest.mark.parametrize (
" provider, model, call_type " ,
[
( " anthropic " , " claude-3-haiku-20240307 " , " chat_completion " ) ,
] ,
)
@pytest.mark.asyncio
async def test_exception_with_headers_httpx (
sync_mode , provider , model , call_type , streaming
) :
"""
User feedback : litellm says " No deployments available for selected model, Try again in 60 seconds "
but Azure says to retry in at most 9 s
` ` `
{ " message " : " litellm.proxy.proxy_server.embeddings(): Exception occured - No deployments available for selected model, Try again in 60 seconds. Passed model=text-embedding-ada-002. pre-call-checks=False, allowed_model_region=n/a, cooldown_list=[( ' b49cbc9314273db7181fe69b1b19993f04efb88f2c1819947c538bac08097e4c ' , { ' Exception Received ' : ' litellm.RateLimitError: AzureException RateLimitError - Requests to the Embeddings_Create Operation under Azure OpenAI API version 2023-09-01-preview have exceeded call rate limit of your current OpenAI S0 pricing tier. Please retry after 9 seconds. Please go here: https://aka.ms/oai/quotaincrease if you would like to further increase the default rate limit. ' , ' Status Code ' : ' 429 ' })] " , " level " : " ERROR " , " timestamp " : " 2024-08-22T03:25:36.900476 " }
` ` `
"""
print ( f " Received args: { locals ( ) } " )
import openai
if sync_mode :
client = HTTPHandler ( )
else :
client = AsyncHTTPHandler ( )
data = { " model " : model }
data , original_function , mapped_target = _pre_call_utils_httpx (
call_type = call_type ,
data = data ,
client = client ,
sync_mode = sync_mode ,
streaming = streaming ,
)
cooldown_time = 30.0
def _return_exception ( * args , * * kwargs ) :
import datetime
from httpx import Headers , HTTPStatusError , Request , Response
# Create the Request object
request = Request ( " POST " , " http://0.0.0.0:9000/chat/completions " )
# Create the Response object with the necessary headers and status code
response = Response (
status_code = 429 ,
headers = Headers (
{
" date " : " Sat, 21 Sep 2024 22:56:53 GMT " ,
" server " : " uvicorn " ,
" retry-after " : " 30 " ,
" content-length " : " 30 " ,
" content-type " : " application/json " ,
}
) ,
request = request ,
)
# Create and raise the HTTPStatusError exception
raise HTTPStatusError (
message = " Error code: 429 - Rate Limit Error! " ,
request = request ,
response = response ,
)
with patch . object (
mapped_target ,
" send " ,
side_effect = _return_exception ,
) :
new_retry_after_mock_client = MagicMock ( return_value = - 1 )
litellm . utils . _get_retry_after_from_exception_header = (
new_retry_after_mock_client
)
exception_raised = False
try :
if sync_mode :
resp = original_function ( * * data , client = client )
if streaming :
for chunk in resp :
continue
else :
resp = await original_function ( * * data , client = client )
if streaming :
async for chunk in resp :
continue
except litellm . RateLimitError as e :
exception_raised = True
2024-12-01 21:24:11 +08:00
assert (
e . litellm_response_headers is not None
) , " litellm_response_headers is None "
2024-09-28 13:52:57 +08:00
print ( " e.litellm_response_headers " , e . litellm_response_headers )
assert int ( e . litellm_response_headers [ " retry-after " ] ) == cooldown_time
if exception_raised is False :
print ( resp )
assert exception_raised
2024-11-15 07:54:28 +08:00
@pytest.mark.asyncio
2025-10-26 01:19:24 +08:00
@pytest.mark.parametrize ( " model " , [ " azure/gpt-4.1-mini " , " openai/gpt-3.5-turbo " ] )
2024-11-15 07:54:28 +08:00
async def test_bad_request_error_contains_httpx_response ( model ) :
"""
Test that the BadRequestError contains the httpx response
Relevant issue : https : / / github . com / BerriAI / litellm / issues / 6732
"""
try :
await litellm . acompletion (
model = model ,
messages = [ { " role " : " user " , " content " : " Hello world " } ] ,
bad_arg = " bad_arg " ,
)
pytest . fail ( " Expected to raise BadRequestError " )
except litellm . BadRequestError as e :
print ( " e.response " , e . response )
print ( " vars(e.response) " , vars ( e . response ) )
assert e . response is not None
2024-12-03 14:00:01 +08:00
def test_exceptions_base_class ( ) :
try :
raise litellm . RateLimitError (
message = " BedrockException: Rate Limit Error " ,
model = " model " ,
llm_provider = " bedrock " ,
)
except litellm . RateLimitError as e :
assert isinstance ( e , litellm . RateLimitError )
assert e . code == " 429 "
assert e . type == " throttling_error "
2024-12-28 11:04:39 +08:00
def test_context_window_exceeded_error_from_litellm_proxy ( ) :
from httpx import Response
from litellm . litellm_core_utils . exception_mapping_utils import (
extract_and_raise_litellm_exception ,
)
args = {
" response " : Response ( status_code = 400 , text = " Bad Request " ) ,
" error_str " : " Error code: 400 - { ' error ' : { ' message ' : \" litellm.ContextWindowExceededError: litellm.BadRequestError: this is a mock context window exceeded error \\ nmodel=gpt-3.5-turbo. context_window_fallbacks=None. fallbacks=None. \\ n \\ nSet ' context_window_fallback ' - https://docs.litellm.ai/docs/routing#fallbacks \\ nReceived Model Group=gpt-3.5-turbo \\ nAvailable Model Group Fallbacks=None \" , ' type ' : None, ' param ' : None, ' code ' : ' 400 ' }} " ,
" model " : " gpt-3.5-turbo " ,
" custom_llm_provider " : " litellm_proxy " ,
}
with pytest . raises ( litellm . ContextWindowExceededError ) :
extract_and_raise_litellm_exception ( * * args )
2025-03-11 06:59:06 +08:00
2026-01-23 02:58:29 +08:00
def test_bad_request_error_with_response_without_request ( ) :
"""
Test that BadRequestError handles Response objects without a request attribute .
2026-03-29 10:17:38 +08:00
2026-01-23 02:58:29 +08:00
This simulates a real scenario where a Response is created without a request
( e . g . , in tests or when manually creating error responses ) , and we need to
ensure it doesn ' t raise RuntimeError when the exception is created.
"""
from httpx import Response
from litellm . litellm_core_utils . exception_mapping_utils import (
extract_and_raise_litellm_exception ,
)
# Create a Response without a request (simulates the scenario that was failing)
response_without_request = Response ( status_code = 400 , text = " Bad Request " )
2026-03-29 10:17:38 +08:00
2026-01-23 02:58:29 +08:00
# Test that extract_and_raise_litellm_exception can handle this
args = {
" response " : response_without_request ,
" error_str " : " Error code: 400 - { ' error ' : { ' message ' : ' litellm.BadRequestError: Invalid request parameters ' , ' type ' : None, ' param ' : None, ' code ' : ' 400 ' }} " ,
" model " : " gpt-3.5-turbo " ,
" custom_llm_provider " : " openai " ,
}
2026-03-29 10:17:38 +08:00
2026-01-23 02:58:29 +08:00
# This should raise BadRequestError without RuntimeError
with pytest . raises ( litellm . BadRequestError ) as exc_info :
extract_and_raise_litellm_exception ( * * args )
2026-03-29 10:17:38 +08:00
2026-01-23 02:58:29 +08:00
# Verify the exception was created successfully
error = exc_info . value
assert error is not None
assert error . model == " gpt-3.5-turbo "
assert error . llm_provider == " openai "
2026-03-29 10:17:38 +08:00
2026-01-23 02:58:29 +08:00
# Verify the exception has a response (should be minimal error response)
assert error . response is not None
# The response should have a request (minimal error response has one)
assert getattr ( error . response , " _request " , None ) is not None
# Should be able to access request property without RuntimeError
assert error . response . request is not None
2025-03-11 06:59:06 +08:00
@pytest.mark.parametrize ( " sync_mode " , [ True , False ] )
@pytest.mark.parametrize ( " stream_mode " , [ True , False ] )
2025-09-28 04:58:31 +08:00
@pytest.mark.parametrize ( " model " , [ " gpt-4.1-nano " ] ) # "gpt-4o-mini",
2025-03-11 06:59:06 +08:00
@pytest.mark.asyncio
async def test_exception_bubbling_up ( sync_mode , stream_mode , model ) :
"""
make sure code , param , and type are bubbled up
"""
import litellm
litellm . set_verbose = True
with pytest . raises ( Exception ) as exc_info :
if sync_mode :
litellm . completion (
model = model ,
messages = [ { " role " : " usera " , " content " : " hi " } ] ,
stream = stream_mode ,
sync_stream = sync_mode ,
)
else :
await litellm . acompletion (
model = model ,
messages = [ { " role " : " usera " , " content " : " hi " } ] ,
stream = stream_mode ,
sync_stream = sync_mode ,
)
assert exc_info . value . code == " invalid_value "
assert exc_info . value . param is not None
assert exc_info . value . type == " invalid_request_error "