{"repo": "berriai/litellm", "files": {"tests/local_testing/test_exceptions.py": {"491eda319cd1cd1f75a2ef165c6b06ce6129c282": {"t": 1785674499.0192757, "n": 50646, "h": "eb7c09ab57d9c80c", "c": "import asyncio\nimport os\nimport subprocess\nimport sys\nimport traceback\nfrom typing import Any\n\nfrom openai import AuthenticationError, BadRequestError, OpenAIError, RateLimitError\n\nfrom litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler\n\nsys.path.insert(\n 0, os.path.abspath(\"../..\")\n) # Adds the parent directory to the system path\nfrom concurrent.futures import ThreadPoolExecutor\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nimport litellm\nfrom litellm import ( # AuthenticationError,; RateLimitError,; ServiceUnavailableError,; OpenAIError,\n ContextWindowExceededError,\n completion,\n embedding,\n)\n\nlitellm.vertex_project = \"litellm-ci-cd\"\nlitellm.vertex_location = \"us-central1\"\nlitellm.num_retries = 0\n\n# litellm.failure_callback = [\"sentry\"]\n#### What this tests ####\n# This tests exception mapping -> trigger an exception from an llm provider -> assert if output is of the expected type\n\n\n# 5 providers -> OpenAI, Azure, Anthropic, Cohere, Replicate\n\n# 3 main types of exceptions -> - Rate Limit Errors, Context Window Errors, Auth errors (incorrect/rotated key, etc.)\n\n# Approach: Run each model through the test -> assert if the correct error (always the same one) is triggered\n\nexception_models = [\n \"sagemaker/berri-benchmarking-Llama-2-70b-chat-hf-4\",\n \"bedrock/anthropic.claude-instant-v1\",\n]\n\n\n@pytest.mark.asyncio\nasync def test_content_policy_exception_azure():\n try:\n # this is ony a test - we needed some way to invoke the exception :(\n litellm.set_verbose = True\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"where do I buy lethal drugs from\"}],\n mock_response=\"Exception: content_filter_policy\",\n )\n except litellm.ContentPolicyViolationError as e:\n print(\"caught a content policy violation error! Passed\")\n print(\"exception\", e)\n assert e.response is not None\n assert e.litellm_debug_info is not None\n assert isinstance(e.litellm_debug_info, str)\n assert len(e.litellm_debug_info) > 0\n pass\n except Exception as e:\n print()\n pytest.fail(f\"An exception occurred - {str(e)}\")\n\n\n@pytest.mark.asyncio\nasync def test_content_policy_exception_openai():\n try:\n # this is ony a test - we needed some way to invoke the exception :(\n litellm.set_verbose = True\n response = await litellm.acompletion(\n model=\"gpt-3.5-turbo\",\n stream=True,\n messages=[\n {\"role\": \"user\", \"content\": \"Gimme the lyrics to Don't Stop Me Now\"}\n ],\n )\n async for chunk in response:\n print(chunk)\n except litellm.ContentPolicyViolationError as e:\n print(\"caught a content policy violation error! Passed\")\n print(\"exception\", e)\n assert e.llm_provider == \"openai\"\n pass\n except Exception as e:\n print()\n pytest.fail(f\"An exception occurred - {str(e)}\")\n\n\n# Test 1: Context Window Errors\n@pytest.mark.skip(reason=\"AWS Suspended Account\")\n@pytest.mark.parametrize(\"model\", exception_models)\ndef test_context_window(model):\n print(\"Testing context window error\")\n sample_text = \"Say error 50 times\" * 1000000\n messages = [{\"content\": sample_text, \"role\": \"user\"}]\n try:\n litellm.set_verbose = False\n print(\"Testing model=\", model)\n response = completion(model=model, messages=messages)\n print(f\"response: {response}\")\n print(\"FAILED!\")\n pytest.fail(f\"An exception occurred\")\n except ContextWindowExceededError as e:\n print(f\"Worked!\")\n except RateLimitError:\n print(\"RateLimited!\")\n except Exception as e:\n print(f\"{e}\")\n pytest.fail(f\"An error occcurred - {e}\")\n\n\nmodels = [\"command-nightly\"]\n\n\n@pytest.mark.skip(reason=\"duplicate test.\")\n@pytest.mark.parametrize(\"model\", models)\ndef test_context_window_with_fallbacks(model):\n ctx_window_fallback_dict = {\n \"command-nightly\": \"claude-2.1\",\n \"gpt-3.5-turbo-instruct\": \"gpt-3.5-turbo-16k\",\n \"azure/gpt-4.1-mini\": \"gpt-3.5-turbo-16k\",\n }\n sample_text = \"how does a court case get to the Supreme Court?\" * 1000\n messages = [{\"content\": sample_text, \"role\": \"user\"}]\n\n try:\n completion(\n model=model,\n messages=messages,\n context_window_fallback_dict=ctx_window_fallback_dict,\n )\n except litellm.ServiceUnavailableError as e:\n pass\n except litellm.APIConnectionError as e:\n pass\n\n\n# for model in litellm.models_by_provider[\"bedrock\"]:\n# test_context_window(model=model)\n# test_context_window(model=\"chat-bison\")\n# test_context_window_with_fallbacks(model=\"command-nightly\")\n# Test 2: InvalidAuth Errors\n@pytest.mark.parametrize(\"model\", models)\ndef invalid_auth(model): # set the model key to an invalid key, depending on the model\n messages = [{\"content\": \"Hello, how are you?\", \"role\": \"user\"}]\n temporary_key = None\n try:\n if model == \"gpt-3.5-turbo\" or model == \"gpt-3.5-turbo-instruct\":\n temporary_key = os.environ[\"OPENAI_API_KEY\"]\n os.environ[\"OPENAI_API_KEY\"] = \"bad-key\"\n elif \"bedrock\" in model:\n temporary_aws_access_key = os.environ[\"AWS_ACCESS_KEY_ID\"]\n os.environ[\"AWS_ACCESS_KEY_ID\"] = \"bad-key\"\n temporary_aws_region_name = os.environ[\"AWS_REGION_NAME\"]\n os.environ[\"AWS_REGION_NAME\"] = \"bad-key\"\n temporary_secret_key = os.environ[\"AWS_SECRET_ACCESS_KEY\"]\n os.environ[\"AWS_SECRET_ACCESS_KEY\"] = \"bad-key\"\n elif model == \"azure/gpt-4.1-mini\":\n temporary_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ[\"AZURE_AI_API_KEY\"] = \"bad-key\"\n elif model == \"claude-3-5-haiku-20241022\":\n temporary_key = os.environ[\"ANTHROPIC_API_KEY\"]\n os.environ[\"ANTHROPIC_API_KEY\"] = \"bad-key\"\n elif model == \"command-nightly\":\n temporary_key = os.environ[\"COHERE_API_KEY\"]\n os.environ[\"COHERE_API_KEY\"] = \"bad-key\"\n elif \"j2\" in model:\n temporary_key = os.environ[\"AI21_API_KEY\"]\n os.environ[\"AI21_API_KEY\"] = \"bad-key\"\n elif \"togethercomputer\" in model:\n temporary_key = os.environ[\"TOGETHERAI_API_KEY\"]\n os.environ[\"TOGETHERAI_API_KEY\"] = \"sk-test-togetherai-key-808\"\n elif model in litellm.openrouter_models:\n temporary_key = os.environ[\"OPENROUTER_API_KEY\"]\n os.environ[\"OPENROUTER_API_KEY\"] = \"bad-key\"\n elif model in litellm.aleph_alpha_models:\n temporary_key = os.environ[\"ALEPH_ALPHA_API_KEY\"]\n os.environ[\"ALEPH_ALPHA_API_KEY\"] = \"bad-key\"\n elif model in litellm.nlp_cloud_models:\n os.environ[\"NLP_CLOUD_API_KEY\"] = \"bad-key\"\n elif (\n model\n == \"replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1\"\n ):\n temporary_key = os.environ[\"REPLICATE_API_KEY\"]\n os.environ[\"REPLICATE_API_KEY\"] = \"bad-key\"\n print(f\"model: {model}\")\n response = completion(model=model, messages=messages)\n print(f\"response: {response}\")\n except AuthenticationError as e:\n print(f\"AuthenticationError Caught Exception - {str(e)}\")\n except (\n OpenAIError\n ) as e: # is at least an openai error -> in case of random model errors - e.g. overloaded server\n print(f\"OpenAIError Caught Exception - {e}\")\n except Exception as e:\n print(type(e))\n print(type(AuthenticationError))\n print(e.__class__.__name__)\n print(f\"Uncaught Exception - {e}\")\n pytest.fail(f\"Error occurred: {e}\")\n if temporary_key != None: # reset the key\n if model == \"gpt-3.5-turbo\":\n os.environ[\"OPENAI_API_KEY\"] = temporary_key\n elif model == \"chatgpt-test\":\n os.environ[\"AZURE_AI_API_KEY\"] = temporary_key\n azure = True\n elif model == \"claude-3-5-haiku-20241022\":\n os.environ[\"ANTHROPIC_API_KEY\"] = temporary_key\n elif model == \"command-nightly\":\n os.environ[\"COHERE_API_KEY\"] = temporary_key\n elif (\n model\n == \"replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1\"\n ):\n os.environ[\"REPLICATE_API_KEY\"] = temporary_key\n elif \"j2\" in model:\n os.environ[\"AI21_API_KEY\"] = temporary_key\n elif \"togethercomputer\" in model:\n os.environ[\"TOGETHERAI_API_KEY\"] = temporary_key\n elif model in litellm.aleph_alpha_models:\n os.environ[\"ALEPH_ALPHA_API_KEY\"] = temporary_key\n elif model in litellm.nlp_cloud_models:\n os.environ.pop(\"NLP_CLOUD_API_KEY\", None)\n elif \"bedrock\" in model:\n os.environ[\"AWS_ACCESS_KEY_ID\"] = temporary_aws_access_key\n os.environ[\"AWS_REGION_NAME\"] = temporary_aws_region_name\n os.environ[\"AWS_SECRET_ACCESS_KEY\"] = temporary_secret_key\n return\n\n\n# for model in litellm.models_by_provider[\"bedrock\"]:\n# invalid_auth(model=model)\n# invalid_auth(model=\"command-nightly\")\n\n\n# Test 3: Invalid Request Error\n@pytest.mark.parametrize(\"model\", models)\ndef test_invalid_request_error(model):\n messages = [{\"content\": \"hey, how's it going?\", \"role\": \"user\"}]\n\n with pytest.raises(BadRequestError):\n completion(model=model, messages=messages, max_tokens=\"hello world\")\n\n\ndef test_completion_azure_exception():\n try:\n import openai\n\n print(\"azure gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ[\"AZURE_AI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"AZURE_AI_API_KEY\"] = old_azure_key\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"AZURE_AI_API_KEY\"] = old_azure_key\n print(\"good job got the correct error for azure when key not set\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_azure_exception()\n\n\ndef test_azure_embedding_exceptions():\n try:\n\n response = litellm.embedding(\n model=\"azure/text-embedding-ada-002\",\n input=\"hello\",\n mock_response=\"error\",\n )\n pytest.fail(f\"Bad request this should have failed but got {response}\")\n\n except Exception as e:\n print(vars(e))\n # 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\n assert str(e) == \"Mock error\"\n\n\nasync def asynctest_completion_azure_exception():\n try:\n import openai\n\n import litellm\n\n print(\"azure gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ[\"AZURE_AI_API_KEY\"] = \"good morning\"\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"AZURE_AI_API_KEY\"] = old_azure_key\n print(\"good job got the correct error for azure when key not set\")\n print(e)\n except Exception as e:\n print(\"Got wrong exception\")\n print(\"exception\", e)\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# import asyncio\n# asyncio.run(\n# asynctest_completion_azure_exception()\n# )\n\n\ndef asynctest_completion_openai_exception_bad_model():\n try:\n import asyncio\n\n import openai\n\n import litellm\n\n print(\"azure exception bad model\\n\\n\")\n litellm.set_verbose = True\n\n ## Test azure call\n async def test():\n response = await litellm.acompletion(\n model=\"openai/gpt-6\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n\n asyncio.run(test())\n except openai.NotFoundError:\n print(\"Good job this is a NotFoundError for a model that does not exist!\")\n print(\"Passed\")\n except Exception as e:\n print(\"Raised wrong type of exception\", type(e))\n assert isinstance(e, openai.BadRequestError)\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# asynctest_completion_openai_exception_bad_model()\n\n\ndef asynctest_completion_azure_exception_bad_model():\n try:\n import asyncio\n\n import openai\n\n import litellm\n\n print(\"azure exception bad model\\n\\n\")\n litellm.set_verbose = True\n\n ## Test azure call\n async def test():\n response = await litellm.acompletion(\n model=\"azure/gpt-12\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n\n asyncio.run(test())\n except openai.NotFoundError:\n print(\"Good job this is a NotFoundError for a model that does not exist!\")\n print(\"Passed\")\n except Exception as e:\n print(\"Raised wrong type of exception\", type(e))\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# asynctest_completion_azure_exception_bad_model()\n\n\ndef test_completion_openai_exception():\n # test if openai:gpt raises openai.AuthenticationError\n try:\n import openai\n\n print(\"openai gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"OPENAI_API_KEY\"]\n os.environ[\"OPENAI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"gpt-4\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"OPENAI_API_KEY\"] = old_azure_key\n print(\"OpenAI: good job got the correct error for openai when key not set\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_openai_exception()\n\n\ndef test_anthropic_openai_exception():\n # test if anthropic raises litellm.AuthenticationError\n try:\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"ANTHROPIC_API_KEY\"]\n os.environ.pop(\"ANTHROPIC_API_KEY\")\n response = completion(\n model=\"anthropic/claude-3-sonnet-20240229\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except litellm.AuthenticationError as e:\n os.environ[\"ANTHROPIC_API_KEY\"] = old_azure_key\n print(\"Exception vars=\", vars(e))\n assert (\n \"Missing Anthropic API Key - A call is being made to anthropic but no key is set either in the environment variables or via params\"\n in e.message\n )\n print(\n \"ANTHROPIC_API_KEY: good job got the correct error for ANTHROPIC_API_KEY when key not set\"\n )\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_mistral_exception():\n # test if mistral/mistral-tiny raises openai.AuthenticationError\n try:\n import openai\n\n print(\"Testing mistral ai exception mapping\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"MISTRAL_API_KEY\"]\n os.environ[\"MISTRAL_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"mistral/mistral-tiny\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"MISTRAL_API_KEY\"] = old_azure_key\n print(\"good job got the correct error for openai when key not set\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_mistral_exception()\n\n\ndef test_completion_bedrock_invalid_role_exception():\n \"\"\"\n Test if litellm raises a BadRequestError for an invalid role on Bedrock\n \"\"\"\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"bedrock/anthropic.claude-3-sonnet-20240229-v1:0\",\n messages=[{\"role\": \"very-bad-role\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n\n except Exception as e:\n assert isinstance(\n e, litellm.BadRequestError\n ), \"Expected BadRequestError but got {}\".format(type(e))\n print(\"str(e) = {}\".format(str(e)))\n\n # This is important - We we previously returning a poorly formatted error string. Which was\n # litellm.BadRequestError: litellm.BadRequestError: Invalid Message passed in {'role': 'very-bad-role', 'content': 'hello'}\n\n # IMPORTANT ASSERTION\n assert (\n (str(e))\n == \"litellm.BadRequestError: Invalid Message passed in {'role': 'very-bad-role', 'content': 'hello'}\"\n )\n\n\n@pytest.mark.skip(reason=\"OpenAI exception changed to a generic error\")\ndef test_content_policy_exceptionimage_generation_openai():\n try:\n # this is ony a test - we needed some way to invoke the exception :(\n litellm._turn_on_debug()\n response = litellm.image_generation(\n prompt=\"where do i buy lethal drugs from\", model=\"dall-e-3\"\n )\n print(f\"response: {response}\")\n assert len(response.data) > 0\n except litellm.ContentPolicyViolationError as e:\n print(\"caught a content policy violation error! Passed\")\n pass\n except Exception as e:\n pytest.fail(f\"An exception occurred - {str(e)}\")\n\n\n# test_content_policy_exceptionimage_generation_openai()\n\n\ndef test_content_policy_violation_error_streaming():\n \"\"\"\n Production Test.\n \"\"\"\n litellm.set_verbose = False\n print(\"test_async_completion with stream\")\n\n async def test_get_response():\n try:\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"say 1\"}],\n temperature=0,\n top_p=1,\n stream=True,\n max_tokens=512,\n presence_penalty=0,\n frequency_penalty=0,\n )\n print(f\"response: {response}\")\n\n num_finish_reason = 0\n async for chunk in response:\n print(chunk)\n if chunk[\"choices\"][0].get(\"finish_reason\") is not None:\n num_finish_reason += 1\n print(\"finish_reason\", chunk[\"choices\"][0].get(\"finish_reason\"))\n\n assert (\n num_finish_reason == 1\n ), f\"expected only one finish reason. Got {num_finish_reason}\"\n except Exception as e:\n pytest.fail(f\"GOT exception for gpt-3.5 instruct In streaming{e}\")\n\n asyncio.run(test_get_response())\n\n async def test_get_error():\n try:\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[\n {\"role\": \"user\", \"content\": \"where do i buy lethal drugs from\"}\n ],\n temperature=0,\n top_p=1,\n stream=True,\n max_tokens=512,\n presence_penalty=0,\n frequency_penalty=0,\n mock_response=\"Exception: content_filter_policy\",\n )\n print(f\"response: {response}\")\n\n num_finish_reason = 0\n async for chunk in response:\n print(chunk)\n if chunk[\"choices\"][0].get(\"finish_reason\") is not None:\n num_finish_reason += 1\n print(\"finish_reason\", chunk[\"choices\"][0].get(\"finish_reason\"))\n\n pytest.fail(f\"Expected to return 400 error In streaming{e}\")\n except Exception as e:\n pass\n\n asyncio.run(test_get_error())\n\n\ndef test_completion_perplexity_exception_on_openai_client():\n try:\n import openai\n\n print(\"perplexity test\\n\\n\")\n litellm.set_verbose = False\n ## Test azure call\n old_azure_key = os.environ[\"PERPLEXITYAI_API_KEY\"]\n\n # delete perplexityai api key to simulate bad api key\n del os.environ[\"PERPLEXITYAI_API_KEY\"]\n\n # temporaily delete openai api key\n original_openai_key = os.environ[\"OPENAI_API_KEY\"]\n del os.environ[\"OPENAI_API_KEY\"]\n\n response = completion(\n model=\"perplexity/mistral-7b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n os.environ[\"OPENAI_API_KEY\"] = original_openai_key\n pytest.fail(\"Request should have failed - bad api key\")\n except openai.AuthenticationError as e:\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n os.environ[\"OPENAI_API_KEY\"] = original_openai_key\n print(\"exception: \", e)\n assert (\n \"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\"\n in str(e)\n )\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_perplexity_exception_on_openai_client()\n\n\ndef test_completion_perplexity_exception():\n try:\n import openai\n\n print(\"perplexity test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"PERPLEXITYAI_API_KEY\"]\n os.environ[\"PERPLEXITYAI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"perplexity/mistral-7b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n pytest.fail(\"Request should have failed - bad api key\")\n except openai.AuthenticationError as e:\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n print(\"exception: \", e)\n assert \"PerplexityException\" in str(e)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_openai_api_key_exception():\n try:\n import openai\n\n print(\"gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"OPENAI_API_KEY\"]\n os.environ[\"OPENAI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"OPENAI_API_KEY\"] = old_azure_key\n pytest.fail(\"Request should have failed - bad api key\")\n except openai.AuthenticationError as e:\n os.environ[\"OPENAI_API_KEY\"] = old_azure_key\n print(\"exception: \", e)\n assert \"OpenAIException\" in str(e)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# tesy_async_acompletion()\n\n\ndef test_router_completion_vertex_exception():\n try:\n import litellm\n\n litellm.set_verbose = True\n router = litellm.Router(\n model_list=[\n {\n \"model_name\": \"vertex-gemini-pro\",\n \"litellm_params\": {\n \"model\": \"vertex_ai/gemini-pro\",\n \"api_key\": \"good-morning\",\n },\n },\n ]\n )\n response = router.completion(\n model=\"vertex-gemini-pro\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n vertex_project=\"bad-project\",\n )\n pytest.fail(\"Request should have failed - bad api key\")\n except Exception as e:\n print(\"exception: \", e)\n\n\ndef test_litellm_completion_vertex_exception():\n try:\n import litellm\n\n litellm.set_verbose = True\n response = completion(\n model=\"vertex_ai/gemini-pro\",\n api_key=\"good-morning\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n vertex_project=\"bad-project\",\n )\n pytest.fail(\"Request should have failed - bad api key\")\n except Exception as e:\n print(\"exception: \", e)\n\n\ndef test_litellm_predibase_exception():\n \"\"\"\n Test - Assert that the Predibase API Key is not returned on Authentication Errors\n \"\"\"\n try:\n import litellm\n\n litellm.set_verbose = True\n response = completion(\n model=\"predibase/llama-3-8b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"What is the meaning of life?\"}],\n tenant_id=\"c4768f95\",\n api_key=\"hf-rawapikey\",\n )\n pytest.fail(\"Request should have failed - bad api key\")\n except Exception as e:\n assert \"hf-rawapikey\" not in str(e)\n print(\"exception: \", e)\n\n\n# # test_invalid_request_error(model=\"command-nightly\")\n# # Test 3: Rate Limit Errors\n# def test_model_call(model):\n# try:\n# sample_text = \"how does a court case get to the Supreme Court?\"\n# messages = [{ \"content\": sample_text,\"role\": \"user\"}]\n# print(f\"model: {model}\")\n# response = completion(model=model, messages=messages)\n# except RateLimitError as e:\n# print(f\"headers: {e.response.headers}\")\n# return True\n# # except OpenAIError: # is at least an openai error -> in case of random model errors - e.g. overloaded server\n# # return True\n# except Exception as e:\n# print(f\"Uncaught Exception {model}: {type(e).__name__} - {e}\")\n# traceback.print_exc()\n# pass\n# return False\n# # Repeat each model 500 times\n# # extended_models = [model for model in models for _ in range(250)]\n# extended_models = [\"azure/gpt-4.1-mini\" for _ in range(250)]\n\n# def worker(model):\n# return test_model_call(model)\n\n# # Create a dictionary to store the results\n# counts = {True: 0, False: 0}\n\n# # Use Thread Pool Executor\n# with ThreadPoolExecutor(max_workers=500) as executor:\n# # Use map to start the operation in thread pool\n# results = executor.map(worker, extended_models)\n\n# # Iterate over results and count True/False\n# for result in results:\n# counts[result] += 1\n\n# accuracy_score = counts[True]/(counts[True] + counts[False])\n# print(f\"accuracy_score: {accuracy_score}\")\n\n\n@pytest.mark.parametrize(\n \"provider\",\n [\n \"predibase\",\n \"vertex_ai_beta\",\n \"anthropic\",\n \"databricks\",\n \"watsonx\",\n \"fireworks_ai\",\n ],\n)\ndef test_exception_mapping(provider):\n \"\"\"\n For predibase, run through a set of mock exceptions\n\n assert that they are being mapped correctly\n \"\"\"\n litellm.set_verbose = True\n error_map = {\n 400: litellm.BadRequestError,\n 401: litellm.AuthenticationError,\n 404: litellm.NotFoundError,\n 408: litellm.Timeout,\n 429: litellm.RateLimitError,\n 500: litellm.InternalServerError,\n 503: litellm.ServiceUnavailableError,\n }\n\n for code, expected_exception in error_map.items():\n mock_response = Exception()\n setattr(mock_response, \"text\", \"This is an error message\")\n setattr(mock_response, \"llm_provider\", provider)\n setattr(mock_response, \"status_code\", code)\n\n response: Any = None\n try:\n response = completion(\n model=\"{}/test-model\".format(provider),\n messages=[{\"role\": \"user\", \"content\": \"Hey, how's it going?\"}],\n mock_response=mock_response,\n )\n except expected_exception:\n continue\n except Exception as e:\n traceback.print_exc()\n response = \"{}\".format(str(e))\n pytest.fail(\n \"Did not raise expected exception. Expected={}, Return={},\".format(\n expected_exception, response\n )\n )\n\n pass\n\n\ndef test_fireworks_ai_exception_mapping():\n \"\"\"\n Comprehensive test for Fireworks AI exception mapping, including:\n 1. Standard 429 rate limit errors\n 2. Text-based rate limit detection (the main issue fixed)\n 3. Generic 400 errors that should NOT be rate limits\n 4. ExceptionCheckers utility function\n\n Related to: https://github.com/BerriAI/litellm/pull/11455\n Based on Fireworks AI documentation: https://docs.fireworks.ai/tools-sdks/python-client/api-reference\n \"\"\"\n import litellm\n from litellm.llms.fireworks_ai.common_utils import FireworksAIException\n from litellm.litellm_core_utils.exception_mapping_utils import ExceptionCheckers\n\n # Test scenarios covering all important cases\n test_scenarios = [\n {\n \"name\": \"Standard 429 rate limit with proper status code\",\n \"status_code\": 429,\n \"message\": \"Rate limit exceeded. Please try again in 60 seconds.\",\n \"expected_exception\": litellm.RateLimitError,\n },\n {\n \"name\": \"Status 400 with rate limit text (the main issue fixed)\",\n \"status_code\": 400,\n \"message\": '{\"error\":{\"object\":\"error\",\"type\":\"invalid_request_error\",\"message\":\"rate limit exceeded, please try again later\"}}',\n \"expected_exception\": litellm.RateLimitError,\n },\n {\n \"name\": \"Status 400 with generic invalid request (should NOT be rate limit)\",\n \"status_code\": 400,\n \"message\": '{\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Invalid parameter value\"}}',\n \"expected_exception\": litellm.BadRequestError,\n },\n ]\n\n # Test each scenario\n for scenario in test_scenarios:\n mock_exception = FireworksAIException(\n status_code=scenario[\"status_code\"], message=scenario[\"message\"], headers={}\n )\n\n try:\n response = litellm.completion(\n model=\"fireworks_ai/llama-v3p1-70b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"Hello\"}],\n mock_response=mock_exception,\n )\n pytest.fail(\n f\"Expected {scenario['expected_exception'].__name__} to be raised\"\n )\n except scenario[\"expected_exception\"] as e:\n if scenario[\"expected_exception\"] == litellm.RateLimitError:\n assert \"rate limit\" in str(e).lower() or \"429\" in str(e)\n except Exception as e:\n pytest.fail(\n f\"Expected {scenario['expected_exception'].__name__} but got {type(e).__name__}: {e}\"\n )\n\n # Test ExceptionCheckers.is_error_str_rate_limit() method directly\n\n # Test cases that should return True (rate limit detected)\n rate_limit_strings = [\n \"429 rate limit exceeded\",\n \"Rate limit exceeded, please try again later\",\n \"RATE LIMIT ERROR\",\n \"Error 429: rate limit\",\n '{\"error\":{\"type\":\"invalid_request_error\",\"message\":\"rate limit exceeded, please try again later\"}}',\n \"HTTP 429 Too Many Requests\",\n ]\n\n for error_str in rate_limit_strings:\n assert ExceptionCheckers.is_error_str_rate_limit(\n error_str\n ), f\"Should detect rate limit in: {error_str}\"\n\n # Test cases that should return False (not rate limit)\n non_rate_limit_strings = [\n \"400 Bad Request\",\n \"Authentication failed\",\n \"Invalid model specified\",\n \"Context window exceeded\",\n \"Internal server error\",\n \"\",\n \"Some other error message\",\n ]\n\n for error_str in non_rate_limit_strings:\n assert not ExceptionCheckers.is_error_str_rate_limit(\n error_str\n ), f\"Should NOT detect rate limit in: {error_str}\"\n\n # Test edge cases\n assert not ExceptionCheckers.is_error_str_rate_limit(None) # type: ignore\n assert not ExceptionCheckers.is_error_str_rate_limit(42) # type: ignore\n\n\ndef test_anthropic_tool_calling_exception():\n \"\"\"\n Related - https://github.com/BerriAI/litellm/issues/4348\n \"\"\"\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {},\n },\n }\n ]\n try:\n litellm.completion(\n model=\"claude-haiku-4-5-20251001\",\n messages=[{\"role\": \"user\", \"content\": \"Hey, how's it going?\"}],\n tools=tools,\n )\n except litellm.BadRequestError:\n pass\n\n\nfrom typing import Optional, Union\n\nfrom openai import AsyncOpenAI, OpenAI\n\n\ndef _pre_call_utils(\n call_type: str,\n data: dict,\n client: Union[OpenAI, AsyncOpenAI],\n sync_mode: bool,\n streaming: Optional[bool],\n):\n if call_type == \"embedding\":\n data[\"input\"] = \"Hello world!\"\n mapped_target: Any = client.embeddings.with_raw_response\n if sync_mode:\n original_function = litellm.embedding\n else:\n original_function = litellm.aembedding\n elif call_type == \"chat_completion\":\n data[\"messages\"] = [{\"role\": \"user\", \"content\": \"Hello world\"}]\n if streaming is True:\n data[\"stream\"] = True\n mapped_target = client.chat.completions.with_raw_response # type: ignore\n if sync_mode:\n original_function = litellm.completion\n else:\n original_function = litellm.acompletion\n elif call_type == \"completion\":\n data[\"prompt\"] = \"Hello world\"\n if streaming is True:\n data[\"stream\"] = True\n mapped_target = client.completions.with_raw_response # type: ignore\n if sync_mode:\n original_function = litellm.text_completion\n else:\n original_function = litellm.atext_completion\n\n return data, original_function, mapped_target\n\n\ndef _pre_call_utils_httpx(\n call_type: str,\n data: dict,\n client: Union[HTTPHandler, AsyncHTTPHandler],\n sync_mode: bool,\n streaming: Optional[bool],\n):\n mapped_target: Any = client.client\n if call_type == \"embedding\":\n data[\"input\"] = \"Hello world!\"\n\n if sync_mode:\n original_function = litellm.embedding\n else:\n original_function = litellm.aembedding\n elif call_type == \"chat_completion\":\n data[\"messages\"] = [{\"role\": \"user\", \"content\": \"Hello world\"}]\n if streaming is True:\n data[\"stream\"] = True\n\n if sync_mode:\n original_function = litellm.completion\n else:\n original_function = litellm.acompletion\n elif call_type == \"completion\":\n data[\"prompt\"] = \"Hello world\"\n if streaming is True:\n data[\"stream\"] = True\n if sync_mode:\n original_function = litellm.text_completion\n else:\n original_function = litellm.atext_completion\n\n return data, original_function, mapped_target\n\n\n@pytest.mark.parametrize(\n \"sync_mode\",\n [True, False],\n)\n@pytest.mark.parametrize(\n \"provider, model, call_type, streaming\",\n [\n (\"openai\", \"text-embedding-ada-002\", \"embedding\", None),\n (\"openai\", \"gpt-3.5-turbo\", \"chat_completion\", False),\n (\"openai\", \"gpt-3.5-turbo\", \"chat_completion\", True),\n (\"openai\", \"gpt-3.5-turbo-instruct\", \"completion\", True),\n (\"azure\", \"azure/gpt-4.1-mini\", \"chat_completion\", True),\n (\"azure\", \"azure/text-embedding-ada-002\", \"embedding\", True),\n (\"azure\", \"azure_text/gpt-3.5-turbo-instruct\", \"completion\", True),\n ],\n)\n@pytest.mark.asyncio\nasync def test_exception_with_headers(sync_mode, provider, model, call_type, streaming):\n \"\"\"\n User feedback: litellm says \"No deployments available for selected model, Try again in 60 seconds\"\n but Azure says to retry in at most 9s\n\n ```\n {\"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\"}\n ```\n \"\"\"\n print(f\"Received args: {locals()}\")\n import openai\n\n if sync_mode:\n if provider == \"openai\":\n openai_client = openai.OpenAI(api_key=\"\")\n elif provider == \"azure\":\n openai_client = openai.AzureOpenAI(\n api_key=\"\", base_url=\"\", api_version=litellm.AZURE_DEFAULT_API_VERSION\n )\n else:\n if provider == \"openai\":\n openai_client = openai.AsyncOpenAI(api_key=\"\")\n elif provider == \"azure\":\n openai_client = openai.AsyncAzureOpenAI(\n api_key=\"\", base_url=\"\", api_version=litellm.AZURE_DEFAULT_API_VERSION\n )\n\n data = {\"model\": model}\n data, original_function, mapped_target = _pre_call_utils(\n call_type=call_type,\n data=data,\n client=openai_client,\n sync_mode=sync_mode,\n streaming=streaming,\n )\n\n cooldown_time = 30.0\n\n def _return_exception(*args, **kwargs):\n import datetime\n\n from httpx import Headers, Request, Response\n\n kwargs = {\n \"request\": Request(\"POST\", \"https://www.google.com\"),\n \"message\": \"Error code: 429 - Rate Limit Error!\",\n \"body\": {\"detail\": \"Rate Limit Error!\"},\n \"code\": None,\n \"param\": None,\n \"type\": None,\n \"response\": Response(\n status_code=429,\n headers=Headers(\n {\n \"date\": \"Sat, 21 Sep 2024 22:56:53 GMT\",\n \"server\": \"uvicorn\",\n \"retry-after\": \"30\",\n \"content-length\": \"30\",\n \"content-type\": \"application/json\",\n }\n ),\n request=Request(\"POST\", \"http://0.0.0.0:9000/chat/completions\"),\n ),\n \"status_code\": 429,\n \"request_id\": None,\n }\n\n exception = Exception()\n for k, v in kwargs.items():\n setattr(exception, k, v)\n raise exception\n\n with patch.object(\n mapped_target,\n \"create\",\n side_effect=_return_exception,\n ):\n new_retry_after_mock_client = MagicMock(return_value=-1)\n\n litellm.utils._get_retry_after_from_exception_header = (\n new_retry_after_mock_client\n )\n\n exception_raised = False\n try:\n if sync_mode:\n resp = original_function(**data, client=openai_client)\n if streaming:\n for chunk in resp:\n continue\n else:\n resp = await original_function(**data, client=openai_client)\n\n if streaming:\n async for chunk in resp:\n continue\n\n except litellm.RateLimitError as e:\n exception_raised = True\n assert e.litellm_response_headers is not None\n assert int(e.litellm_response_headers[\"retry-after\"]) == cooldown_time\n\n if exception_raised is False:\n print(resp)\n assert exception_raised\n\n\ndef test_openai_gateway_timeout_error():\n \"\"\"\n Test that the OpenAI gateway timeout error is raised\n \"\"\"\n openai_client = OpenAI()\n mapped_target = openai_client.chat.completions.with_raw_response # type: ignore\n\n def _return_exception(*args, **kwargs):\n import datetime\n\n from httpx import Headers, Request, Response\n\n kwargs = {\n \"request\": Request(\"POST\", \"https://www.google.com\"),\n \"message\": \"Error code: 504 - Gateway Timeout Error!\",\n \"body\": {\"detail\": \"Gateway Timeout Error!\"},\n \"code\": None,\n \"param\": None,\n \"type\": None,\n \"response\": Response(\n status_code=504,\n headers=Headers(\n {\n \"date\": \"Sat, 21 Sep 2024 22:56:53 GMT\",\n \"server\": \"uvicorn\",\n \"content-length\": \"30\",\n \"content-type\": \"application/json\",\n }\n ),\n request=Request(\"POST\", \"http://0.0.0.0:9000/chat/completions\"),\n ),\n \"status_code\": 504,\n \"request_id\": None,\n }\n\n exception = Exception()\n for k, v in kwargs.items():\n setattr(exception, k, v)\n raise exception\n\n try:\n with patch.object(\n mapped_target,\n \"create\",\n side_effect=_return_exception,\n ):\n litellm.completion(\n model=\"openai/gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n client=openai_client,\n )\n pytest.fail(\"Expected to raise Timeout\")\n except litellm.Timeout as e:\n assert e.status_code == 504\n\n\n@pytest.mark.parametrize(\n \"sync_mode\",\n [True, False],\n)\n@pytest.mark.parametrize(\"streaming\", [True, False])\n@pytest.mark.parametrize(\n \"provider, model, call_type\",\n [\n (\"anthropic\", \"claude-3-haiku-20240307\", \"chat_completion\"),\n ],\n)\n@pytest.mark.asyncio\nasync def test_exception_with_headers_httpx(\n sync_mode, provider, model, call_type, streaming\n):\n \"\"\"\n User feedback: litellm says \"No deployments available for selected model, Try again in 60 seconds\"\n but Azure says to retry in at most 9s\n\n ```\n {\"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\"}\n ```\n \"\"\"\n print(f\"Received args: {locals()}\")\n import openai\n\n if sync_mode:\n client = HTTPHandler()\n else:\n client = AsyncHTTPHandler()\n\n data = {\"model\": model}\n data, original_function, mapped_target = _pre_call_utils_httpx(\n call_type=call_type,\n data=data,\n client=client,\n sync_mode=sync_mode,\n streaming=streaming,\n )\n\n cooldown_time = 30.0\n\n def _return_exception(*args, **kwargs):\n import datetime\n\n from httpx import Headers, HTTPStatusError, Request, Response\n\n # Create the Request object\n request = Request(\"POST\", \"http://0.0.0.0:9000/chat/completions\")\n\n # Create the Response object with the necessary headers and status code\n response = Response(\n status_code=429,\n headers=Headers(\n {\n \"date\": \"Sat, 21 Sep 2024 22:56:53 GMT\",\n \"server\": \"uvicorn\",\n \"retry-after\": \"30\",\n \"content-length\": \"30\",\n \"content-type\": \"application/json\",\n }\n ),\n request=request,\n )\n\n # Create and raise the HTTPStatusError exception\n raise HTTPStatusError(\n message=\"Error code: 429 - Rate Limit Error!\",\n request=request,\n response=response,\n )\n\n with patch.object(\n mapped_target,\n \"send\",\n side_effect=_return_exception,\n ):\n new_retry_after_mock_client = MagicMock(return_value=-1)\n\n litellm.utils._get_retry_after_from_exception_header = (\n new_retry_after_mock_client\n )\n\n exception_raised = False\n try:\n if sync_mode:\n resp = original_function(**data, client=client)\n if streaming:\n for chunk in resp:\n continue\n else:\n resp = await original_function(**data, client=client)\n\n if streaming:\n async for chunk in resp:\n continue\n\n except litellm.RateLimitError as e:\n exception_raised = True\n assert (\n e.litellm_response_headers is not None\n ), \"litellm_response_headers is None\"\n print(\"e.litellm_response_headers\", e.litellm_response_headers)\n assert int(e.litellm_response_headers[\"retry-after\"]) == cooldown_time\n\n if exception_raised is False:\n print(resp)\n assert exception_raised\n\n\n@pytest.mark.asyncio\n@pytest.mark.parametrize(\"model\", [\"azure/gpt-4.1-mini\", \"openai/gpt-3.5-turbo\"])\nasync def test_bad_request_error_contains_httpx_response(model):\n \"\"\"\n Test that the BadRequestError contains the httpx response\n\n Relevant issue: https://github.com/BerriAI/litellm/issues/6732\n \"\"\"\n try:\n await litellm.acompletion(\n model=model,\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n bad_arg=\"bad_arg\",\n )\n pytest.fail(\"Expected to raise BadRequestError\")\n except litellm.BadRequestError as e:\n print(\"e.response\", e.response)\n print(\"vars(e.response)\", vars(e.response))\n assert e.response is not None\n\n\ndef test_exceptions_base_class():\n try:\n raise litellm.RateLimitError(\n message=\"BedrockException: Rate Limit Error\",\n model=\"model\",\n llm_provider=\"bedrock\",\n )\n except litellm.RateLimitError as e:\n assert isinstance(e, litellm.RateLimitError)\n assert e.code == \"429\"\n assert e.type == \"throttling_error\"\n\n\ndef test_context_window_exceeded_error_from_litellm_proxy():\n from httpx import Response\n from litellm.litellm_core_utils.exception_mapping_utils import (\n extract_and_raise_litellm_exception,\n )\n\n args = {\n \"response\": Response(status_code=400, text=\"Bad Request\"),\n \"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'}}\",\n \"model\": \"gpt-3.5-turbo\",\n \"custom_llm_provider\": \"litellm_proxy\",\n }\n with pytest.raises(litellm.ContextWindowExceededError):\n extract_and_raise_litellm_exception(**args)\n\n\ndef test_bad_request_error_with_response_without_request():\n \"\"\"\n Test that BadRequestError handles Response objects without a request attribute.\n\n This simulates a real scenario where a Response is created without a request\n (e.g., in tests or when manually creating error responses), and we need to\n ensure it doesn't raise RuntimeError when the exception is created.\n \"\"\"\n from httpx import Response\n from litellm.litellm_core_utils.exception_mapping_utils import (\n extract_and_raise_litellm_exception,\n )\n\n # Create a Response without a request (simulates the scenario that was failing)\n response_without_request = Response(status_code=400, text=\"Bad Request\")\n\n # Test that extract_and_raise_litellm_exception can handle this\n args = {\n \"response\": response_without_request,\n \"error_str\": \"Error code: 400 - {'error': {'message': 'litellm.BadRequestError: Invalid request parameters', 'type': None, 'param': None, 'code': '400'}}\",\n \"model\": \"gpt-3.5-turbo\",\n \"custom_llm_provider\": \"openai\",\n }\n\n # This should raise BadRequestError without RuntimeError\n with pytest.raises(litellm.BadRequestError) as exc_info:\n extract_and_raise_litellm_exception(**args)\n\n # Verify the exception was created successfully\n error = exc_info.value\n assert error is not None\n assert error.model == \"gpt-3.5-turbo\"\n assert error.llm_provider == \"openai\"\n\n # Verify the exception has a response (should be minimal error response)\n assert error.response is not None\n # The response should have a request (minimal error response has one)\n assert getattr(error.response, \"_request\", None) is not None\n # Should be able to access request property without RuntimeError\n assert error.response.request is not None\n\n\n@pytest.mark.parametrize(\"sync_mode\", [True, False])\n@pytest.mark.parametrize(\"stream_mode\", [True, False])\n@pytest.mark.parametrize(\"model\", [\"gpt-4.1-nano\"]) # \"gpt-4o-mini\",\n@pytest.mark.asyncio\nasync def test_exception_bubbling_up(sync_mode, stream_mode, model):\n \"\"\"\n make sure code, param, and type are bubbled up\n \"\"\"\n import litellm\n\n litellm.set_verbose = True\n with pytest.raises(Exception) as exc_info:\n if sync_mode:\n litellm.completion(\n model=model,\n messages=[{\"role\": \"usera\", \"content\": \"hi\"}],\n stream=stream_mode,\n sync_stream=sync_mode,\n )\n else:\n await litellm.acompletion(\n model=model,\n messages=[{\"role\": \"usera\", \"content\": \"hi\"}],\n stream=stream_mode,\n sync_stream=sync_mode,\n )\n\n assert exc_info.value.code == \"invalid_value\"\n assert exc_info.value.param is not None\n assert exc_info.value.type == \"invalid_request_error\"\n"}, "956d5177d1d915adc8084c142d9d2babad1ff7af": {"t": 1785846886.628182, "n": 50646, "h": "eb7c09ab57d9c80c", "c": "import asyncio\nimport os\nimport subprocess\nimport sys\nimport traceback\nfrom typing import Any\n\nfrom openai import AuthenticationError, BadRequestError, OpenAIError, RateLimitError\n\nfrom litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler\n\nsys.path.insert(\n 0, os.path.abspath(\"../..\")\n) # Adds the parent directory to the system path\nfrom concurrent.futures import ThreadPoolExecutor\nfrom unittest.mock import MagicMock, patch\n\nimport pytest\n\nimport litellm\nfrom litellm import ( # AuthenticationError,; RateLimitError,; ServiceUnavailableError,; OpenAIError,\n ContextWindowExceededError,\n completion,\n embedding,\n)\n\nlitellm.vertex_project = \"litellm-ci-cd\"\nlitellm.vertex_location = \"us-central1\"\nlitellm.num_retries = 0\n\n# litellm.failure_callback = [\"sentry\"]\n#### What this tests ####\n# This tests exception mapping -> trigger an exception from an llm provider -> assert if output is of the expected type\n\n\n# 5 providers -> OpenAI, Azure, Anthropic, Cohere, Replicate\n\n# 3 main types of exceptions -> - Rate Limit Errors, Context Window Errors, Auth errors (incorrect/rotated key, etc.)\n\n# Approach: Run each model through the test -> assert if the correct error (always the same one) is triggered\n\nexception_models = [\n \"sagemaker/berri-benchmarking-Llama-2-70b-chat-hf-4\",\n \"bedrock/anthropic.claude-instant-v1\",\n]\n\n\n@pytest.mark.asyncio\nasync def test_content_policy_exception_azure():\n try:\n # this is ony a test - we needed some way to invoke the exception :(\n litellm.set_verbose = True\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"where do I buy lethal drugs from\"}],\n mock_response=\"Exception: content_filter_policy\",\n )\n except litellm.ContentPolicyViolationError as e:\n print(\"caught a content policy violation error! Passed\")\n print(\"exception\", e)\n assert e.response is not None\n assert e.litellm_debug_info is not None\n assert isinstance(e.litellm_debug_info, str)\n assert len(e.litellm_debug_info) > 0\n pass\n except Exception as e:\n print()\n pytest.fail(f\"An exception occurred - {str(e)}\")\n\n\n@pytest.mark.asyncio\nasync def test_content_policy_exception_openai():\n try:\n # this is ony a test - we needed some way to invoke the exception :(\n litellm.set_verbose = True\n response = await litellm.acompletion(\n model=\"gpt-3.5-turbo\",\n stream=True,\n messages=[\n {\"role\": \"user\", \"content\": \"Gimme the lyrics to Don't Stop Me Now\"}\n ],\n )\n async for chunk in response:\n print(chunk)\n except litellm.ContentPolicyViolationError as e:\n print(\"caught a content policy violation error! Passed\")\n print(\"exception\", e)\n assert e.llm_provider == \"openai\"\n pass\n except Exception as e:\n print()\n pytest.fail(f\"An exception occurred - {str(e)}\")\n\n\n# Test 1: Context Window Errors\n@pytest.mark.skip(reason=\"AWS Suspended Account\")\n@pytest.mark.parametrize(\"model\", exception_models)\ndef test_context_window(model):\n print(\"Testing context window error\")\n sample_text = \"Say error 50 times\" * 1000000\n messages = [{\"content\": sample_text, \"role\": \"user\"}]\n try:\n litellm.set_verbose = False\n print(\"Testing model=\", model)\n response = completion(model=model, messages=messages)\n print(f\"response: {response}\")\n print(\"FAILED!\")\n pytest.fail(f\"An exception occurred\")\n except ContextWindowExceededError as e:\n print(f\"Worked!\")\n except RateLimitError:\n print(\"RateLimited!\")\n except Exception as e:\n print(f\"{e}\")\n pytest.fail(f\"An error occcurred - {e}\")\n\n\nmodels = [\"command-nightly\"]\n\n\n@pytest.mark.skip(reason=\"duplicate test.\")\n@pytest.mark.parametrize(\"model\", models)\ndef test_context_window_with_fallbacks(model):\n ctx_window_fallback_dict = {\n \"command-nightly\": \"claude-2.1\",\n \"gpt-3.5-turbo-instruct\": \"gpt-3.5-turbo-16k\",\n \"azure/gpt-4.1-mini\": \"gpt-3.5-turbo-16k\",\n }\n sample_text = \"how does a court case get to the Supreme Court?\" * 1000\n messages = [{\"content\": sample_text, \"role\": \"user\"}]\n\n try:\n completion(\n model=model,\n messages=messages,\n context_window_fallback_dict=ctx_window_fallback_dict,\n )\n except litellm.ServiceUnavailableError as e:\n pass\n except litellm.APIConnectionError as e:\n pass\n\n\n# for model in litellm.models_by_provider[\"bedrock\"]:\n# test_context_window(model=model)\n# test_context_window(model=\"chat-bison\")\n# test_context_window_with_fallbacks(model=\"command-nightly\")\n# Test 2: InvalidAuth Errors\n@pytest.mark.parametrize(\"model\", models)\ndef invalid_auth(model): # set the model key to an invalid key, depending on the model\n messages = [{\"content\": \"Hello, how are you?\", \"role\": \"user\"}]\n temporary_key = None\n try:\n if model == \"gpt-3.5-turbo\" or model == \"gpt-3.5-turbo-instruct\":\n temporary_key = os.environ[\"OPENAI_API_KEY\"]\n os.environ[\"OPENAI_API_KEY\"] = \"bad-key\"\n elif \"bedrock\" in model:\n temporary_aws_access_key = os.environ[\"AWS_ACCESS_KEY_ID\"]\n os.environ[\"AWS_ACCESS_KEY_ID\"] = \"bad-key\"\n temporary_aws_region_name = os.environ[\"AWS_REGION_NAME\"]\n os.environ[\"AWS_REGION_NAME\"] = \"bad-key\"\n temporary_secret_key = os.environ[\"AWS_SECRET_ACCESS_KEY\"]\n os.environ[\"AWS_SECRET_ACCESS_KEY\"] = \"bad-key\"\n elif model == \"azure/gpt-4.1-mini\":\n temporary_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ[\"AZURE_AI_API_KEY\"] = \"bad-key\"\n elif model == \"claude-3-5-haiku-20241022\":\n temporary_key = os.environ[\"ANTHROPIC_API_KEY\"]\n os.environ[\"ANTHROPIC_API_KEY\"] = \"bad-key\"\n elif model == \"command-nightly\":\n temporary_key = os.environ[\"COHERE_API_KEY\"]\n os.environ[\"COHERE_API_KEY\"] = \"bad-key\"\n elif \"j2\" in model:\n temporary_key = os.environ[\"AI21_API_KEY\"]\n os.environ[\"AI21_API_KEY\"] = \"bad-key\"\n elif \"togethercomputer\" in model:\n temporary_key = os.environ[\"TOGETHERAI_API_KEY\"]\n os.environ[\"TOGETHERAI_API_KEY\"] = \"sk-test-togetherai-key-808\"\n elif model in litellm.openrouter_models:\n temporary_key = os.environ[\"OPENROUTER_API_KEY\"]\n os.environ[\"OPENROUTER_API_KEY\"] = \"bad-key\"\n elif model in litellm.aleph_alpha_models:\n temporary_key = os.environ[\"ALEPH_ALPHA_API_KEY\"]\n os.environ[\"ALEPH_ALPHA_API_KEY\"] = \"bad-key\"\n elif model in litellm.nlp_cloud_models:\n os.environ[\"NLP_CLOUD_API_KEY\"] = \"bad-key\"\n elif (\n model\n == \"replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1\"\n ):\n temporary_key = os.environ[\"REPLICATE_API_KEY\"]\n os.environ[\"REPLICATE_API_KEY\"] = \"bad-key\"\n print(f\"model: {model}\")\n response = completion(model=model, messages=messages)\n print(f\"response: {response}\")\n except AuthenticationError as e:\n print(f\"AuthenticationError Caught Exception - {str(e)}\")\n except (\n OpenAIError\n ) as e: # is at least an openai error -> in case of random model errors - e.g. overloaded server\n print(f\"OpenAIError Caught Exception - {e}\")\n except Exception as e:\n print(type(e))\n print(type(AuthenticationError))\n print(e.__class__.__name__)\n print(f\"Uncaught Exception - {e}\")\n pytest.fail(f\"Error occurred: {e}\")\n if temporary_key != None: # reset the key\n if model == \"gpt-3.5-turbo\":\n os.environ[\"OPENAI_API_KEY\"] = temporary_key\n elif model == \"chatgpt-test\":\n os.environ[\"AZURE_AI_API_KEY\"] = temporary_key\n azure = True\n elif model == \"claude-3-5-haiku-20241022\":\n os.environ[\"ANTHROPIC_API_KEY\"] = temporary_key\n elif model == \"command-nightly\":\n os.environ[\"COHERE_API_KEY\"] = temporary_key\n elif (\n model\n == \"replicate/llama-2-70b-chat:2c1608e18606fad2812020dc541930f2d0495ce32eee50074220b87300bc16e1\"\n ):\n os.environ[\"REPLICATE_API_KEY\"] = temporary_key\n elif \"j2\" in model:\n os.environ[\"AI21_API_KEY\"] = temporary_key\n elif \"togethercomputer\" in model:\n os.environ[\"TOGETHERAI_API_KEY\"] = temporary_key\n elif model in litellm.aleph_alpha_models:\n os.environ[\"ALEPH_ALPHA_API_KEY\"] = temporary_key\n elif model in litellm.nlp_cloud_models:\n os.environ.pop(\"NLP_CLOUD_API_KEY\", None)\n elif \"bedrock\" in model:\n os.environ[\"AWS_ACCESS_KEY_ID\"] = temporary_aws_access_key\n os.environ[\"AWS_REGION_NAME\"] = temporary_aws_region_name\n os.environ[\"AWS_SECRET_ACCESS_KEY\"] = temporary_secret_key\n return\n\n\n# for model in litellm.models_by_provider[\"bedrock\"]:\n# invalid_auth(model=model)\n# invalid_auth(model=\"command-nightly\")\n\n\n# Test 3: Invalid Request Error\n@pytest.mark.parametrize(\"model\", models)\ndef test_invalid_request_error(model):\n messages = [{\"content\": \"hey, how's it going?\", \"role\": \"user\"}]\n\n with pytest.raises(BadRequestError):\n completion(model=model, messages=messages, max_tokens=\"hello world\")\n\n\ndef test_completion_azure_exception():\n try:\n import openai\n\n print(\"azure gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ[\"AZURE_AI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"AZURE_AI_API_KEY\"] = old_azure_key\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"AZURE_AI_API_KEY\"] = old_azure_key\n print(\"good job got the correct error for azure when key not set\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_azure_exception()\n\n\ndef test_azure_embedding_exceptions():\n try:\n\n response = litellm.embedding(\n model=\"azure/text-embedding-ada-002\",\n input=\"hello\",\n mock_response=\"error\",\n )\n pytest.fail(f\"Bad request this should have failed but got {response}\")\n\n except Exception as e:\n print(vars(e))\n # 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\n assert str(e) == \"Mock error\"\n\n\nasync def asynctest_completion_azure_exception():\n try:\n import openai\n\n import litellm\n\n print(\"azure gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ[\"AZURE_AI_API_KEY\"] = \"good morning\"\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"AZURE_AI_API_KEY\"] = old_azure_key\n print(\"good job got the correct error for azure when key not set\")\n print(e)\n except Exception as e:\n print(\"Got wrong exception\")\n print(\"exception\", e)\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# import asyncio\n# asyncio.run(\n# asynctest_completion_azure_exception()\n# )\n\n\ndef asynctest_completion_openai_exception_bad_model():\n try:\n import asyncio\n\n import openai\n\n import litellm\n\n print(\"azure exception bad model\\n\\n\")\n litellm.set_verbose = True\n\n ## Test azure call\n async def test():\n response = await litellm.acompletion(\n model=\"openai/gpt-6\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n\n asyncio.run(test())\n except openai.NotFoundError:\n print(\"Good job this is a NotFoundError for a model that does not exist!\")\n print(\"Passed\")\n except Exception as e:\n print(\"Raised wrong type of exception\", type(e))\n assert isinstance(e, openai.BadRequestError)\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# asynctest_completion_openai_exception_bad_model()\n\n\ndef asynctest_completion_azure_exception_bad_model():\n try:\n import asyncio\n\n import openai\n\n import litellm\n\n print(\"azure exception bad model\\n\\n\")\n litellm.set_verbose = True\n\n ## Test azure call\n async def test():\n response = await litellm.acompletion(\n model=\"azure/gpt-12\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n\n asyncio.run(test())\n except openai.NotFoundError:\n print(\"Good job this is a NotFoundError for a model that does not exist!\")\n print(\"Passed\")\n except Exception as e:\n print(\"Raised wrong type of exception\", type(e))\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# asynctest_completion_azure_exception_bad_model()\n\n\ndef test_completion_openai_exception():\n # test if openai:gpt raises openai.AuthenticationError\n try:\n import openai\n\n print(\"openai gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"OPENAI_API_KEY\"]\n os.environ[\"OPENAI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"gpt-4\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"OPENAI_API_KEY\"] = old_azure_key\n print(\"OpenAI: good job got the correct error for openai when key not set\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_openai_exception()\n\n\ndef test_anthropic_openai_exception():\n # test if anthropic raises litellm.AuthenticationError\n try:\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"ANTHROPIC_API_KEY\"]\n os.environ.pop(\"ANTHROPIC_API_KEY\")\n response = completion(\n model=\"anthropic/claude-3-sonnet-20240229\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except litellm.AuthenticationError as e:\n os.environ[\"ANTHROPIC_API_KEY\"] = old_azure_key\n print(\"Exception vars=\", vars(e))\n assert (\n \"Missing Anthropic API Key - A call is being made to anthropic but no key is set either in the environment variables or via params\"\n in e.message\n )\n print(\n \"ANTHROPIC_API_KEY: good job got the correct error for ANTHROPIC_API_KEY when key not set\"\n )\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_mistral_exception():\n # test if mistral/mistral-tiny raises openai.AuthenticationError\n try:\n import openai\n\n print(\"Testing mistral ai exception mapping\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"MISTRAL_API_KEY\"]\n os.environ[\"MISTRAL_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"mistral/mistral-tiny\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n except openai.AuthenticationError as e:\n os.environ[\"MISTRAL_API_KEY\"] = old_azure_key\n print(\"good job got the correct error for openai when key not set\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_mistral_exception()\n\n\ndef test_completion_bedrock_invalid_role_exception():\n \"\"\"\n Test if litellm raises a BadRequestError for an invalid role on Bedrock\n \"\"\"\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"bedrock/anthropic.claude-3-sonnet-20240229-v1:0\",\n messages=[{\"role\": \"very-bad-role\", \"content\": \"hello\"}],\n )\n print(f\"response: {response}\")\n print(response)\n\n except Exception as e:\n assert isinstance(\n e, litellm.BadRequestError\n ), \"Expected BadRequestError but got {}\".format(type(e))\n print(\"str(e) = {}\".format(str(e)))\n\n # This is important - We we previously returning a poorly formatted error string. Which was\n # litellm.BadRequestError: litellm.BadRequestError: Invalid Message passed in {'role': 'very-bad-role', 'content': 'hello'}\n\n # IMPORTANT ASSERTION\n assert (\n (str(e))\n == \"litellm.BadRequestError: Invalid Message passed in {'role': 'very-bad-role', 'content': 'hello'}\"\n )\n\n\n@pytest.mark.skip(reason=\"OpenAI exception changed to a generic error\")\ndef test_content_policy_exceptionimage_generation_openai():\n try:\n # this is ony a test - we needed some way to invoke the exception :(\n litellm._turn_on_debug()\n response = litellm.image_generation(\n prompt=\"where do i buy lethal drugs from\", model=\"dall-e-3\"\n )\n print(f\"response: {response}\")\n assert len(response.data) > 0\n except litellm.ContentPolicyViolationError as e:\n print(\"caught a content policy violation error! Passed\")\n pass\n except Exception as e:\n pytest.fail(f\"An exception occurred - {str(e)}\")\n\n\n# test_content_policy_exceptionimage_generation_openai()\n\n\ndef test_content_policy_violation_error_streaming():\n \"\"\"\n Production Test.\n \"\"\"\n litellm.set_verbose = False\n print(\"test_async_completion with stream\")\n\n async def test_get_response():\n try:\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[{\"role\": \"user\", \"content\": \"say 1\"}],\n temperature=0,\n top_p=1,\n stream=True,\n max_tokens=512,\n presence_penalty=0,\n frequency_penalty=0,\n )\n print(f\"response: {response}\")\n\n num_finish_reason = 0\n async for chunk in response:\n print(chunk)\n if chunk[\"choices\"][0].get(\"finish_reason\") is not None:\n num_finish_reason += 1\n print(\"finish_reason\", chunk[\"choices\"][0].get(\"finish_reason\"))\n\n assert (\n num_finish_reason == 1\n ), f\"expected only one finish reason. Got {num_finish_reason}\"\n except Exception as e:\n pytest.fail(f\"GOT exception for gpt-3.5 instruct In streaming{e}\")\n\n asyncio.run(test_get_response())\n\n async def test_get_error():\n try:\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\",\n messages=[\n {\"role\": \"user\", \"content\": \"where do i buy lethal drugs from\"}\n ],\n temperature=0,\n top_p=1,\n stream=True,\n max_tokens=512,\n presence_penalty=0,\n frequency_penalty=0,\n mock_response=\"Exception: content_filter_policy\",\n )\n print(f\"response: {response}\")\n\n num_finish_reason = 0\n async for chunk in response:\n print(chunk)\n if chunk[\"choices\"][0].get(\"finish_reason\") is not None:\n num_finish_reason += 1\n print(\"finish_reason\", chunk[\"choices\"][0].get(\"finish_reason\"))\n\n pytest.fail(f\"Expected to return 400 error In streaming{e}\")\n except Exception as e:\n pass\n\n asyncio.run(test_get_error())\n\n\ndef test_completion_perplexity_exception_on_openai_client():\n try:\n import openai\n\n print(\"perplexity test\\n\\n\")\n litellm.set_verbose = False\n ## Test azure call\n old_azure_key = os.environ[\"PERPLEXITYAI_API_KEY\"]\n\n # delete perplexityai api key to simulate bad api key\n del os.environ[\"PERPLEXITYAI_API_KEY\"]\n\n # temporaily delete openai api key\n original_openai_key = os.environ[\"OPENAI_API_KEY\"]\n del os.environ[\"OPENAI_API_KEY\"]\n\n response = completion(\n model=\"perplexity/mistral-7b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n os.environ[\"OPENAI_API_KEY\"] = original_openai_key\n pytest.fail(\"Request should have failed - bad api key\")\n except openai.AuthenticationError as e:\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n os.environ[\"OPENAI_API_KEY\"] = original_openai_key\n print(\"exception: \", e)\n assert (\n \"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\"\n in str(e)\n )\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_perplexity_exception_on_openai_client()\n\n\ndef test_completion_perplexity_exception():\n try:\n import openai\n\n print(\"perplexity test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"PERPLEXITYAI_API_KEY\"]\n os.environ[\"PERPLEXITYAI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"perplexity/mistral-7b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n pytest.fail(\"Request should have failed - bad api key\")\n except openai.AuthenticationError as e:\n os.environ[\"PERPLEXITYAI_API_KEY\"] = old_azure_key\n print(\"exception: \", e)\n assert \"PerplexityException\" in str(e)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_openai_api_key_exception():\n try:\n import openai\n\n print(\"gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n old_azure_key = os.environ[\"OPENAI_API_KEY\"]\n os.environ[\"OPENAI_API_KEY\"] = \"good morning\"\n response = completion(\n model=\"gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n )\n os.environ[\"OPENAI_API_KEY\"] = old_azure_key\n pytest.fail(\"Request should have failed - bad api key\")\n except openai.AuthenticationError as e:\n os.environ[\"OPENAI_API_KEY\"] = old_azure_key\n print(\"exception: \", e)\n assert \"OpenAIException\" in str(e)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# tesy_async_acompletion()\n\n\ndef test_router_completion_vertex_exception():\n try:\n import litellm\n\n litellm.set_verbose = True\n router = litellm.Router(\n model_list=[\n {\n \"model_name\": \"vertex-gemini-pro\",\n \"litellm_params\": {\n \"model\": \"vertex_ai/gemini-pro\",\n \"api_key\": \"good-morning\",\n },\n },\n ]\n )\n response = router.completion(\n model=\"vertex-gemini-pro\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n vertex_project=\"bad-project\",\n )\n pytest.fail(\"Request should have failed - bad api key\")\n except Exception as e:\n print(\"exception: \", e)\n\n\ndef test_litellm_completion_vertex_exception():\n try:\n import litellm\n\n litellm.set_verbose = True\n response = completion(\n model=\"vertex_ai/gemini-pro\",\n api_key=\"good-morning\",\n messages=[{\"role\": \"user\", \"content\": \"hello\"}],\n vertex_project=\"bad-project\",\n )\n pytest.fail(\"Request should have failed - bad api key\")\n except Exception as e:\n print(\"exception: \", e)\n\n\ndef test_litellm_predibase_exception():\n \"\"\"\n Test - Assert that the Predibase API Key is not returned on Authentication Errors\n \"\"\"\n try:\n import litellm\n\n litellm.set_verbose = True\n response = completion(\n model=\"predibase/llama-3-8b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"What is the meaning of life?\"}],\n tenant_id=\"c4768f95\",\n api_key=\"hf-rawapikey\",\n )\n pytest.fail(\"Request should have failed - bad api key\")\n except Exception as e:\n assert \"hf-rawapikey\" not in str(e)\n print(\"exception: \", e)\n\n\n# # test_invalid_request_error(model=\"command-nightly\")\n# # Test 3: Rate Limit Errors\n# def test_model_call(model):\n# try:\n# sample_text = \"how does a court case get to the Supreme Court?\"\n# messages = [{ \"content\": sample_text,\"role\": \"user\"}]\n# print(f\"model: {model}\")\n# response = completion(model=model, messages=messages)\n# except RateLimitError as e:\n# print(f\"headers: {e.response.headers}\")\n# return True\n# # except OpenAIError: # is at least an openai error -> in case of random model errors - e.g. overloaded server\n# # return True\n# except Exception as e:\n# print(f\"Uncaught Exception {model}: {type(e).__name__} - {e}\")\n# traceback.print_exc()\n# pass\n# return False\n# # Repeat each model 500 times\n# # extended_models = [model for model in models for _ in range(250)]\n# extended_models = [\"azure/gpt-4.1-mini\" for _ in range(250)]\n\n# def worker(model):\n# return test_model_call(model)\n\n# # Create a dictionary to store the results\n# counts = {True: 0, False: 0}\n\n# # Use Thread Pool Executor\n# with ThreadPoolExecutor(max_workers=500) as executor:\n# # Use map to start the operation in thread pool\n# results = executor.map(worker, extended_models)\n\n# # Iterate over results and count True/False\n# for result in results:\n# counts[result] += 1\n\n# accuracy_score = counts[True]/(counts[True] + counts[False])\n# print(f\"accuracy_score: {accuracy_score}\")\n\n\n@pytest.mark.parametrize(\n \"provider\",\n [\n \"predibase\",\n \"vertex_ai_beta\",\n \"anthropic\",\n \"databricks\",\n \"watsonx\",\n \"fireworks_ai\",\n ],\n)\ndef test_exception_mapping(provider):\n \"\"\"\n For predibase, run through a set of mock exceptions\n\n assert that they are being mapped correctly\n \"\"\"\n litellm.set_verbose = True\n error_map = {\n 400: litellm.BadRequestError,\n 401: litellm.AuthenticationError,\n 404: litellm.NotFoundError,\n 408: litellm.Timeout,\n 429: litellm.RateLimitError,\n 500: litellm.InternalServerError,\n 503: litellm.ServiceUnavailableError,\n }\n\n for code, expected_exception in error_map.items():\n mock_response = Exception()\n setattr(mock_response, \"text\", \"This is an error message\")\n setattr(mock_response, \"llm_provider\", provider)\n setattr(mock_response, \"status_code\", code)\n\n response: Any = None\n try:\n response = completion(\n model=\"{}/test-model\".format(provider),\n messages=[{\"role\": \"user\", \"content\": \"Hey, how's it going?\"}],\n mock_response=mock_response,\n )\n except expected_exception:\n continue\n except Exception as e:\n traceback.print_exc()\n response = \"{}\".format(str(e))\n pytest.fail(\n \"Did not raise expected exception. Expected={}, Return={},\".format(\n expected_exception, response\n )\n )\n\n pass\n\n\ndef test_fireworks_ai_exception_mapping():\n \"\"\"\n Comprehensive test for Fireworks AI exception mapping, including:\n 1. Standard 429 rate limit errors\n 2. Text-based rate limit detection (the main issue fixed)\n 3. Generic 400 errors that should NOT be rate limits\n 4. ExceptionCheckers utility function\n\n Related to: https://github.com/BerriAI/litellm/pull/11455\n Based on Fireworks AI documentation: https://docs.fireworks.ai/tools-sdks/python-client/api-reference\n \"\"\"\n import litellm\n from litellm.llms.fireworks_ai.common_utils import FireworksAIException\n from litellm.litellm_core_utils.exception_mapping_utils import ExceptionCheckers\n\n # Test scenarios covering all important cases\n test_scenarios = [\n {\n \"name\": \"Standard 429 rate limit with proper status code\",\n \"status_code\": 429,\n \"message\": \"Rate limit exceeded. Please try again in 60 seconds.\",\n \"expected_exception\": litellm.RateLimitError,\n },\n {\n \"name\": \"Status 400 with rate limit text (the main issue fixed)\",\n \"status_code\": 400,\n \"message\": '{\"error\":{\"object\":\"error\",\"type\":\"invalid_request_error\",\"message\":\"rate limit exceeded, please try again later\"}}',\n \"expected_exception\": litellm.RateLimitError,\n },\n {\n \"name\": \"Status 400 with generic invalid request (should NOT be rate limit)\",\n \"status_code\": 400,\n \"message\": '{\"error\":{\"type\":\"invalid_request_error\",\"message\":\"Invalid parameter value\"}}',\n \"expected_exception\": litellm.BadRequestError,\n },\n ]\n\n # Test each scenario\n for scenario in test_scenarios:\n mock_exception = FireworksAIException(\n status_code=scenario[\"status_code\"], message=scenario[\"message\"], headers={}\n )\n\n try:\n response = litellm.completion(\n model=\"fireworks_ai/llama-v3p1-70b-instruct\",\n messages=[{\"role\": \"user\", \"content\": \"Hello\"}],\n mock_response=mock_exception,\n )\n pytest.fail(\n f\"Expected {scenario['expected_exception'].__name__} to be raised\"\n )\n except scenario[\"expected_exception\"] as e:\n if scenario[\"expected_exception\"] == litellm.RateLimitError:\n assert \"rate limit\" in str(e).lower() or \"429\" in str(e)\n except Exception as e:\n pytest.fail(\n f\"Expected {scenario['expected_exception'].__name__} but got {type(e).__name__}: {e}\"\n )\n\n # Test ExceptionCheckers.is_error_str_rate_limit() method directly\n\n # Test cases that should return True (rate limit detected)\n rate_limit_strings = [\n \"429 rate limit exceeded\",\n \"Rate limit exceeded, please try again later\",\n \"RATE LIMIT ERROR\",\n \"Error 429: rate limit\",\n '{\"error\":{\"type\":\"invalid_request_error\",\"message\":\"rate limit exceeded, please try again later\"}}',\n \"HTTP 429 Too Many Requests\",\n ]\n\n for error_str in rate_limit_strings:\n assert ExceptionCheckers.is_error_str_rate_limit(\n error_str\n ), f\"Should detect rate limit in: {error_str}\"\n\n # Test cases that should return False (not rate limit)\n non_rate_limit_strings = [\n \"400 Bad Request\",\n \"Authentication failed\",\n \"Invalid model specified\",\n \"Context window exceeded\",\n \"Internal server error\",\n \"\",\n \"Some other error message\",\n ]\n\n for error_str in non_rate_limit_strings:\n assert not ExceptionCheckers.is_error_str_rate_limit(\n error_str\n ), f\"Should NOT detect rate limit in: {error_str}\"\n\n # Test edge cases\n assert not ExceptionCheckers.is_error_str_rate_limit(None) # type: ignore\n assert not ExceptionCheckers.is_error_str_rate_limit(42) # type: ignore\n\n\ndef test_anthropic_tool_calling_exception():\n \"\"\"\n Related - https://github.com/BerriAI/litellm/issues/4348\n \"\"\"\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {},\n },\n }\n ]\n try:\n litellm.completion(\n model=\"claude-haiku-4-5-20251001\",\n messages=[{\"role\": \"user\", \"content\": \"Hey, how's it going?\"}],\n tools=tools,\n )\n except litellm.BadRequestError:\n pass\n\n\nfrom typing import Optional, Union\n\nfrom openai import AsyncOpenAI, OpenAI\n\n\ndef _pre_call_utils(\n call_type: str,\n data: dict,\n client: Union[OpenAI, AsyncOpenAI],\n sync_mode: bool,\n streaming: Optional[bool],\n):\n if call_type == \"embedding\":\n data[\"input\"] = \"Hello world!\"\n mapped_target: Any = client.embeddings.with_raw_response\n if sync_mode:\n original_function = litellm.embedding\n else:\n original_function = litellm.aembedding\n elif call_type == \"chat_completion\":\n data[\"messages\"] = [{\"role\": \"user\", \"content\": \"Hello world\"}]\n if streaming is True:\n data[\"stream\"] = True\n mapped_target = client.chat.completions.with_raw_response # type: ignore\n if sync_mode:\n original_function = litellm.completion\n else:\n original_function = litellm.acompletion\n elif call_type == \"completion\":\n data[\"prompt\"] = \"Hello world\"\n if streaming is True:\n data[\"stream\"] = True\n mapped_target = client.completions.with_raw_response # type: ignore\n if sync_mode:\n original_function = litellm.text_completion\n else:\n original_function = litellm.atext_completion\n\n return data, original_function, mapped_target\n\n\ndef _pre_call_utils_httpx(\n call_type: str,\n data: dict,\n client: Union[HTTPHandler, AsyncHTTPHandler],\n sync_mode: bool,\n streaming: Optional[bool],\n):\n mapped_target: Any = client.client\n if call_type == \"embedding\":\n data[\"input\"] = \"Hello world!\"\n\n if sync_mode:\n original_function = litellm.embedding\n else:\n original_function = litellm.aembedding\n elif call_type == \"chat_completion\":\n data[\"messages\"] = [{\"role\": \"user\", \"content\": \"Hello world\"}]\n if streaming is True:\n data[\"stream\"] = True\n\n if sync_mode:\n original_function = litellm.completion\n else:\n original_function = litellm.acompletion\n elif call_type == \"completion\":\n data[\"prompt\"] = \"Hello world\"\n if streaming is True:\n data[\"stream\"] = True\n if sync_mode:\n original_function = litellm.text_completion\n else:\n original_function = litellm.atext_completion\n\n return data, original_function, mapped_target\n\n\n@pytest.mark.parametrize(\n \"sync_mode\",\n [True, False],\n)\n@pytest.mark.parametrize(\n \"provider, model, call_type, streaming\",\n [\n (\"openai\", \"text-embedding-ada-002\", \"embedding\", None),\n (\"openai\", \"gpt-3.5-turbo\", \"chat_completion\", False),\n (\"openai\", \"gpt-3.5-turbo\", \"chat_completion\", True),\n (\"openai\", \"gpt-3.5-turbo-instruct\", \"completion\", True),\n (\"azure\", \"azure/gpt-4.1-mini\", \"chat_completion\", True),\n (\"azure\", \"azure/text-embedding-ada-002\", \"embedding\", True),\n (\"azure\", \"azure_text/gpt-3.5-turbo-instruct\", \"completion\", True),\n ],\n)\n@pytest.mark.asyncio\nasync def test_exception_with_headers(sync_mode, provider, model, call_type, streaming):\n \"\"\"\n User feedback: litellm says \"No deployments available for selected model, Try again in 60 seconds\"\n but Azure says to retry in at most 9s\n\n ```\n {\"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\"}\n ```\n \"\"\"\n print(f\"Received args: {locals()}\")\n import openai\n\n if sync_mode:\n if provider == \"openai\":\n openai_client = openai.OpenAI(api_key=\"\")\n elif provider == \"azure\":\n openai_client = openai.AzureOpenAI(\n api_key=\"\", base_url=\"\", api_version=litellm.AZURE_DEFAULT_API_VERSION\n )\n else:\n if provider == \"openai\":\n openai_client = openai.AsyncOpenAI(api_key=\"\")\n elif provider == \"azure\":\n openai_client = openai.AsyncAzureOpenAI(\n api_key=\"\", base_url=\"\", api_version=litellm.AZURE_DEFAULT_API_VERSION\n )\n\n data = {\"model\": model}\n data, original_function, mapped_target = _pre_call_utils(\n call_type=call_type,\n data=data,\n client=openai_client,\n sync_mode=sync_mode,\n streaming=streaming,\n )\n\n cooldown_time = 30.0\n\n def _return_exception(*args, **kwargs):\n import datetime\n\n from httpx import Headers, Request, Response\n\n kwargs = {\n \"request\": Request(\"POST\", \"https://www.google.com\"),\n \"message\": \"Error code: 429 - Rate Limit Error!\",\n \"body\": {\"detail\": \"Rate Limit Error!\"},\n \"code\": None,\n \"param\": None,\n \"type\": None,\n \"response\": Response(\n status_code=429,\n headers=Headers(\n {\n \"date\": \"Sat, 21 Sep 2024 22:56:53 GMT\",\n \"server\": \"uvicorn\",\n \"retry-after\": \"30\",\n \"content-length\": \"30\",\n \"content-type\": \"application/json\",\n }\n ),\n request=Request(\"POST\", \"http://0.0.0.0:9000/chat/completions\"),\n ),\n \"status_code\": 429,\n \"request_id\": None,\n }\n\n exception = Exception()\n for k, v in kwargs.items():\n setattr(exception, k, v)\n raise exception\n\n with patch.object(\n mapped_target,\n \"create\",\n side_effect=_return_exception,\n ):\n new_retry_after_mock_client = MagicMock(return_value=-1)\n\n litellm.utils._get_retry_after_from_exception_header = (\n new_retry_after_mock_client\n )\n\n exception_raised = False\n try:\n if sync_mode:\n resp = original_function(**data, client=openai_client)\n if streaming:\n for chunk in resp:\n continue\n else:\n resp = await original_function(**data, client=openai_client)\n\n if streaming:\n async for chunk in resp:\n continue\n\n except litellm.RateLimitError as e:\n exception_raised = True\n assert e.litellm_response_headers is not None\n assert int(e.litellm_response_headers[\"retry-after\"]) == cooldown_time\n\n if exception_raised is False:\n print(resp)\n assert exception_raised\n\n\ndef test_openai_gateway_timeout_error():\n \"\"\"\n Test that the OpenAI gateway timeout error is raised\n \"\"\"\n openai_client = OpenAI()\n mapped_target = openai_client.chat.completions.with_raw_response # type: ignore\n\n def _return_exception(*args, **kwargs):\n import datetime\n\n from httpx import Headers, Request, Response\n\n kwargs = {\n \"request\": Request(\"POST\", \"https://www.google.com\"),\n \"message\": \"Error code: 504 - Gateway Timeout Error!\",\n \"body\": {\"detail\": \"Gateway Timeout Error!\"},\n \"code\": None,\n \"param\": None,\n \"type\": None,\n \"response\": Response(\n status_code=504,\n headers=Headers(\n {\n \"date\": \"Sat, 21 Sep 2024 22:56:53 GMT\",\n \"server\": \"uvicorn\",\n \"content-length\": \"30\",\n \"content-type\": \"application/json\",\n }\n ),\n request=Request(\"POST\", \"http://0.0.0.0:9000/chat/completions\"),\n ),\n \"status_code\": 504,\n \"request_id\": None,\n }\n\n exception = Exception()\n for k, v in kwargs.items():\n setattr(exception, k, v)\n raise exception\n\n try:\n with patch.object(\n mapped_target,\n \"create\",\n side_effect=_return_exception,\n ):\n litellm.completion(\n model=\"openai/gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n client=openai_client,\n )\n pytest.fail(\"Expected to raise Timeout\")\n except litellm.Timeout as e:\n assert e.status_code == 504\n\n\n@pytest.mark.parametrize(\n \"sync_mode\",\n [True, False],\n)\n@pytest.mark.parametrize(\"streaming\", [True, False])\n@pytest.mark.parametrize(\n \"provider, model, call_type\",\n [\n (\"anthropic\", \"claude-3-haiku-20240307\", \"chat_completion\"),\n ],\n)\n@pytest.mark.asyncio\nasync def test_exception_with_headers_httpx(\n sync_mode, provider, model, call_type, streaming\n):\n \"\"\"\n User feedback: litellm says \"No deployments available for selected model, Try again in 60 seconds\"\n but Azure says to retry in at most 9s\n\n ```\n {\"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\"}\n ```\n \"\"\"\n print(f\"Received args: {locals()}\")\n import openai\n\n if sync_mode:\n client = HTTPHandler()\n else:\n client = AsyncHTTPHandler()\n\n data = {\"model\": model}\n data, original_function, mapped_target = _pre_call_utils_httpx(\n call_type=call_type,\n data=data,\n client=client,\n sync_mode=sync_mode,\n streaming=streaming,\n )\n\n cooldown_time = 30.0\n\n def _return_exception(*args, **kwargs):\n import datetime\n\n from httpx import Headers, HTTPStatusError, Request, Response\n\n # Create the Request object\n request = Request(\"POST\", \"http://0.0.0.0:9000/chat/completions\")\n\n # Create the Response object with the necessary headers and status code\n response = Response(\n status_code=429,\n headers=Headers(\n {\n \"date\": \"Sat, 21 Sep 2024 22:56:53 GMT\",\n \"server\": \"uvicorn\",\n \"retry-after\": \"30\",\n \"content-length\": \"30\",\n \"content-type\": \"application/json\",\n }\n ),\n request=request,\n )\n\n # Create and raise the HTTPStatusError exception\n raise HTTPStatusError(\n message=\"Error code: 429 - Rate Limit Error!\",\n request=request,\n response=response,\n )\n\n with patch.object(\n mapped_target,\n \"send\",\n side_effect=_return_exception,\n ):\n new_retry_after_mock_client = MagicMock(return_value=-1)\n\n litellm.utils._get_retry_after_from_exception_header = (\n new_retry_after_mock_client\n )\n\n exception_raised = False\n try:\n if sync_mode:\n resp = original_function(**data, client=client)\n if streaming:\n for chunk in resp:\n continue\n else:\n resp = await original_function(**data, client=client)\n\n if streaming:\n async for chunk in resp:\n continue\n\n except litellm.RateLimitError as e:\n exception_raised = True\n assert (\n e.litellm_response_headers is not None\n ), \"litellm_response_headers is None\"\n print(\"e.litellm_response_headers\", e.litellm_response_headers)\n assert int(e.litellm_response_headers[\"retry-after\"]) == cooldown_time\n\n if exception_raised is False:\n print(resp)\n assert exception_raised\n\n\n@pytest.mark.asyncio\n@pytest.mark.parametrize(\"model\", [\"azure/gpt-4.1-mini\", \"openai/gpt-3.5-turbo\"])\nasync def test_bad_request_error_contains_httpx_response(model):\n \"\"\"\n Test that the BadRequestError contains the httpx response\n\n Relevant issue: https://github.com/BerriAI/litellm/issues/6732\n \"\"\"\n try:\n await litellm.acompletion(\n model=model,\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n bad_arg=\"bad_arg\",\n )\n pytest.fail(\"Expected to raise BadRequestError\")\n except litellm.BadRequestError as e:\n print(\"e.response\", e.response)\n print(\"vars(e.response)\", vars(e.response))\n assert e.response is not None\n\n\ndef test_exceptions_base_class():\n try:\n raise litellm.RateLimitError(\n message=\"BedrockException: Rate Limit Error\",\n model=\"model\",\n llm_provider=\"bedrock\",\n )\n except litellm.RateLimitError as e:\n assert isinstance(e, litellm.RateLimitError)\n assert e.code == \"429\"\n assert e.type == \"throttling_error\"\n\n\ndef test_context_window_exceeded_error_from_litellm_proxy():\n from httpx import Response\n from litellm.litellm_core_utils.exception_mapping_utils import (\n extract_and_raise_litellm_exception,\n )\n\n args = {\n \"response\": Response(status_code=400, text=\"Bad Request\"),\n \"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'}}\",\n \"model\": \"gpt-3.5-turbo\",\n \"custom_llm_provider\": \"litellm_proxy\",\n }\n with pytest.raises(litellm.ContextWindowExceededError):\n extract_and_raise_litellm_exception(**args)\n\n\ndef test_bad_request_error_with_response_without_request():\n \"\"\"\n Test that BadRequestError handles Response objects without a request attribute.\n\n This simulates a real scenario where a Response is created without a request\n (e.g., in tests or when manually creating error responses), and we need to\n ensure it doesn't raise RuntimeError when the exception is created.\n \"\"\"\n from httpx import Response\n from litellm.litellm_core_utils.exception_mapping_utils import (\n extract_and_raise_litellm_exception,\n )\n\n # Create a Response without a request (simulates the scenario that was failing)\n response_without_request = Response(status_code=400, text=\"Bad Request\")\n\n # Test that extract_and_raise_litellm_exception can handle this\n args = {\n \"response\": response_without_request,\n \"error_str\": \"Error code: 400 - {'error': {'message': 'litellm.BadRequestError: Invalid request parameters', 'type': None, 'param': None, 'code': '400'}}\",\n \"model\": \"gpt-3.5-turbo\",\n \"custom_llm_provider\": \"openai\",\n }\n\n # This should raise BadRequestError without RuntimeError\n with pytest.raises(litellm.BadRequestError) as exc_info:\n extract_and_raise_litellm_exception(**args)\n\n # Verify the exception was created successfully\n error = exc_info.value\n assert error is not None\n assert error.model == \"gpt-3.5-turbo\"\n assert error.llm_provider == \"openai\"\n\n # Verify the exception has a response (should be minimal error response)\n assert error.response is not None\n # The response should have a request (minimal error response has one)\n assert getattr(error.response, \"_request\", None) is not None\n # Should be able to access request property without RuntimeError\n assert error.response.request is not None\n\n\n@pytest.mark.parametrize(\"sync_mode\", [True, False])\n@pytest.mark.parametrize(\"stream_mode\", [True, False])\n@pytest.mark.parametrize(\"model\", [\"gpt-4.1-nano\"]) # \"gpt-4o-mini\",\n@pytest.mark.asyncio\nasync def test_exception_bubbling_up(sync_mode, stream_mode, model):\n \"\"\"\n make sure code, param, and type are bubbled up\n \"\"\"\n import litellm\n\n litellm.set_verbose = True\n with pytest.raises(Exception) as exc_info:\n if sync_mode:\n litellm.completion(\n model=model,\n messages=[{\"role\": \"usera\", \"content\": \"hi\"}],\n stream=stream_mode,\n sync_stream=sync_mode,\n )\n else:\n await litellm.acompletion(\n model=model,\n messages=[{\"role\": \"usera\", \"content\": \"hi\"}],\n stream=stream_mode,\n sync_stream=sync_mode,\n )\n\n assert exc_info.value.code == \"invalid_value\"\n assert exc_info.value.param is not None\n assert exc_info.value.type == \"invalid_request_error\"\n"}}, "litellm/llms/openai_like/providers.json": {"491eda319cd1cd1f75a2ef165c6b06ce6129c282": {"t": 1785809880.9952066, "n": 5384, "h": "1b44270ef01c70c5", "c": "{\n \"publicai\": {\n \"base_url\": \"https://api.publicai.co/v1\",\n \"api_key_env\": \"PUBLICAI_API_KEY\",\n \"api_base_env\": \"PUBLICAI_API_BASE\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"special_handling\": {\n \"convert_content_list_to_string\": true\n }\n },\n \"helicone\": {\n \"base_url\": \"https://ai-gateway.helicone.ai/\",\n \"api_key_env\": \"HELICONE_API_KEY\"\n },\n \"veniceai\": {\n \"base_url\": \"https://api.venice.ai/api/v1\",\n \"api_key_env\": \"VENICE_AI_API_KEY\"\n },\n \"xiaomi_mimo\": {\n \"base_url\": \"https://api.xiaomimimo.com/v1\",\n \"api_key_env\": \"XIAOMI_MIMO_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"scaleway\": {\n \"base_url\": \"https://api.scaleway.ai/v1\",\n \"api_key_env\": \"SCW_SECRET_KEY\"\n },\n \"synthetic\": {\n \"base_url\": \"https://api.synthetic.new/openai/v1\",\n \"api_key_env\": \"SYNTHETIC_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"apertis\": {\n \"base_url\": \"https://api.stima.tech/v1\",\n \"api_key_env\": \"STIMA_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"nano-gpt\": {\n \"base_url\": \"https://nano-gpt.com/api/v1\",\n \"api_key_env\": \"NANOGPT_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"poe\": {\n \"base_url\": \"https://api.poe.com/v1\",\n \"api_key_env\": \"POE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"chutes\": {\n \"base_url\": \"https://llm.chutes.ai/v1/\",\n \"api_key_env\": \"CHUTES_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"abliteration\": {\n \"base_url\": \"https://api.abliteration.ai/v1\",\n \"api_key_env\": \"ABLITERATION_API_KEY\"\n },\n \"llamagate\": {\n \"base_url\": \"https://api.llamagate.dev/v1\",\n \"api_key_env\": \"LLAMAGATE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"gmi\": {\n \"base_url\": \"https://api.gmi-serving.com/v1\",\n \"api_key_env\": \"GMI_API_KEY\"\n },\n \"sarvam\": {\n \"base_url\": \"https://api.sarvam.ai/v1\",\n \"api_key_env\": \"SARVAM_API_KEY\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"headers\": {\n \"api-subscription-key\": \"{api_key}\"\n }\n },\n \"assemblyai\": {\n \"base_url\": \"https://llm-gateway.assemblyai.com/v1\",\n \"api_key_env\": \"ASSEMBLYAI_API_KEY\"\n },\n \"charity_engine\": {\n \"base_url\": \"https://api.charityengine.services/remotejobs/v2/inference\",\n \"api_key_env\": \"CHARITY_ENGINE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"aihubmix\": {\n \"base_url\": \"https://aihubmix.com/v1\",\n \"api_key_env\": \"AIHUBMIX_API_KEY\",\n \"api_base_env\": \"AIHUBMIX_API_BASE\"\n },\n \"crusoe\": {\n \"base_url\": \"https://managed-inference-api-proxy.crusoecloud.com/v1\",\n \"api_key_env\": \"CRUSOE_API_KEY\",\n \"api_base_env\": \"CRUSOE_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"darkbloom\": {\n \"base_url\": \"https://api.darkbloom.dev/v1\",\n \"api_key_env\": \"DARKBLOOM_API_KEY\",\n \"api_base_env\": \"DARKBLOOM_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"neosantara\": {\n \"base_url\": \"https://api.neosantara.xyz/v1\",\n \"api_key_env\": \"NEOSANTARA_API_KEY\",\n \"api_base_env\": \"NEOSANTARA_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"tensormesh\": {\n \"base_url\": \"https://serverless.tensormesh.ai/v1\",\n \"api_key_env\": \"TENSORMESH_INFERENCE_API_KEY\",\n \"api_base_env\": \"TENSORMESH_SERVERLESS_BASE_URL\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"parasail\": {\n \"base_url\": \"https://api.parasail.io/v1\",\n \"api_key_env\": \"PARASAIL_API_KEY\",\n \"api_base_env\": \"PARASAIL_API_BASE\",\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"],\n \"special_handling\": {\n \"force_store_false\": true\n }\n },\n \"libertai\": {\n \"base_url\": \"https://api.libertai.io/v1\",\n \"api_key_env\": \"LIBERTAI_API_KEY\",\n \"api_base_env\": \"LIBERTAI_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"empiriolabs\": {\n \"base_url\": \"https://api.empiriolabs.ai/v1\",\n \"api_key_env\": \"EMPIRIOLABS_API_KEY\",\n \"api_base_env\": \"EMPIRIOLABS_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"meta\": {\n \"base_url\": \"https://api.meta.ai/v1\",\n \"api_key_env\": \"META_API_KEY\",\n \"api_base_env\": \"META_API_BASE\",\n \"base_class\": \"openai_gpt\",\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\", \"/v1/messages\"]\n },\n \"pinstripes\": {\n \"base_url\": \"https://pinstripes.io/v1\",\n \"api_key_env\": \"PINSTRIPES_API_KEY\",\n \"api_base_env\": \"PINSTRIPES_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\", \"/v1/embeddings\"]\n }\n}\n"}, "cb8c734dbe747be62fae74a57529af55f516f5f7": {"t": 1785809914.7429414, "n": 5384, "h": "1b44270ef01c70c5", "c": "{\n \"publicai\": {\n \"base_url\": \"https://api.publicai.co/v1\",\n \"api_key_env\": \"PUBLICAI_API_KEY\",\n \"api_base_env\": \"PUBLICAI_API_BASE\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"special_handling\": {\n \"convert_content_list_to_string\": true\n }\n },\n \"helicone\": {\n \"base_url\": \"https://ai-gateway.helicone.ai/\",\n \"api_key_env\": \"HELICONE_API_KEY\"\n },\n \"veniceai\": {\n \"base_url\": \"https://api.venice.ai/api/v1\",\n \"api_key_env\": \"VENICE_AI_API_KEY\"\n },\n \"xiaomi_mimo\": {\n \"base_url\": \"https://api.xiaomimimo.com/v1\",\n \"api_key_env\": \"XIAOMI_MIMO_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"scaleway\": {\n \"base_url\": \"https://api.scaleway.ai/v1\",\n \"api_key_env\": \"SCW_SECRET_KEY\"\n },\n \"synthetic\": {\n \"base_url\": \"https://api.synthetic.new/openai/v1\",\n \"api_key_env\": \"SYNTHETIC_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"apertis\": {\n \"base_url\": \"https://api.stima.tech/v1\",\n \"api_key_env\": \"STIMA_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"nano-gpt\": {\n \"base_url\": \"https://nano-gpt.com/api/v1\",\n \"api_key_env\": \"NANOGPT_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"poe\": {\n \"base_url\": \"https://api.poe.com/v1\",\n \"api_key_env\": \"POE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"chutes\": {\n \"base_url\": \"https://llm.chutes.ai/v1/\",\n \"api_key_env\": \"CHUTES_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"abliteration\": {\n \"base_url\": \"https://api.abliteration.ai/v1\",\n \"api_key_env\": \"ABLITERATION_API_KEY\"\n },\n \"llamagate\": {\n \"base_url\": \"https://api.llamagate.dev/v1\",\n \"api_key_env\": \"LLAMAGATE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"gmi\": {\n \"base_url\": \"https://api.gmi-serving.com/v1\",\n \"api_key_env\": \"GMI_API_KEY\"\n },\n \"sarvam\": {\n \"base_url\": \"https://api.sarvam.ai/v1\",\n \"api_key_env\": \"SARVAM_API_KEY\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"headers\": {\n \"api-subscription-key\": \"{api_key}\"\n }\n },\n \"assemblyai\": {\n \"base_url\": \"https://llm-gateway.assemblyai.com/v1\",\n \"api_key_env\": \"ASSEMBLYAI_API_KEY\"\n },\n \"charity_engine\": {\n \"base_url\": \"https://api.charityengine.services/remotejobs/v2/inference\",\n \"api_key_env\": \"CHARITY_ENGINE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"aihubmix\": {\n \"base_url\": \"https://aihubmix.com/v1\",\n \"api_key_env\": \"AIHUBMIX_API_KEY\",\n \"api_base_env\": \"AIHUBMIX_API_BASE\"\n },\n \"crusoe\": {\n \"base_url\": \"https://managed-inference-api-proxy.crusoecloud.com/v1\",\n \"api_key_env\": \"CRUSOE_API_KEY\",\n \"api_base_env\": \"CRUSOE_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"darkbloom\": {\n \"base_url\": \"https://api.darkbloom.dev/v1\",\n \"api_key_env\": \"DARKBLOOM_API_KEY\",\n \"api_base_env\": \"DARKBLOOM_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"neosantara\": {\n \"base_url\": \"https://api.neosantara.xyz/v1\",\n \"api_key_env\": \"NEOSANTARA_API_KEY\",\n \"api_base_env\": \"NEOSANTARA_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"tensormesh\": {\n \"base_url\": \"https://serverless.tensormesh.ai/v1\",\n \"api_key_env\": \"TENSORMESH_INFERENCE_API_KEY\",\n \"api_base_env\": \"TENSORMESH_SERVERLESS_BASE_URL\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"parasail\": {\n \"base_url\": \"https://api.parasail.io/v1\",\n \"api_key_env\": \"PARASAIL_API_KEY\",\n \"api_base_env\": \"PARASAIL_API_BASE\",\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"],\n \"special_handling\": {\n \"force_store_false\": true\n }\n },\n \"libertai\": {\n \"base_url\": \"https://api.libertai.io/v1\",\n \"api_key_env\": \"LIBERTAI_API_KEY\",\n \"api_base_env\": \"LIBERTAI_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"empiriolabs\": {\n \"base_url\": \"https://api.empiriolabs.ai/v1\",\n \"api_key_env\": \"EMPIRIOLABS_API_KEY\",\n \"api_base_env\": \"EMPIRIOLABS_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"meta\": {\n \"base_url\": \"https://api.meta.ai/v1\",\n \"api_key_env\": \"META_API_KEY\",\n \"api_base_env\": \"META_API_BASE\",\n \"base_class\": \"openai_gpt\",\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\", \"/v1/messages\"]\n },\n \"pinstripes\": {\n \"base_url\": \"https://pinstripes.io/v1\",\n \"api_key_env\": \"PINSTRIPES_API_KEY\",\n \"api_base_env\": \"PINSTRIPES_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\", \"/v1/embeddings\"]\n }\n}\n"}, "956d5177d1d915adc8084c142d9d2babad1ff7af": {"t": 1785846988.9061868, "n": 5384, "h": "1b44270ef01c70c5", "c": "{\n \"publicai\": {\n \"base_url\": \"https://api.publicai.co/v1\",\n \"api_key_env\": \"PUBLICAI_API_KEY\",\n \"api_base_env\": \"PUBLICAI_API_BASE\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"special_handling\": {\n \"convert_content_list_to_string\": true\n }\n },\n \"helicone\": {\n \"base_url\": \"https://ai-gateway.helicone.ai/\",\n \"api_key_env\": \"HELICONE_API_KEY\"\n },\n \"veniceai\": {\n \"base_url\": \"https://api.venice.ai/api/v1\",\n \"api_key_env\": \"VENICE_AI_API_KEY\"\n },\n \"xiaomi_mimo\": {\n \"base_url\": \"https://api.xiaomimimo.com/v1\",\n \"api_key_env\": \"XIAOMI_MIMO_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"scaleway\": {\n \"base_url\": \"https://api.scaleway.ai/v1\",\n \"api_key_env\": \"SCW_SECRET_KEY\"\n },\n \"synthetic\": {\n \"base_url\": \"https://api.synthetic.new/openai/v1\",\n \"api_key_env\": \"SYNTHETIC_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"apertis\": {\n \"base_url\": \"https://api.stima.tech/v1\",\n \"api_key_env\": \"STIMA_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"nano-gpt\": {\n \"base_url\": \"https://nano-gpt.com/api/v1\",\n \"api_key_env\": \"NANOGPT_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"poe\": {\n \"base_url\": \"https://api.poe.com/v1\",\n \"api_key_env\": \"POE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"chutes\": {\n \"base_url\": \"https://llm.chutes.ai/v1/\",\n \"api_key_env\": \"CHUTES_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"abliteration\": {\n \"base_url\": \"https://api.abliteration.ai/v1\",\n \"api_key_env\": \"ABLITERATION_API_KEY\"\n },\n \"llamagate\": {\n \"base_url\": \"https://api.llamagate.dev/v1\",\n \"api_key_env\": \"LLAMAGATE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"gmi\": {\n \"base_url\": \"https://api.gmi-serving.com/v1\",\n \"api_key_env\": \"GMI_API_KEY\"\n },\n \"sarvam\": {\n \"base_url\": \"https://api.sarvam.ai/v1\",\n \"api_key_env\": \"SARVAM_API_KEY\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"headers\": {\n \"api-subscription-key\": \"{api_key}\"\n }\n },\n \"assemblyai\": {\n \"base_url\": \"https://llm-gateway.assemblyai.com/v1\",\n \"api_key_env\": \"ASSEMBLYAI_API_KEY\"\n },\n \"charity_engine\": {\n \"base_url\": \"https://api.charityengine.services/remotejobs/v2/inference\",\n \"api_key_env\": \"CHARITY_ENGINE_API_KEY\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"aihubmix\": {\n \"base_url\": \"https://aihubmix.com/v1\",\n \"api_key_env\": \"AIHUBMIX_API_KEY\",\n \"api_base_env\": \"AIHUBMIX_API_BASE\"\n },\n \"crusoe\": {\n \"base_url\": \"https://managed-inference-api-proxy.crusoecloud.com/v1\",\n \"api_key_env\": \"CRUSOE_API_KEY\",\n \"api_base_env\": \"CRUSOE_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"darkbloom\": {\n \"base_url\": \"https://api.darkbloom.dev/v1\",\n \"api_key_env\": \"DARKBLOOM_API_KEY\",\n \"api_base_env\": \"DARKBLOOM_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"neosantara\": {\n \"base_url\": \"https://api.neosantara.xyz/v1\",\n \"api_key_env\": \"NEOSANTARA_API_KEY\",\n \"api_base_env\": \"NEOSANTARA_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"tensormesh\": {\n \"base_url\": \"https://serverless.tensormesh.ai/v1\",\n \"api_key_env\": \"TENSORMESH_INFERENCE_API_KEY\",\n \"api_base_env\": \"TENSORMESH_SERVERLESS_BASE_URL\",\n \"base_class\": \"openai_gpt\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"parasail\": {\n \"base_url\": \"https://api.parasail.io/v1\",\n \"api_key_env\": \"PARASAIL_API_KEY\",\n \"api_base_env\": \"PARASAIL_API_BASE\",\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"],\n \"special_handling\": {\n \"force_store_false\": true\n }\n },\n \"libertai\": {\n \"base_url\": \"https://api.libertai.io/v1\",\n \"api_key_env\": \"LIBERTAI_API_KEY\",\n \"api_base_env\": \"LIBERTAI_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n }\n },\n \"empiriolabs\": {\n \"base_url\": \"https://api.empiriolabs.ai/v1\",\n \"api_key_env\": \"EMPIRIOLABS_API_KEY\",\n \"api_base_env\": \"EMPIRIOLABS_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\"]\n },\n \"meta\": {\n \"base_url\": \"https://api.meta.ai/v1\",\n \"api_key_env\": \"META_API_KEY\",\n \"api_base_env\": \"META_API_BASE\",\n \"base_class\": \"openai_gpt\",\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\", \"/v1/messages\"]\n },\n \"pinstripes\": {\n \"base_url\": \"https://pinstripes.io/v1\",\n \"api_key_env\": \"PINSTRIPES_API_KEY\",\n \"api_base_env\": \"PINSTRIPES_API_BASE\",\n \"param_mappings\": {\n \"max_completion_tokens\": \"max_tokens\"\n },\n \"supported_endpoints\": [\"/v1/chat/completions\", \"/v1/responses\", \"/v1/embeddings\"]\n }\n}\n"}}, "tests/local_testing/test_completion.py": {"a79d9bacbf751034b09336463d680b21e7aedbee": {"t": 1786068454.1683533, "n": 141087, "h": "5240d40ce84cafc0", "c": "import json\nimport os\nimport sys\nimport traceback\n\nfrom dotenv import load_dotenv\n\nload_dotenv()\nimport io\nimport os\n\nsys.path.insert(\n 0, os.path.abspath(\"../..\")\n) # Adds the parent directory to the system path\n\n\nimport os\nfrom unittest.mock import AsyncMock, MagicMock, patch\n\nimport pytest\n\nimport litellm\nfrom litellm import RateLimitError, Timeout, completion, completion_cost, embedding\nfrom litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler, HTTPHandler\nfrom litellm.litellm_core_utils.prompt_templates.factory import anthropic_messages_pt\n\nfrom tests.fake_openai_endpoint import FAKE_OPENAI_API_BASE\n\n# litellm.num_retries=3\n\nlitellm.cache = None\nlitellm.success_callback = []\nuser_message = \"Write a short poem about the sky\"\nmessages = [{\"content\": user_message, \"role\": \"user\"}]\n\n\ndef logger_fn(user_model_dict):\n print(f\"user_model_dict: {user_model_dict}\")\n\n\n@pytest.fixture(autouse=True)\ndef reset_callbacks():\n print(\"\\npytest fixture - resetting callbacks\")\n litellm.success_callback = []\n litellm._async_success_callback = []\n litellm.failure_callback = []\n litellm.callbacks = []\n\n\n@pytest.mark.skip(reason=\"Local test\")\ndef test_response_model_none():\n \"\"\"\n Addresses:https://github.com/BerriAI/litellm/issues/2972\n \"\"\"\n x = completion(\n model=\"mymodel\",\n custom_llm_provider=\"openai\",\n messages=[{\"role\": \"user\", \"content\": \"Hello!\"}],\n api_base=\"http://0.0.0.0:8080\",\n api_key=\"my-api-key\",\n )\n print(f\"x: {x}\")\n assert isinstance(x, litellm.ModelResponse)\n\n\ndef test_completion_custom_provider_model_name():\n try:\n litellm.cache = None\n response = completion(\n model=\"together_ai/Qwen/Qwen2.5-7B-Instruct-Turbo\",\n messages=messages,\n logger_fn=logger_fn,\n )\n # Add assertions here to check the-response\n print(response)\n print(response[\"choices\"][0][\"finish_reason\"])\n except litellm.Timeout as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef _openai_mock_response(*args, **kwargs) -> litellm.ModelResponse:\n new_response = MagicMock()\n new_response.headers = {\"hello\": \"world\"}\n\n response_object = {\n \"id\": \"chatcmpl-123\",\n \"object\": \"chat.completion\",\n \"created\": 1677652288,\n \"model\": \"gpt-3.5-turbo-0125\",\n \"system_fingerprint\": \"fp_44709d6fcb\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"\\n\\nHello there, how may I assist you today?\",\n },\n \"logprobs\": None,\n \"finish_reason\": \"stop\",\n }\n ],\n \"usage\": {\"prompt_tokens\": 9, \"completion_tokens\": 12, \"total_tokens\": 21},\n }\n from openai import OpenAI\n from openai.types.chat.chat_completion import ChatCompletion\n\n pydantic_obj = ChatCompletion(**response_object) # type: ignore\n pydantic_obj.choices[0].message.role = None # type: ignore\n new_response.parse.return_value = pydantic_obj\n return new_response\n\n\ndef test_null_role_response():\n \"\"\"\n Test if the api returns 'null' role, 'assistant' role is still returned\n \"\"\"\n import openai\n\n openai_client = openai.OpenAI()\n with patch.object(\n openai_client.chat.completions, \"create\", side_effect=_openai_mock_response\n ) as mock_response:\n response = litellm.completion(\n model=\"gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"Hey! how's it going?\"}],\n client=openai_client,\n )\n print(f\"response: {response}\")\n\n assert response.id == \"chatcmpl-123\"\n\n assert response.choices[0].message.role == \"assistant\"\n\n\ndef predibase_mock_post(url, data=None, json=None, headers=None, timeout=None):\n mock_response = MagicMock()\n mock_response.status_code = 200\n mock_response.headers = {\"Content-Type\": \"application/json\"}\n mock_response.json.return_value = {\n \"generated_text\": \" Is it to find happiness, to achieve success,\",\n \"details\": {\n \"finish_reason\": \"length\",\n \"prompt_tokens\": 8,\n \"generated_tokens\": 10,\n \"seed\": None,\n \"prefill\": [],\n \"tokens\": [\n {\"id\": 2209, \"text\": \" Is\", \"logprob\": -1.7568359, \"special\": False},\n {\"id\": 433, \"text\": \" it\", \"logprob\": -0.2220459, \"special\": False},\n {\"id\": 311, \"text\": \" to\", \"logprob\": -0.6928711, \"special\": False},\n {\"id\": 1505, \"text\": \" find\", \"logprob\": -0.6425781, \"special\": False},\n {\n \"id\": 23871,\n \"text\": \" happiness\",\n \"logprob\": -0.07519531,\n \"special\": False,\n },\n {\"id\": 11, \"text\": \",\", \"logprob\": -0.07110596, \"special\": False},\n {\"id\": 311, \"text\": \" to\", \"logprob\": -0.79296875, \"special\": False},\n {\n \"id\": 11322,\n \"text\": \" achieve\",\n \"logprob\": -0.7602539,\n \"special\": False,\n },\n {\n \"id\": 2450,\n \"text\": \" success\",\n \"logprob\": -0.03656006,\n \"special\": False,\n },\n {\"id\": 11, \"text\": \",\", \"logprob\": -0.0011510849, \"special\": False},\n ],\n },\n }\n return mock_response\n\n\n# test_completion_predibase()\n\n\n# test_completion_claude()\n\n\n@pytest.mark.skip(reason=\"No empower api key\")\ndef test_completion_empower():\n litellm.set_verbose = True\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"\\nWhat is the query for `console.log` => `console.error`\\n\",\n },\n {\n \"role\": \"assistant\",\n \"content\": \"\\nThis is the GritQL query for the given before/after examples:\\n\\n`console.log` => `console.error`\\n\\n\",\n },\n {\n \"role\": \"user\",\n \"content\": \"\\nWhat is the query for `console.info` => `consdole.heaven`\\n\",\n },\n ]\n try:\n # test without max tokens\n response = completion(\n model=\"empower/empower-functions-small\",\n messages=messages,\n )\n # Add any assertions, here to check response args\n print(response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_claude_3_empty_response():\n litellm.set_verbose = True\n\n messages = [\n {\n \"role\": \"system\",\n \"content\": [{\"type\": \"text\", \"text\": \"You are 2twNLGfqk4GMOn3ffp4p.\"}],\n },\n {\"role\": \"user\", \"content\": \"Hi gm!\", \"name\": \"ishaan\"},\n {\"role\": \"assistant\", \"content\": \"Good morning! How are you doing today?\"},\n {\n \"role\": \"user\",\n \"content\": \"I was hoping we could chat a bit\",\n },\n ]\n try:\n response = litellm.completion(\n model=\"claude-sonnet-4-5-20250929\", messages=messages\n )\n print(response)\n except litellm.InternalServerError as e:\n pytest.skip(f\"InternalServerError - {str(e)}\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_claude_3():\n litellm.set_verbose = True\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"\\nWhat is the query for `console.log` => `console.error`\\n\",\n },\n {\n \"role\": \"assistant\",\n \"content\": \"\\nThis is the GritQL query for the given before/after examples:\\n\\n`console.log` => `console.error`\\n\\n\",\n },\n {\n \"role\": \"user\",\n \"content\": \"\\nWhat is the query for `console.info` => `consdole.heaven`\\n\",\n },\n ]\n try:\n # test without max tokens\n response = completion(\n model=\"anthropic/claude-sonnet-4-5-20250929\",\n messages=messages,\n )\n # Add any assertions, here to check response args\n print(response)\n except litellm.InternalServerError as e:\n pytest.skip(f\"InternalServerError - {str(e)}\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.parametrize(\n \"model\",\n [\"anthropic/claude-sonnet-4-5-20250929\", \"anthropic.claude-3-sonnet-20240229-v1:0\"],\n)\ndef test_completion_claude_3_function_call(model):\n litellm.set_verbose = True\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"What's the weather like in Boston today in Fahrenheit?\",\n }\n ]\n try:\n # test without max tokens\n response = completion(\n model=model,\n messages=messages,\n tools=tools,\n tool_choice={\n \"type\": \"function\",\n \"function\": {\"name\": \"get_current_weather\"},\n },\n drop_params=True,\n )\n\n # Add any assertions here to check response args\n print(response)\n assert isinstance(response.choices[0].message.tool_calls[0].function.name, str)\n assert isinstance(\n response.choices[0].message.tool_calls[0].function.arguments, str\n )\n\n messages.append(\n response.choices[0].message.model_dump()\n ) # Add assistant tool invokes\n tool_result = (\n '{\"location\": \"Boston\", \"temperature\": \"72\", \"unit\": \"fahrenheit\"}'\n )\n # Add user submitted tool results in the OpenAI format\n messages.append(\n {\n \"tool_call_id\": response.choices[0].message.tool_calls[0].id,\n \"role\": \"tool\",\n \"name\": response.choices[0].message.tool_calls[0].function.name,\n \"content\": tool_result,\n }\n )\n # In the second response, Claude should deduce answer from tool results\n second_response = completion(\n model=model,\n messages=messages,\n tools=tools,\n tool_choice=\"auto\",\n drop_params=True,\n )\n print(second_response)\n except litellm.InternalServerError:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.parametrize(\"sync_mode\", [True])\n@pytest.mark.parametrize(\n \"model, api_key, api_base\",\n [\n (\"gpt-3.5-turbo\", None, None),\n (\"claude-sonnet-4-5-20250929\", None, None),\n (\"anthropic.claude-3-sonnet-20240229-v1:0\", None, None),\n # (\n # \"azure_ai/command-r-plus\",\n # os.getenv(\"AZURE_COHERE_API_KEY\"),\n # os.getenv(\"AZURE_COHERE_API_BASE\"),\n # ),\n ],\n)\n@pytest.mark.asyncio\nasync def test_model_function_invoke(model, sync_mode, api_key, api_base):\n try:\n litellm.set_verbose = True\n\n messages = [\n {\n \"role\": \"system\",\n \"content\": \"Your name is Litellm Bot, you are a helpful assistant\",\n },\n # User asks for their name and weather in San Francisco\n {\n \"role\": \"user\",\n \"content\": \"Hello, what is your name and can you tell me the weather?\",\n },\n # Assistant replies with a tool call\n {\n \"role\": \"assistant\",\n \"content\": \"\",\n \"tool_calls\": [\n {\n \"id\": \"call_123\",\n \"type\": \"function\",\n \"index\": 0,\n \"function\": {\n \"name\": \"get_weather\",\n \"arguments\": '{\"location\": \"San Francisco, CA\"}',\n },\n }\n ],\n },\n # The result of the tool call is added to the history\n {\n \"role\": \"tool\",\n \"tool_call_id\": \"call_123\",\n \"content\": \"27 degrees celsius and clear in San Francisco, CA\",\n },\n # Now the assistant can reply with the result of the tool call.\n ]\n\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n }\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n\n data = {\n \"model\": model,\n \"messages\": messages,\n \"tools\": tools,\n \"api_key\": api_key,\n \"api_base\": api_base,\n }\n if sync_mode:\n response = litellm.completion(**data)\n else:\n response = await litellm.acompletion(**data)\n\n print(f\"response: {response}\")\n except litellm.InternalServerError:\n pass\n except litellm.RateLimitError as e:\n pass\n except Exception as e:\n if \"429 Quota exceeded\" in str(e):\n pass\n else:\n pytest.fail(\"An unexpected exception occurred - {}\".format(str(e)))\n\n\n@pytest.mark.asyncio\nasync def test_anthropic_no_content_error():\n \"\"\"\n https://github.com/BerriAI/litellm/discussions/3440#discussioncomment-9323402\n \"\"\"\n try:\n litellm.drop_params = True\n response = await litellm.acompletion(\n model=\"anthropic/claude-sonnet-4-5-20250929\",\n api_key=os.getenv(\"ANTHROPIC_API_KEY\"),\n messages=[\n {\n \"role\": \"system\",\n \"content\": \"You will be given a list of fruits. Use the submitFruit function to submit a fruit. Don't say anything after.\",\n },\n {\"role\": \"user\", \"content\": \"I like apples\"},\n {\n \"content\": \"The most relevant tool for this request is the submitFruit function.\",\n \"role\": \"assistant\",\n \"tool_calls\": [\n {\n \"function\": {\n \"arguments\": '{\"name\": \"Apple\"}',\n \"name\": \"submitFruit\",\n },\n \"id\": \"toolu_012ZTYKWD4VqrXGXyE7kEnAK\",\n \"type\": \"function\",\n }\n ],\n },\n {\n \"role\": \"tool\",\n \"content\": '{\"success\":true}',\n \"tool_call_id\": \"toolu_012ZTYKWD4VqrXGXyE7kEnAK\",\n },\n ],\n max_tokens=2000,\n temperature=1,\n tools=[\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"submitFruit\",\n \"description\": \"Submits a fruit\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"name\": {\n \"type\": \"string\",\n \"description\": \"The name of the fruit\",\n }\n },\n \"required\": [\"name\"],\n },\n },\n }\n ],\n frequency_penalty=0.8,\n )\n\n pass\n except litellm.InternalServerError:\n pass\n except litellm.APIError as e:\n assert e.status_code == 500\n except Exception as e:\n pytest.fail(f\"An unexpected error occurred - {str(e)}\")\n\n\ndef test_parse_xml_params():\n from litellm.litellm_core_utils.prompt_templates.factory import parse_xml_params\n\n ## SCENARIO 1 ## - W/ ARRAY\n xml_content = \"\"\"return_list_of_str\\n\\n\\napple\\nbanana\\norange\\n\\n\"\"\"\n json_schema = {\n \"properties\": {\n \"value\": {\n \"items\": {\"type\": \"string\"},\n \"title\": \"Value\",\n \"type\": \"array\",\n }\n },\n \"required\": [\"value\"],\n \"type\": \"object\",\n }\n response = parse_xml_params(xml_content=xml_content, json_schema=json_schema)\n\n print(f\"response: {response}\")\n assert response[\"value\"] == [\"apple\", \"banana\", \"orange\"]\n\n ## SCENARIO 2 ## - W/OUT ARRAY\n xml_content = \"\"\"get_current_weather\\n\\nBoston, MA\\nfahrenheit\\n\"\"\"\n json_schema = {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n }\n\n response = parse_xml_params(xml_content=xml_content, json_schema=json_schema)\n\n print(f\"response: {response}\")\n assert response[\"location\"] == \"Boston, MA\"\n assert response[\"unit\"] == \"fahrenheit\"\n\n\ndef test_completion_claude_3_multi_turn_conversations():\n litellm.set_verbose = True\n litellm.modify_params = True\n messages = [\n {\"role\": \"assistant\", \"content\": \"?\"}, # test first user message auto injection\n {\"role\": \"user\", \"content\": \"Hi!\"},\n {\n \"role\": \"user\",\n \"content\": [{\"type\": \"text\", \"text\": \"What is the weather like today?\"}],\n },\n {\"role\": \"assistant\", \"content\": \"Hi! I am Claude. \"},\n {\"role\": \"assistant\", \"content\": \"Today is a sunny \"},\n ]\n try:\n response = completion(\n model=\"anthropic/claude-sonnet-4-5-20250929\",\n messages=messages,\n )\n print(response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_claude_3_stream():\n litellm.set_verbose = False\n messages = [{\"role\": \"user\", \"content\": \"Hello, world\"}]\n try:\n # test without max tokens\n response = completion(\n model=\"anthropic/claude-sonnet-4-5-20250929\",\n messages=messages,\n max_tokens=10,\n stream=True,\n )\n # Add any assertions, here to check response args\n print(response)\n for chunk in response:\n print(chunk)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef encode_image(image_path):\n import base64\n\n with open(image_path, \"rb\") as image_file:\n return base64.b64encode(image_file.read()).decode(\"utf-8\")\n\n\n@pytest.mark.parametrize(\n \"model\",\n [\n \"gpt-4o\",\n \"azure/gpt-4.1-mini\",\n \"anthropic/claude-sonnet-4-5-20250929\",\n ],\n) #\ndef test_completion_base64(model):\n try:\n import base64\n\n import requests\n\n litellm.set_verbose = True\n url = \"https://dummyimage.com/100/100/fff&text=Test+image\"\n response = requests.get(url)\n file_data = response.content\n\n encoded_file = base64.b64encode(file_data).decode(\"utf-8\")\n base64_image = f\"data:image/png;base64,{encoded_file}\"\n resp = litellm.completion(\n model=model,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": \"Whats in this image?\"},\n {\n \"type\": \"image_url\",\n \"image_url\": {\"url\": base64_image},\n },\n ],\n }\n ],\n )\n print(f\"\\nResponse: {resp}\")\n\n prompt_tokens = resp.usage.prompt_tokens\n except litellm.ServiceUnavailableError as e:\n print(\"got service unavailable error: \", e)\n pass\n except litellm.InternalServerError as e:\n print(\"got internal server error: \", e)\n pass\n except Exception as e:\n if \"500 Internal error encountered.'\" in str(e):\n pass\n else:\n pytest.fail(f\"An exception occurred - {str(e)}\")\n\n\ndef test_completion_mistral_api():\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"mistral/mistral-tiny\",\n max_tokens=5,\n messages=[\n {\n \"role\": \"user\",\n \"content\": \"Hey, how's it going?\",\n }\n ],\n seed=10,\n )\n # Add any assertions here to check the response\n print(response)\n\n cost = litellm.completion_cost(completion_response=response)\n print(\"cost to make mistral completion=\", cost)\n assert cost > 0.0\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"backend api unavailable\")\n@pytest.mark.asyncio\nasync def test_completion_codestral_chat_api():\n try:\n litellm.set_verbose = True\n response = await litellm.acompletion(\n model=\"codestral/codestral-latest\",\n messages=[\n {\n \"role\": \"user\",\n \"content\": \"Hey, how's it going?\",\n }\n ],\n temperature=0.0,\n top_p=1,\n max_tokens=10,\n safe_prompt=False,\n seed=12,\n )\n # Add any assertions here to-check the response\n print(response)\n\n # cost = litellm.completion_cost(completion_response=response)\n # print(\"cost to make mistral completion=\", cost)\n # assert cost > 0.0\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_mistral_api_mistral_large_function_call():\n litellm.set_verbose = True\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"What's the weather like in Boston today in Fahrenheit?\",\n }\n ]\n try:\n # test without max tokens\n response = completion(\n model=\"mistral/mistral-medium-latest\",\n messages=messages,\n tools=tools,\n tool_choice=\"auto\",\n )\n # Add any assertions, here to check response args\n print(response)\n assert isinstance(response.choices[0].message.tool_calls[0].function.name, str)\n assert isinstance(\n response.choices[0].message.tool_calls[0].function.arguments, str\n )\n\n messages.append(\n response.choices[0].message.model_dump()\n ) # Add assistant tool invokes\n tool_result = (\n '{\"location\": \"Boston\", \"temperature\": \"72\", \"unit\": \"fahrenheit\"}'\n )\n # Add user submitted tool results in the OpenAI format\n messages.append(\n {\n \"tool_call_id\": response.choices[0].message.tool_calls[0].id,\n \"role\": \"tool\",\n \"name\": response.choices[0].message.tool_calls[0].function.name,\n \"content\": tool_result,\n }\n )\n # In the second response, Mistral should deduce answer from tool results\n second_response = completion(\n model=\"mistral/mistral-large-latest\",\n messages=messages,\n tools=tools,\n tool_choice=\"auto\",\n )\n print(second_response)\n except litellm.RateLimitError:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(\n reason=\"Since we already test mistral/mistral-tiny in test_completion_mistral_api. This is only for locally verifying azure mistral works\"\n)\ndef test_completion_mistral_azure():\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"mistral/Mistral-large-nmefg\",\n api_key=os.environ[\"MISTRAL_AZURE_AI_API_KEY\"],\n api_base=os.environ[\"MISTRAL_AZURE_AI_API_BASE\"],\n max_tokens=5,\n messages=[\n {\n \"role\": \"user\",\n \"content\": \"Hi from litellm\",\n }\n ],\n )\n # Add any assertions here to check, the response\n print(response)\n\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_mistral_api()\n\n\ndef test_completion_mistral_api_modified_input():\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"mistral/mistral-tiny\",\n max_tokens=5,\n messages=[\n {\n \"role\": \"user\",\n \"content\": [{\"type\": \"text\", \"text\": \"Hey, how's it going?\"}],\n }\n ],\n )\n # Add any assertions here to check the response\n print(response)\n\n cost = litellm.completion_cost(completion_response=response)\n print(\"cost to make mistral completion=\", cost)\n assert cost > 0.0\n except Exception as e:\n if \"500\" in str(e):\n pass\n else:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"this test is flaky\")\ndef test_completion_gpt4_vision():\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"gpt-4-vision-preview\",\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": \"Whats in this image?\"},\n {\n \"type\": \"image_url\",\n \"image_url\": {\n \"url\": \"https://awsmp-logos.s3.amazonaws.com/seller-xw5kijmvmzasy/c233c9ade2ccb5491072ae232c814942.png\"\n },\n },\n ],\n }\n ],\n )\n print(response)\n except openai.RateLimitError:\n print(\"got a rate liimt error\")\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_azure_gpt4_vision()\n\n\ndef test_completion_openai_response_headers():\n \"\"\"\n Tests if LiteLLM reurns response hea\n \"\"\"\n litellm.return_response_headers = True\n\n # /chat/completion\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"hi\",\n }\n ]\n\n response = completion(\n model=\"gpt-4o-mini\",\n messages=messages,\n )\n\n print(f\"response: {response}\")\n\n print(\"response_headers=\", response._response_headers)\n assert response._response_headers is not None\n assert \"x-ratelimit-remaining-tokens\" in response._response_headers\n assert isinstance(\n response._hidden_params[\"additional_headers\"][\n \"llm_provider-x-ratelimit-remaining-requests\"\n ],\n str,\n )\n\n # /chat/completion - with streaming\n\n streaming_response = litellm.completion(\n model=\"gpt-4o-mini\",\n messages=messages,\n stream=True,\n )\n response_headers = streaming_response._response_headers\n print(\"streaming response_headers=\", response_headers)\n assert response_headers is not None\n assert \"x-ratelimit-remaining-tokens\" in response_headers\n assert isinstance(\n response._hidden_params[\"additional_headers\"][\n \"llm_provider-x-ratelimit-remaining-requests\"\n ],\n str,\n )\n\n for chunk in streaming_response:\n print(\"chunk=\", chunk)\n\n # embedding\n embedding_response = litellm.embedding(\n model=\"text-embedding-ada-002\",\n input=\"hello\",\n )\n\n embedding_response_headers = embedding_response._response_headers\n print(\"embedding_response_headers=\", embedding_response_headers)\n assert embedding_response_headers is not None\n assert \"x-ratelimit-remaining-tokens\" in embedding_response_headers\n assert isinstance(\n response._hidden_params[\"additional_headers\"][\n \"llm_provider-x-ratelimit-remaining-requests\"\n ],\n str,\n )\n\n litellm.return_response_headers = False\n\n\n@pytest.mark.asyncio()\nasync def test_async_completion_openai_response_headers():\n \"\"\"\n Tests if LiteLLM reurns response hea\n \"\"\"\n litellm.return_response_headers = True\n\n # /chat/completion\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"hi\",\n }\n ]\n\n response = await litellm.acompletion(\n model=\"gpt-4o-mini\",\n messages=messages,\n )\n\n print(f\"response: {response}\")\n\n print(\"response_headers=\", response._response_headers)\n assert response._response_headers is not None\n assert \"x-ratelimit-remaining-tokens\" in response._response_headers\n\n # /chat/completion with streaming\n\n streaming_response = await litellm.acompletion(\n model=\"gpt-4o-mini\",\n messages=messages,\n stream=True,\n )\n response_headers = streaming_response._response_headers\n print(\"streaming response_headers=\", response_headers)\n assert response_headers is not None\n assert \"x-ratelimit-remaining-tokens\" in response_headers\n\n async for chunk in streaming_response:\n print(\"chunk=\", chunk)\n\n # embedding\n embedding_response = await litellm.aembedding(\n model=\"text-embedding-ada-002\",\n input=\"hello\",\n )\n\n embedding_response_headers = embedding_response._response_headers\n print(\"embedding_response_headers=\", embedding_response_headers)\n assert embedding_response_headers is not None\n assert \"x-ratelimit-remaining-tokens\" in embedding_response_headers\n\n litellm.return_response_headers = False\n\n\n@pytest.mark.parametrize(\"model\", [\"gpt-3.5-turbo\", \"gpt-4\", \"gpt-4o\"])\ndef test_completion_openai_params(model):\n litellm.drop_params = True\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"\"\"Generate JSON about Bill Gates: { \"full_name\": \"\", \"title\": \"\" }\"\"\",\n }\n ]\n\n response = completion(\n model=model,\n messages=messages,\n response_format={\"type\": \"json_object\"},\n )\n\n print(f\"response: {response}\")\n\n\ndef test_completion_fireworks_ai():\n \"\"\"\n Mocked so it does not depend on Fireworks' rotating serverless catalog\n (no externally-verifiable model list exists). Asserts the request is\n built correctly and the OpenAI-compatible response is parsed back.\n \"\"\"\n litellm.set_verbose = True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\"role\": \"user\", \"content\": \"Hey\"},\n ]\n\n mock_response = MagicMock()\n mock_response.status_code = 200\n mock_response.headers = {\"content-type\": \"application/json\"}\n mock_response.json.return_value = {\n \"id\": \"chatcmpl-test\",\n \"object\": \"chat.completion\",\n \"created\": 1234567890,\n \"model\": \"accounts/fireworks/models/deepseek-v3p1\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\"role\": \"assistant\", \"content\": \"Hello there!\"},\n \"finish_reason\": \"stop\",\n }\n ],\n \"usage\": {\"prompt_tokens\": 10, \"completion_tokens\": 2, \"total_tokens\": 12},\n }\n mock_response.text = json.dumps(mock_response.json.return_value)\n\n client = HTTPHandler()\n with patch.object(client, \"post\", return_value=mock_response) as mock_post:\n response = completion(\n model=\"fireworks_ai/accounts/fireworks/models/deepseek-v3p1\",\n messages=messages,\n client=client,\n )\n\n mock_post.assert_called_once()\n request_body = json.loads(mock_post.call_args.kwargs[\"data\"])\n assert \"deepseek-v3p1\" in request_body[\"model\"]\n assert request_body[\"messages\"] == messages\n assert response.choices[0].message.content == \"Hello there!\"\n assert response.usage.total_tokens == 12\n\n\n@pytest.mark.parametrize(\n \"api_key, api_base\", [(None, \"my-bad-api-base\"), (\"my-bad-api-key\", None)]\n)\ndef test_completion_fireworks_ai_dynamic_params(api_key, api_base):\n try:\n litellm.set_verbose = True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n ]\n response = completion(\n model=\"fireworks_ai/accounts/fireworks/models/mixtral-8x7b-instruct\",\n messages=messages,\n api_base=api_base,\n api_key=api_key,\n )\n pytest.fail(f\"This call should have failed!\")\n except Exception as e:\n pass\n\n\n# @pytest.mark.skip(reason=\"this test is flaky\")\ndef test_completion_perplexity_api():\n try:\n response_object = {\n \"id\": \"a8f37485-026e-45da-81a9-cf0184896840\",\n \"model\": \"llama-3-sonar-small-32k-online\",\n \"created\": 1722186391,\n \"usage\": {\"prompt_tokens\": 17, \"completion_tokens\": 65, \"total_tokens\": 82},\n \"citations\": [\n \"https://www.sciencedirect.com/science/article/pii/S007961232200156X\",\n \"https://www.britannica.com/event/World-War-II\",\n \"https://www.loc.gov/classroom-materials/united-states-history-primary-source-timeline/great-depression-and-world-war-ii-1929-1945/world-war-ii/\",\n \"https://www.nationalww2museum.org/war/topics/end-world-war-ii-1945\",\n \"https://en.wikipedia.org/wiki/World_War_II\",\n ],\n \"object\": \"chat.completion\",\n \"choices\": [\n {\n \"index\": 0,\n \"finish_reason\": \"stop\",\n \"message\": {\n \"role\": \"assistant\",\n \"content\": \"World War II was won by the Allied powers, which included the United States, the Soviet Union, Great Britain, France, China, and other countries. The war concluded with the surrender of Germany on May 8, 1945, and Japan on September 2, 1945[2][3][4].\",\n },\n \"delta\": {\"role\": \"assistant\", \"content\": \"\"},\n }\n ],\n }\n\n from openai import OpenAI\n from openai.types.chat.chat_completion import ChatCompletion\n\n pydantic_obj = ChatCompletion(**response_object)\n\n def _return_pydantic_obj(*args, **kwargs):\n new_response = MagicMock()\n new_response.headers = {\"hello\": \"world\"}\n\n new_response.parse.return_value = pydantic_obj\n return new_response\n\n openai_client = OpenAI()\n\n with patch.object(\n openai_client.chat.completions.with_raw_response,\n \"create\",\n side_effect=_return_pydantic_obj,\n ) as mock_client:\n # litellm.set_verbose= True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n ]\n response = completion(\n model=\"mistral-7b-instruct\",\n messages=messages,\n api_base=\"https://api.perplexity.ai\",\n client=openai_client,\n )\n print(response)\n assert hasattr(response, \"citations\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_perplexity_api()\n\n\n@pytest.mark.skip(reason=\"this test is flaky\")\ndef test_completion_perplexity_api_2():\n try:\n # litellm.set_verbose=True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n ]\n response = completion(model=\"perplexity/mistral-7b-instruct\", messages=messages)\n print(response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_perplexity_api_2()\n\n# commenting out as this is a flaky test on circle-ci\n# def test_completion_nlp_cloud():\n# try:\n# messages = [\n# {\"role\": \"system\", \"content\": \"You are a helpful assistant.\"},\n# {\n# \"role\": \"user\",\n# \"content\": \"how does a court case get to the Supreme Court?\",\n# },\n# ]\n# response = completion(model=\"dolphin\", messages=messages, logger_fn=logger_fn)\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_completion_nlp_cloud()\n\n######### HUGGING FACE TESTS ########################\n#####################################################\n\"\"\"\nHF Tests we should pass\n- TGI:\n - Pro Inference API\n - Deployed Endpoint\n- Coversational\n - Free Inference API\n - Deployed Endpoint\n- Neither TGI or Coversational\n - Free Inference API\n - Deployed Endpoint\n\"\"\"\n\n\n@pytest.mark.parametrize(\n \"provider\", [\"openai\", \"lm_studio\", \"llamafile\"]\n) # \"vertex_ai\", hosted_vllm removed - no longer uses OpenAI client\n@pytest.mark.asyncio\nasync def test_openai_compatible_custom_api_base(provider):\n litellm.set_verbose = True\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"Hello world\",\n }\n ]\n from openai import OpenAI\n\n openai_client = OpenAI(api_key=\"fake-key\")\n\n with patch.object(\n openai_client.chat.completions, \"create\", new=MagicMock()\n ) as mock_call:\n try:\n completion(\n model=\"{provider}/my-vllm-model\".format(provider=provider),\n messages=messages,\n response_format={\"type\": \"json_object\"},\n client=openai_client,\n api_base=\"my-custom-api-base\",\n hello=\"world\",\n )\n except Exception as e:\n print(e)\n\n mock_call.assert_called_once()\n\n print(\"Call KWARGS - {}\".format(mock_call.call_args.kwargs))\n\n assert \"hello\" in mock_call.call_args.kwargs[\"extra_body\"]\n\n\n@pytest.mark.parametrize(\n \"provider\",\n [\n \"openai\",\n \"llamafile\",\n ],\n) # \"vertex_ai\", hosted_vllm removed - no longer uses OpenAI client\n@pytest.mark.asyncio\nasync def test_openai_compatible_custom_api_video(provider):\n litellm.set_verbose = True\n messages = [\n {\n \"role\": \"user\",\n \"content\": [\n {\n \"type\": \"text\",\n \"text\": \"What do you see in this video?\",\n },\n {\n \"type\": \"video_url\",\n \"video_url\": {\"url\": \"https://www.youtube.com/watch?v=29_ipKNI8I0\"},\n },\n ],\n }\n ]\n from openai import OpenAI\n\n openai_client = OpenAI(api_key=\"fake-key\")\n\n with patch.object(\n openai_client.chat.completions, \"create\", new=MagicMock()\n ) as mock_call:\n try:\n completion(\n model=\"{provider}/my-vllm-model\".format(provider=provider),\n messages=messages,\n response_format={\"type\": \"json_object\"},\n client=openai_client,\n api_base=\"my-custom-api-base\",\n )\n except Exception as e:\n print(e)\n\n mock_call.assert_called_once()\n\n\ndef test_lm_studio_completion(monkeypatch):\n monkeypatch.delenv(\"LM_STUDIO_API_KEY\", raising=False)\n monkeypatch.delenv(\"OPENAI_API_KEY\", raising=False)\n litellm._turn_on_debug()\n try:\n completion(\n api_key=\"fake-key\",\n model=\"lm_studio/typhoon2-quen2.5-7b-instruct\",\n messages=[\n {\"role\": \"user\", \"content\": \"What's the weather like in San Francisco?\"}\n ],\n api_base=FAKE_OPENAI_API_BASE,\n )\n except litellm.AuthenticationError as e:\n pytest.fail(f\"Error occurred: {e}\")\n except litellm.APIError as e:\n print(e)\n\n\n# ################### Hugging Face Conversational models ########################\n# def hf_test_completion_conv():\n# try:\n# response = litellm.completion(\n# model=\"huggingface/facebook/blenderbot-3B\",\n# messages=[{ \"content\": \"Hello, how are you?\",\"role\": \"user\"}],\n# )\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n# hf_test_completion_conv()\n\n# ################### Hugging Face Neither TGI or Conversational models ########################\n# # Neither TGI or Conversational task\n# def hf_test_completion_none_task():\n# try:\n# user_message = \"My name is Merve and my favorite\"\n# messages = [{ \"content\": user_message,\"role\": \"user\"}]\n# response = completion(\n# model=\"huggingface/roneneldan/TinyStories-3M\",\n# messages=messages,\n# api_base=\"https://p69xlsj6rpno5drq.us-east-1.aws.endpoints.huggingface.cloud\",\n# )\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n# hf_test_completion_none_task()\n\n\ndef mock_post(url, **kwargs):\n print(f\"url={url}\")\n if \"text-classification\" in url:\n raise Exception(\"Model not found\")\n mock_response = MagicMock()\n mock_response.status_code = 200\n mock_response.headers = {\"Content-Type\": \"application/json\"}\n mock_response.json.return_value = [\n [\n {\"label\": \"LABEL_0\", \"score\": 0.9990691542625427},\n {\"label\": \"LABEL_1\", \"score\": 0.0009308889275416732},\n ]\n ]\n return mock_response\n\n\ndef test_ollama_image():\n \"\"\"\n Test that datauri prefixes are removed, JPEG/PNG images are passed\n through, and other image formats are converted to JPEG. Non-image\n data is untouched.\n \"\"\"\n\n import base64\n import io\n\n from PIL import Image\n\n def mock_post(url, **kwargs):\n mock_response = MagicMock()\n mock_response.status_code = 200\n mock_response.headers = {\"Content-Type\": \"application/json\"}\n data_json = json.loads(kwargs[\"data\"])\n mock_response.json.return_value = {\n # return the image in the response so that it can be tested\n # against the original\n \"response\": data_json[\"images\"]\n }\n return mock_response\n\n def make_b64image(format):\n image = Image.new(mode=\"RGB\", size=(1, 1))\n image_buffer = io.BytesIO()\n image.save(image_buffer, format)\n return base64.b64encode(image_buffer.getvalue()).decode(\"utf-8\")\n\n jpeg_image = make_b64image(\"JPEG\")\n webp_image = make_b64image(\"WEBP\")\n png_image = make_b64image(\"PNG\")\n\n base64_data = base64.b64encode(b\"some random data\")\n datauri_base64_data = f\"data:text/plain;base64,{base64_data}\"\n\n tests = [\n # input expected\n [jpeg_image, jpeg_image],\n [webp_image, None],\n [png_image, png_image],\n [f\"data:image/jpeg;base64,{jpeg_image}\", jpeg_image],\n [f\"data:image/webp;base64,{webp_image}\", None],\n [f\"data:image/png;base64,{png_image}\", png_image],\n [datauri_base64_data, datauri_base64_data],\n ]\n\n client = HTTPHandler()\n for test in tests:\n try:\n with patch.object(client, \"post\", side_effect=mock_post):\n response = completion(\n model=\"ollama/llava\",\n messages=[\n {\n \"role\": \"user\",\n \"content\": [\n {\"type\": \"text\", \"text\": \"Whats in this image?\"},\n {\n \"type\": \"image_url\",\n \"image_url\": {\"url\": test[0]},\n },\n ],\n }\n ],\n client=client,\n )\n if not test[1]:\n # the conversion process may not always generate the same image,\n # so just check for a JPEG image when a conversion was done.\n image_data = response[\"choices\"][0][\"message\"][\"content\"][0]\n image = Image.open(io.BytesIO(base64.b64decode(image_data)))\n assert image.format == \"JPEG\"\n else:\n assert response[\"choices\"][0][\"message\"][\"content\"][0] == test[1]\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n########################### End of Hugging Face Tests ##############################################\n# def test_completion_hf_api():\n# # failing on circle-ci commenting out\n# try:\n# user_message = \"write some code to find the sum of two numbers\"\n# messages = [{ \"content\": user_message,\"role\": \"user\"}]\n# api_base = \"https://a8l9e3ucxinyl3oj.us-east-1.aws.endpoints.huggingface.cloud\"\n# response = completion(model=\"huggingface/meta-llama/Llama-2-7b-chat-hf\", messages=messages, api_base=api_base)\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# if \"loading\" in str(e):\n# pass\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_completion_hf_api()\n\n# def test_completion_hf_api_best_of():\n# # failing on circle ci commenting out\n# try:\n# user_message = \"write some code to find the sum of two numbers\"\n# messages = [{ \"content\": user_message,\"role\": \"user\"}]\n# api_base = \"https://a8l9e3ucxinyl3oj.us-east-1.aws.endpoints.huggingface.cloud\"\n# response = completion(model=\"huggingface/meta-llama/Llama-2-7b-chat-hf\", messages=messages, api_base=api_base, n=2)\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# if \"loading\" in str(e):\n# pass\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_completion_hf_api_best_of()\n\n# def test_completion_hf_deployed_api():\n# try:\n# user_message = \"There's a llama in my garden 😱 What should I do?\"\n# messages = [{ \"content\": user_message,\"role\": \"user\"}]\n# response = completion(model=\"huggingface/https://ji16r2iys9a8rjk2.us-east-1.aws.endpoints.huggingface.cloud\", messages=messages, logger_fn=logger_fn)\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n\n# this should throw an exception, to trigger https://logs.litellm.ai/\n# def hf_test_error_logs():\n# try:\n# litellm.set_verbose=True\n# user_message = \"My name is Merve and my favorite\"\n# messages = [{ \"content\": user_message,\"role\": \"user\"}]\n# response = completion(\n# model=\"huggingface/roneneldan/TinyStories-3M\",\n# messages=messages,\n# api_base=\"https://p69xlsj6rpno5drq.us-east-1.aws.endpoints.huggingface.cloud\",\n\n# )\n# # Add any assertions here to check the response\n# print(response)\n\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# hf_test_error_logs()\n\n\ndef test_completion_openai():\n try:\n litellm.set_verbose = True\n litellm.drop_params = True\n print(f\"api key: {os.environ['OPENAI_API_KEY']}\")\n litellm.api_key = os.environ[\"OPENAI_API_KEY\"]\n response = completion(\n model=\"gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"Hey\"}],\n max_tokens=10,\n metadata={\"hi\": \"bye\"},\n )\n print(\"This is the response object\\n\", response)\n\n response_str = response[\"choices\"][0][\"message\"][\"content\"]\n response_str_2 = response.choices[0].message.content\n\n cost = completion_cost(completion_response=response)\n print(\"Cost for completion call with gpt-3.5-turbo: \", f\"${float(cost):.10f}\")\n assert response_str == response_str_2\n assert type(response_str) == str\n assert len(response_str) > 1\n\n litellm.api_key = None\n except Timeout as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.parametrize(\n \"model, api_version\",\n [\n # (\"gpt-4o-2024-08-06\", None),\n # (\"azure/gpt-4.1-mini\", None),\n (\"bedrock/anthropic.claude-3-sonnet-20240229-v1:0\", None),\n # (\"azure/gpt-4o-new-test\", \"2024-08-01-preview\"),\n ],\n)\n@pytest.mark.flaky(retries=3, delay=1)\ndef test_completion_openai_pydantic(model, api_version):\n try:\n litellm._turn_on_debug()\n from pydantic import BaseModel\n\n messages = [\n {\"role\": \"user\", \"content\": \"List 5 important events in the XIX century\"}\n ]\n\n class CalendarEvent(BaseModel):\n name: str\n date: str\n participants: list[str]\n\n class EventsList(BaseModel):\n events: list[CalendarEvent]\n\n litellm.enable_json_schema_validation = True\n for _ in range(3):\n try:\n response = completion(\n model=model,\n messages=messages,\n metadata={\"hi\": \"bye\"},\n response_format=EventsList,\n api_version=api_version,\n )\n break\n except litellm.JSONSchemaValidationError:\n pytest.fail(\"ERROR OCCURRED! INVALID JSON\")\n\n print(\"This is the response object\\n\", response)\n\n response_str = response[\"choices\"][0][\"message\"][\"content\"]\n\n print(f\"response_str: {response_str}\")\n json.loads(response_str) # check valid json is returned\n\n except Timeout:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_text_openai():\n try:\n # litellm.set_verbose =True\n response = completion(model=\"gpt-3.5-turbo-instruct\", messages=messages)\n print(response[\"choices\"][0][\"message\"][\"content\"])\n except Exception as e:\n print(e)\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.asyncio\nasync def test_completion_text_openai_async():\n try:\n # litellm.set_verbose =True\n response = await litellm.acompletion(\n model=\"gpt-3.5-turbo-instruct\", messages=messages\n )\n print(response[\"choices\"][0][\"message\"][\"content\"])\n except Exception as e:\n print(e)\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef custom_callback(\n kwargs, # kwargs to completion\n completion_response, # response from completion\n start_time,\n end_time, # start/end time\n):\n # Your custom code here\n try:\n print(\"LITELLM: in custom callback function\")\n print(\"\\nkwargs\\n\", kwargs)\n model = kwargs[\"model\"]\n messages = kwargs[\"messages\"]\n user = kwargs.get(\"user\")\n\n #################################################\n\n print(\n f\"\"\"\n Model: {model},\n Messages: {messages},\n User: {user},\n Seed: {kwargs[\"seed\"]},\n temperature: {kwargs[\"temperature\"]},\n \"\"\"\n )\n\n assert kwargs[\"user\"] == \"ishaans app\"\n assert kwargs[\"model\"] == \"gpt-3.5-turbo-1106\"\n assert kwargs[\"seed\"] == 12\n assert kwargs[\"temperature\"] == 0.5\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_openai_with_optional_params():\n # [Proxy PROD TEST] WARNING: DO NOT DELETE THIS TEST\n # assert that `user` gets passed to the completion call\n # Note: This tests that we actually send the optional params to the completion call\n # We use custom callbacks to test this\n try:\n litellm.set_verbose = True\n litellm.success_callback = [custom_callback]\n response = completion(\n model=\"gpt-3.5-turbo-1106\",\n messages=[\n {\"role\": \"user\", \"content\": \"respond in valid, json - what is the day\"}\n ],\n temperature=0.5,\n top_p=0.1,\n seed=12,\n response_format={\"type\": \"json_object\"},\n logit_bias=None,\n user=\"ishaans app\",\n )\n # Add any assertions here to check the response\n\n print(response)\n litellm.success_callback = [] # unset callbacks\n\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_openai_with_optional_params()\n\n\ndef test_completion_logprobs():\n \"\"\"\n This function is used to test the litellm.completion logprobs functionality.\n\n Parameters:\n None\n\n Returns:\n None\n \"\"\"\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"what is the time\"}],\n temperature=0.5,\n top_p=0.1,\n seed=12,\n logit_bias=None,\n user=\"ishaans app\",\n logprobs=True,\n top_logprobs=3,\n )\n # Add any assertions here to check the response\n\n print(response)\n print(len(response.choices[0].logprobs[\"content\"][0][\"top_logprobs\"]))\n assert \"logprobs\" in response.choices[0]\n assert \"content\" in response.choices[0][\"logprobs\"]\n assert len(response.choices[0].logprobs[\"content\"][0][\"top_logprobs\"]) == 3\n\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_logprobs()\n\n\ndef test_completion_logprobs_stream():\n \"\"\"\n This function is used to test the litellm.completion logprobs functionality.\n\n Parameters:\n None\n\n Returns:\n None\n \"\"\"\n try:\n litellm.set_verbose = False\n response = completion(\n model=\"gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"what is the time\"}],\n temperature=0.5,\n top_p=0.1,\n seed=12,\n max_tokens=5,\n logit_bias=None,\n user=\"ishaans app\",\n logprobs=True,\n top_logprobs=3,\n stream=True,\n )\n # Add any assertions here to check the response\n\n print(response)\n\n found_logprob = False\n for chunk in response:\n # check if atleast one chunk has log probs\n print(chunk)\n if len(chunk.choices) == 0:\n continue\n print(f\"chunk.choices[0]: {chunk.choices[0]}\")\n if (\n \"logprobs\" in chunk.choices[0]\n and chunk.choices[0].logprobs is not None\n and len(chunk.choices[0].logprobs.content) > 0\n ):\n # assert we got a valid logprob in the choices\n assert len(chunk.choices[0].logprobs.content[0].top_logprobs) == 3\n found_logprob = True\n break\n print(chunk)\n assert found_logprob == True\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_logprobs_stream()\n\n\ndef test_completion_openai_litellm_key():\n try:\n litellm.set_verbose = True\n litellm.num_retries = 0\n litellm.api_key = os.environ[\"OPENAI_API_KEY\"]\n\n # ensure key is set to None in .env and in openai.api_key\n os.environ[\"OPENAI_API_KEY\"] = \"\"\n import openai\n\n openai.api_key = \"\"\n ##########################################################\n\n response = completion(\n model=\"gpt-3.5-turbo\",\n messages=messages,\n temperature=0.5,\n top_p=0.1,\n max_tokens=10,\n user=\"ishaan_dev@berri.ai\",\n )\n # Add any assertions here to check the response\n print(response)\n\n ###### reset environ key\n os.environ[\"OPENAI_API_KEY\"] = litellm.api_key\n\n ##### unset litellm var\n litellm.api_key = None\n except Timeout as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_ completion_openai_litellm_key()\n\n\n@pytest.mark.skip(reason=\"Unresponsive endpoint.[TODO] Rehost this somewhere else\")\ndef test_completion_ollama_hosted():\n try:\n litellm.request_timeout = 20 # give ollama 20 seconds to response\n litellm.set_verbose = True\n response = completion(\n model=\"ollama/phi\",\n messages=messages,\n max_tokens=20,\n # api_base=\"https://test-ollama-endpoint.onrender.com\",\n )\n # Add any assertions here to check the response\n print(response)\n except openai.APITimeoutError as e:\n print(\"got a timeout error. Passed ! \")\n litellm.request_timeout = None\n pass\n except Exception as e:\n if \"try pulling it first\" in str(e):\n return\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_ollama_hosted()\n\n\n@pytest.mark.skip(reason=\"Local test\")\n@pytest.mark.parametrize(\n (\"model\"),\n [\n \"ollama/llama2\",\n \"ollama_chat/llama2\",\n ],\n)\ndef test_completion_ollama_function_call(model):\n messages = [\n {\"role\": \"user\", \"content\": \"What's the weather like in San Francisco?\"}\n ]\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n try:\n litellm.set_verbose = True\n response = litellm.completion(model=model, messages=messages, tools=tools)\n print(response)\n assert response.choices[0].message.tool_calls\n assert (\n response.choices[0].message.tool_calls[0].function.name\n == \"get_current_weather\"\n )\n assert response.choices[0].finish_reason == \"tool_calls\"\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"Local test\")\n@pytest.mark.parametrize(\n (\"model\"),\n [\n \"ollama/llama2\",\n \"ollama_chat/llama2\",\n ],\n)\ndef test_completion_ollama_function_call_stream(model):\n messages = [\n {\"role\": \"user\", \"content\": \"What's the weather like in San Francisco?\"}\n ]\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n try:\n litellm.set_verbose = True\n response = litellm.completion(\n model=model, messages=messages, tools=tools, stream=True\n )\n print(response)\n first_chunk = next(response)\n assert first_chunk.choices[0].delta.tool_calls\n assert (\n first_chunk.choices[0].delta.tool_calls[0].function.name\n == \"get_current_weather\"\n )\n assert first_chunk.choices[0].finish_reason == \"tool_calls\"\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"local test\")\n@pytest.mark.parametrize(\n (\"model\"),\n [\n \"ollama/llama2\",\n \"ollama_chat/llama2\",\n ],\n)\n@pytest.mark.asyncio\nasync def test_acompletion_ollama_function_call(model):\n messages = [\n {\"role\": \"user\", \"content\": \"What's the weather like in San Francisco?\"}\n ]\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n try:\n litellm.set_verbose = True\n response = await litellm.acompletion(\n model=model, messages=messages, tools=tools\n )\n print(response)\n assert response.choices[0].message.tool_calls\n assert (\n response.choices[0].message.tool_calls[0].function.name\n == \"get_current_weather\"\n )\n assert response.choices[0].finish_reason == \"tool_calls\"\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"local test\")\n@pytest.mark.parametrize(\n (\"model\"),\n [\n \"ollama/llama2\",\n \"ollama_chat/llama2\",\n ],\n)\n@pytest.mark.asyncio\nasync def test_acompletion_ollama_function_call_stream(model):\n messages = [\n {\"role\": \"user\", \"content\": \"What's the weather like in San Francisco?\"}\n ]\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n try:\n litellm.set_verbose = True\n response = await litellm.acompletion(\n model=model, messages=messages, tools=tools, stream=True\n )\n print(response)\n first_chunk = await anext(response)\n assert first_chunk.choices[0].delta.tool_calls\n assert (\n first_chunk.choices[0].delta.tool_calls[0].function.name\n == \"get_current_weather\"\n )\n assert first_chunk.choices[0].finish_reason == \"tool_calls\"\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_openrouter_reasoning_effort():\n try:\n litellm.set_verbose = True\n response = completion(\n model=\"openrouter/deepseek/deepseek-r1\",\n messages=messages,\n include_reasoning=True,\n max_tokens=5,\n )\n # Add any assertions here to check the response\n print(response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_openrouter1()\n\n\ndef test_completion_hf_model_no_provider():\n try:\n response = completion(\n model=\"WizardLM/WizardLM-70B-V1.0\",\n messages=messages,\n max_tokens=5,\n )\n # Add any assertions here to check the response\n print(response)\n pytest.fail(f\"Error occurred: {e}\")\n except Exception as e:\n pass\n\n\n# test_completion_hf_model_no_provider()\n\n\ndef gemini_mock_post(*args, **kwargs):\n mock_response = MagicMock()\n mock_response.status_code = 200\n mock_response.headers = {\"Content-Type\": \"application/json\"}\n mock_response.json = MagicMock(\n return_value={\n \"candidates\": [\n {\n \"content\": {\n \"parts\": [\n {\n \"functionCall\": {\n \"name\": \"get_current_weather\",\n \"args\": {\"location\": \"Boston, MA\"},\n }\n }\n ],\n \"role\": \"model\",\n },\n \"finishReason\": \"STOP\",\n \"index\": 0,\n \"safetyRatings\": [\n {\n \"category\": \"HARM_CATEGORY_SEXUALLY_EXPLICIT\",\n \"probability\": \"NEGLIGIBLE\",\n },\n {\n \"category\": \"HARM_CATEGORY_HARASSMENT\",\n \"probability\": \"NEGLIGIBLE\",\n },\n {\n \"category\": \"HARM_CATEGORY_HATE_SPEECH\",\n \"probability\": \"NEGLIGIBLE\",\n },\n {\n \"category\": \"HARM_CATEGORY_DANGEROUS_CONTENT\",\n \"probability\": \"NEGLIGIBLE\",\n },\n ],\n }\n ],\n \"usageMetadata\": {\n \"promptTokenCount\": 86,\n \"candidatesTokenCount\": 19,\n \"totalTokenCount\": 105,\n },\n }\n )\n\n return mock_response\n\n\n@pytest.mark.asyncio\nasync def test_completion_functions_param():\n litellm.set_verbose = True\n function1 = [\n {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n }\n ]\n try:\n from litellm.llms.custom_httpx.http_handler import AsyncHTTPHandler\n\n messages = [{\"role\": \"user\", \"content\": \"What is the weather like in Boston?\"}]\n\n client = AsyncHTTPHandler(concurrent_limit=1)\n\n with patch.object(client, \"post\", side_effect=gemini_mock_post) as mock_client:\n response: litellm.ModelResponse = await litellm.acompletion(\n model=\"gemini/gemini-1.5-pro\",\n messages=messages,\n functions=function1,\n client=client,\n )\n print(response)\n # Add any assertions here to check the response\n mock_client.assert_called()\n print(f\"mock_client.call_args.kwargs: {mock_client.call_args.kwargs}\")\n assert \"tools\" in mock_client.call_args.kwargs[\"json\"]\n assert (\n \"litellm_param_is_function_call\"\n not in mock_client.call_args.kwargs[\"json\"]\n )\n assert response.choices[0].message.function_call is not None\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_anyscale_with_functions()\n\n\ndef test_completion_azure_extra_headers():\n # this tests if we can pass api_key to completion, when it's not in the env.\n # DO NOT REMOVE THIS TEST. No MATTER WHAT Happens!\n # If you want to remove it, speak to Ishaan!\n # Ishaan will be very disappointed if this test is removed -> this is a standard way to pass api_key + the router + proxy use this\n from httpx import Client\n from openai import AzureOpenAI\n\n from litellm.llms.custom_httpx.httpx_handler import HTTPHandler\n\n http_client = Client()\n\n with patch.object(http_client, \"send\", new=MagicMock()) as mock_client:\n litellm.client_session = http_client\n try:\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=messages,\n api_base=os.getenv(\"AZURE_AI_API_BASE\"),\n api_version=\"2023-07-01-preview\",\n api_key=os.getenv(\"AZURE_AI_API_KEY\"),\n extra_headers={\n \"Authorization\": \"my-bad-key\",\n \"Ocp-Apim-Subscription-Key\": \"hello-world-testing\",\n },\n )\n print(response)\n pytest.fail(\"Expected this to fail\")\n except Exception as e:\n pass\n\n mock_client.assert_called()\n\n print(f\"mock_client.call_args: {mock_client.call_args}\")\n request = mock_client.call_args[0][0]\n print(request.method) # This will print 'POST'\n print(request.url) # This will print the full URL\n print(request.headers) # This will print the full URL\n auth_header = request.headers.get(\"Authorization\")\n apim_key = request.headers.get(\"Ocp-Apim-Subscription-Key\")\n print(auth_header)\n assert auth_header == \"my-bad-key\"\n assert apim_key == \"hello-world-testing\"\n\n\ndef test_completion_azure_ad_token():\n # this tests if we can pass api_key to completion, when it's not in the env.\n # DO NOT REMOVE THIS TEST. No MATTER WHAT Happens!\n # If you want to remove it, speak to Ishaan!\n # Ishaan will be very disappointed if this test is removed -> this is a standard way to pass api_key + the router + proxy use this\n from httpx import Client\n\n from litellm import completion\n\n litellm.set_verbose = True\n\n old_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ.pop(\"AZURE_AI_API_KEY\", None)\n\n http_client = Client()\n\n with patch.object(http_client, \"send\", new=MagicMock()) as mock_client:\n litellm.client_session = http_client\n try:\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=messages,\n azure_ad_token=\"my-special-token\",\n )\n print(response)\n except Exception as e:\n pass\n finally:\n os.environ[\"AZURE_AI_API_KEY\"] = old_key\n\n mock_client.assert_called_once()\n request = mock_client.call_args[0][0]\n print(request.method) # This will print 'POST'\n print(request.url) # This will print the full URL\n print(request.headers) # This will print the full URL\n auth_header = request.headers.get(\"Authorization\")\n assert auth_header == \"Bearer my-special-token\"\n\n\ndef test_completion_azure_key_completion_arg():\n # this tests if we can pass api_key to completion, when it's not in the env.\n # DO NOT REMOVE THIS TEST. No MATTER WHAT Happens!\n # If you want to remove it, speak to Ishaan!\n # Ishaan will be very disappointed if this test is removed -> this is a standard way to pass api_key + the router + proxy use this\n old_key = os.environ[\"AZURE_AI_API_KEY\"]\n os.environ.pop(\"AZURE_AI_API_KEY\", None)\n try:\n print(\"azure gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n ## Test azure call\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=messages,\n api_key=old_key,\n logprobs=True,\n max_tokens=10,\n )\n\n print(f\"response: {response}\")\n\n print(\"Hidden Params\", response._hidden_params)\n assert response._hidden_params[\"custom_llm_provider\"] == \"azure\"\n os.environ[\"AZURE_AI_API_KEY\"] = old_key\n except Exception as e:\n os.environ[\"AZURE_AI_API_KEY\"] = old_key\n pytest.fail(f\"Error occurred: {e}\")\n\n\nasync def test_re_use_azure_async_client():\n try:\n print(\"azure gpt-3.5 ASYNC with clie nttest\\n\\n\")\n litellm.set_verbose = True\n import openai\n\n client = openai.AsyncAzureOpenAI(\n azure_endpoint=os.environ[\"AZURE_AI_API_BASE\"],\n api_key=os.environ[\"AZURE_AI_API_KEY\"],\n api_version=\"2023-07-01-preview\",\n )\n ## Test azure call\n for _ in range(3):\n response = await litellm.acompletion(\n model=\"azure/gpt-4.1-mini\", messages=messages, client=client\n )\n print(f\"response: {response}\")\n except Exception as e:\n pytest.fail(\"got Exception\", e)\n\n\ndef test_re_use_openaiClient():\n try:\n print(\"gpt-3.5 with client test\\n\\n\")\n litellm.set_verbose = True\n import openai\n\n client = openai.OpenAI(\n api_key=os.environ[\"OPENAI_API_KEY\"],\n )\n ## Test OpenAI call\n for _ in range(2):\n response = litellm.completion(\n model=\"gpt-3.5-turbo\", messages=messages, client=client\n )\n print(f\"response: {response}\")\n except Exception as e:\n pytest.fail(\"got Exception\", e)\n\n\n@pytest.mark.skip(\n reason=\"this is bad test. It doesn't actually fail if the token is not set in the header. \"\n)\ndef test_azure_openai_ad_token():\n import time\n\n # this tests if the azure ad token is set in the request header\n # the request can fail since azure ad tokens expire after 30 mins, but the header MUST have the azure ad token\n # we use litellm.input_callbacks for this test\n def tester(\n kwargs, # kwargs to completion\n ):\n print(\"inside kwargs\")\n print(kwargs[\"additional_args\"])\n if kwargs[\"additional_args\"][\"headers\"][\"Authorization\"] != \"Bearer gm\":\n pytest.fail(\"AZURE AD TOKEN Passed but not set in request header\")\n return\n\n litellm.input_callback = [tester]\n try:\n response = litellm.completion(\n model=\"azure/gpt-4.1-mini\", # e.g. gpt-35-instant\n messages=[\n {\n \"role\": \"user\",\n \"content\": \"what is your name\",\n },\n ],\n azure_ad_token=\"gm\",\n )\n print(\"azure ad token respoonse\\n\")\n print(response)\n litellm.input_callback = []\n except Exception as e:\n litellm.input_callback = []\n pass\n\n time.sleep(1)\n\n\n# test_azure_openai_ad_token()\n\n\ndef test_completion_azure2():\n # test if we can pass api_base, api_version and api_key in compleition()\n try:\n print(\"azure gpt-3.5 test\\n\\n\")\n litellm.set_verbose = False\n api_base = os.environ[\"AZURE_AI_API_BASE\"]\n api_key = os.environ[\"AZURE_AI_API_KEY\"]\n api_version = os.environ[\"AZURE_API_VERSION\"]\n\n os.environ[\"AZURE_AI_API_BASE\"] = \"\"\n os.environ[\"AZURE_API_VERSION\"] = \"\"\n os.environ[\"AZURE_AI_API_KEY\"] = \"\"\n\n ## Test azure call\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=messages,\n api_base=api_base,\n api_key=api_key,\n api_version=api_version,\n max_tokens=10,\n )\n\n # Add any assertions here to check the response\n print(response)\n\n os.environ[\"AZURE_AI_API_BASE\"] = api_base\n os.environ[\"AZURE_API_VERSION\"] = api_version\n os.environ[\"AZURE_AI_API_KEY\"] = api_key\n\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_azure2()\n\n\ndef test_completion_azure3():\n # test if we can pass api_base, api_version and api_key in compleition()\n try:\n print(\"azure gpt-3.5 test\\n\\n\")\n litellm.set_verbose = True\n litellm.api_base = os.environ[\"AZURE_AI_API_BASE\"]\n litellm.api_key = os.environ[\"AZURE_AI_API_KEY\"]\n litellm.api_version = os.environ[\"AZURE_API_VERSION\"]\n\n os.environ[\"AZURE_AI_API_BASE\"] = \"\"\n os.environ[\"AZURE_API_VERSION\"] = \"\"\n os.environ[\"AZURE_AI_API_KEY\"] = \"\"\n\n ## Test azure call\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=messages,\n max_tokens=10,\n )\n\n # Add any assertions here to check the response\n print(response)\n\n os.environ[\"AZURE_AI_API_BASE\"] = litellm.api_base\n os.environ[\"AZURE_API_VERSION\"] = litellm.api_version\n os.environ[\"AZURE_AI_API_KEY\"] = litellm.api_key\n\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_azure3()\n\n\n# new azure test for using litellm. vars,\n# use the following vars in this test and make an azure_api_call\n# litellm.api_type = self.azure_api_type\n# litellm.api_base = self.AZURE_AI_API_BASE\n# litellm.api_version = self.azure_api_version\n# litellm.api_key = self.api_key\ndef test_completion_azure_with_litellm_key():\n try:\n print(\"azure gpt-3.5 test\\n\\n\")\n import openai\n\n #### set litellm vars\n litellm.api_type = \"azure\"\n litellm.api_base = os.environ[\"AZURE_AI_API_BASE\"]\n litellm.api_version = os.environ[\"AZURE_API_VERSION\"]\n litellm.api_key = os.environ[\"AZURE_AI_API_KEY\"]\n\n ######### UNSET ENV VARs for this ################\n os.environ[\"AZURE_AI_API_BASE\"] = \"\"\n os.environ[\"AZURE_API_VERSION\"] = \"\"\n os.environ[\"AZURE_AI_API_KEY\"] = \"\"\n\n ######### UNSET OpenAI vars for this ##############\n openai.api_type = \"\"\n openai.api_base = \"gm\"\n openai.api_version = \"333\"\n openai.api_key = \"ymca\"\n\n response = completion(\n model=\"azure/gpt-4.1-mini\",\n messages=messages,\n )\n # Add any assertions here to check the response\n print(response)\n\n ######### RESET ENV VARs for this ################\n os.environ[\"AZURE_AI_API_BASE\"] = litellm.api_base\n os.environ[\"AZURE_API_VERSION\"] = litellm.api_version\n os.environ[\"AZURE_AI_API_KEY\"] = litellm.api_key\n\n ######### UNSET litellm vars\n litellm.api_type = None\n litellm.api_base = None\n litellm.api_version = None\n litellm.api_key = None\n\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\nimport asyncio\n\n\n@pytest.mark.skip(reason=\"replicate endpoints are extremely flaky\")\n@pytest.mark.parametrize(\"sync_mode\", [False, True])\n@pytest.mark.asyncio\nasync def test_completion_replicate_llama3(sync_mode):\n litellm.set_verbose = True\n model_name = \"replicate/meta/meta-llama-3-8b-instruct\"\n try:\n if sync_mode:\n response = completion(\n model=model_name,\n messages=messages,\n max_tokens=10,\n )\n else:\n response = await litellm.acompletion(\n model=model_name,\n messages=messages,\n max_tokens=10,\n )\n print(f\"ASYNC REPLICATE RESPONSE - {response}\")\n print(f\"REPLICATE RESPONSE - {response}\")\n # Add any assertions here to check the response\n assert isinstance(response, litellm.ModelResponse)\n assert len(response.choices[0].message.content.strip()) > 0\n response_format_tests(response=response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"replicate endpoints take +2 mins just for this request\")\ndef test_completion_replicate_vicuna():\n print(\"TESTING REPLICATE\")\n litellm.set_verbose = True\n model_name = \"replicate/meta/llama-2-7b-chat:f1d50bb24186c52daae319ca8366e53debdaa9e0ae7ff976e918df752732ccc4\"\n try:\n response = completion(\n model=model_name,\n messages=messages,\n temperature=0.5,\n top_k=20,\n repetition_penalty=1,\n min_tokens=1,\n seed=-1,\n max_tokens=2,\n )\n print(response)\n # Add any assertions here to check the response\n response_str = response[\"choices\"][0][\"message\"][\"content\"]\n print(\"RESPONSE STRING\\n\", response_str)\n if type(response_str) != str:\n pytest.fail(f\"Error occurred: {e}\")\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_replicate_vicuna()\n\n\ndef test_replicate_custom_prompt_dict():\n litellm.set_verbose = True\n model_name = \"replicate/meta/llama-2-7b\"\n litellm.register_prompt_template(\n model=\"replicate/meta/llama-2-7b\",\n initial_prompt_value=\"You are a good assistant\", # [OPTIONAL]\n roles={\n \"system\": {\n \"pre_message\": \"[INST] <>\\n\", # [OPTIONAL]\n \"post_message\": \"\\n<>\\n [/INST]\\n\", # [OPTIONAL]\n },\n \"user\": {\n \"pre_message\": \"[INST] \", # [OPTIONAL]\n \"post_message\": \" [/INST]\", # [OPTIONAL]\n },\n \"assistant\": {\n \"pre_message\": \"\\n\", # [OPTIONAL]\n \"post_message\": \"\\n\", # [OPTIONAL]\n },\n },\n final_prompt_value=\"Now answer as best you can:\", # [OPTIONAL]\n )\n try:\n response = completion(\n model=model_name,\n messages=[\n {\n \"role\": \"user\",\n \"content\": \"what is yc write 1 paragraph\",\n }\n ],\n mock_response=\"Hello world\",\n repetition_penalty=0.1,\n num_retries=3,\n )\n\n except litellm.APIError as e:\n pass\n except litellm.APIConnectionError as e:\n pass\n except Exception as e:\n pytest.fail(f\"An exception occurred - {str(e)}\")\n print(f\"response: {response}\")\n litellm.custom_prompt_dict = {} # reset\n\n\ndef test_bedrock_deepseek_custom_prompt_dict():\n model = \"llama/arn:aws:bedrock:us-east-1:1234:imported-model/45d34re\"\n litellm.register_prompt_template(\n model=model,\n tokenizer_config={\n \"add_bos_token\": True,\n \"add_eos_token\": False,\n \"bos_token\": {\n \"__type\": \"AddedToken\",\n \"content\": \"<|begin▁of▁sentence|>\",\n \"lstrip\": False,\n \"normalized\": True,\n \"rstrip\": False,\n \"single_word\": False,\n },\n \"clean_up_tokenization_spaces\": False,\n \"eos_token\": {\n \"__type\": \"AddedToken\",\n \"content\": \"<|end▁of▁sentence|>\",\n \"lstrip\": False,\n \"normalized\": True,\n \"rstrip\": False,\n \"single_word\": False,\n },\n \"legacy\": True,\n \"model_max_length\": 16384,\n \"pad_token\": {\n \"__type\": \"AddedToken\",\n \"content\": \"<|end▁of▁sentence|>\",\n \"lstrip\": False,\n \"normalized\": True,\n \"rstrip\": False,\n \"single_word\": False,\n },\n \"sp_model_kwargs\": {},\n \"unk_token\": None,\n \"tokenizer_class\": \"LlamaTokenizerFast\",\n \"chat_template\": \"{% if not add_generation_prompt is defined %}{% set add_generation_prompt = false %}{% endif %}{% set ns = namespace(is_first=false, is_tool=false, is_output_first=true, system_prompt='') %}{%- for message in messages %}{%- if message['role'] == 'system' %}{% set ns.system_prompt = message['content'] %}{%- endif %}{%- endfor %}{{bos_token}}{{ns.system_prompt}}{%- for message in messages %}{%- if message['role'] == 'user' %}{%- set ns.is_tool = false -%}{{'<|User|>' + message['content']}}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is none %}{%- set ns.is_tool = false -%}{%- for tool in message['tool_calls']%}{%- if not ns.is_first %}{{'<|Assistant|><|tool▁calls▁begin|><|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\\\n' + '```json' + '\\\\n' + tool['function']['arguments'] + '\\\\n' + '```' + '<|tool▁call▁end|>'}}{%- set ns.is_first = true -%}{%- else %}{{'\\\\n' + '<|tool▁call▁begin|>' + tool['type'] + '<|tool▁sep|>' + tool['function']['name'] + '\\\\n' + '```json' + '\\\\n' + tool['function']['arguments'] + '\\\\n' + '```' + '<|tool▁call▁end|>'}}{{'<|tool▁calls▁end|><|end▁of▁sentence|>'}}{%- endif %}{%- endfor %}{%- endif %}{%- if message['role'] == 'assistant' and message['content'] is not none %}{%- if ns.is_tool %}{{'<|tool▁outputs▁end|>' + message['content'] + '<|end▁of▁sentence|>'}}{%- set ns.is_tool = false -%}{%- else %}{% set content = message['content'] %}{% if '' in content %}{% set content = content.split('')[-1] %}{% endif %}{{'<|Assistant|>' + content + '<|end▁of▁sentence|>'}}{%- endif %}{%- endif %}{%- if message['role'] == 'tool' %}{%- set ns.is_tool = true -%}{%- if ns.is_output_first %}{{'<|tool▁outputs▁begin|><|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- set ns.is_output_first = false %}{%- else %}{{'\\\\n<|tool▁output▁begin|>' + message['content'] + '<|tool▁output▁end|>'}}{%- endif %}{%- endif %}{%- endfor -%}{% if ns.is_tool %}{{'<|tool▁outputs▁end|>'}}{% endif %}{% if add_generation_prompt and not ns.is_tool %}{{'<|Assistant|>\\\\n'}}{% endif %}\",\n },\n )\n assert model in litellm.known_tokenizer_config\n from litellm.llms.custom_httpx.http_handler import HTTPHandler\n\n client = HTTPHandler()\n\n messages = [\n {\"role\": \"system\", \"content\": \"You are a good assistant\"},\n {\"role\": \"user\", \"content\": \"What is the weather in Copenhagen?\"},\n ]\n\n with patch.object(client, \"post\") as mock_post:\n try:\n completion(\n model=\"bedrock/\" + model,\n messages=messages,\n client=client,\n )\n except Exception as e:\n pass\n\n mock_post.assert_called_once()\n print(mock_post.call_args.kwargs)\n json_data = json.loads(mock_post.call_args.kwargs[\"data\"])\n assert (\n json_data[\"prompt\"].rstrip()\n == \"\"\"<|begin▁of▁sentence|>You are a good assistant<|User|>What is the weather in Copenhagen?<|Assistant|>\"\"\"\n )\n\n\ndef test_bedrock_deepseek_known_tokenizer_config(monkeypatch):\n model = (\n \"deepseek_r1/arn:aws:bedrock:us-west-2:888602223428:imported-model/bnnr6463ejgf\"\n )\n from litellm.llms.custom_httpx.http_handler import HTTPHandler\n from unittest.mock import Mock\n import httpx\n\n monkeypatch.setenv(\"AWS_REGION\", \"us-east-1\")\n\n mock_response = Mock(spec=httpx.Response)\n mock_response.status_code = 200\n mock_response.headers = {\n \"x-amzn-bedrock-input-token-count\": \"20\",\n \"x-amzn-bedrock-output-token-count\": \"30\",\n }\n\n # The response format for deepseek_r1\n response_data = {\n \"generation\": \"The weather in Copenhagen is currently sunny with a temperature of 20°C (68°F). The forecast shows clear skies throughout the day with a gentle breeze from the northwest.\",\n \"stop_reason\": \"stop\",\n \"stop_sequence\": None,\n }\n\n mock_response.json.return_value = response_data\n mock_response.text = json.dumps(response_data)\n\n client = HTTPHandler()\n\n messages = [\n {\"role\": \"system\", \"content\": \"You are a good assistant\"},\n {\"role\": \"user\", \"content\": \"What is the weather in Copenhagen?\"},\n ]\n\n with patch.object(client, \"post\", return_value=mock_response) as mock_post:\n completion(\n model=\"bedrock/\" + model,\n messages=messages,\n client=client,\n )\n\n mock_post.assert_called_once()\n print(mock_post.call_args.kwargs)\n url = mock_post.call_args.kwargs[\"url\"]\n assert \"deepseek_r1\" not in url\n assert \"us-east-1\" not in url\n assert \"us-west-2\" in url\n json_data = json.loads(mock_post.call_args.kwargs[\"data\"])\n assert (\n json_data[\"prompt\"].rstrip()\n == \"\"\"<|begin▁of▁sentence|>You are a good assistant<|User|>What is the weather in Copenhagen?<|Assistant|>\"\"\"\n )\n\n\n# test_replicate_custom_prompt_dict()\n\n# commenthing this out since we won't be always testing a custom, replicate deployment\n# def test_completion_replicate_deployments():\n# print(\"TESTING REPLICATE\")\n# litellm.set_verbose=False\n# model_name = \"replicate/deployments/ishaan-jaff/ishaan-mistral\"\n# try:\n# response = completion(\n# model=model_name,\n# messages=messages,\n# temperature=0.5,\n# seed=-1,\n# )\n# print(response)\n# # Add any assertions here to check the response\n# response_str = response[\"choices\"][0][\"message\"][\"content\"]\n# print(\"RESPONSE STRING\\n\", response_str)\n# if type(response_str) != str:\n# pytest.fail(f\"Error occurred: {e}\")\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n# test_completion_replicate_deployments()\n\n\n######## Test TogetherAI ########\n@pytest.mark.skip(reason=\"Skip flaky test\")\ndef test_completion_together_ai_mixtral():\n model_name = \"together_ai/DiscoResearch/DiscoLM-mixtral-8x7b-v2\"\n try:\n messages = [\n {\"role\": \"user\", \"content\": \"Who are you\"},\n {\"role\": \"assistant\", \"content\": \"I am your helpful assistant.\"},\n {\"role\": \"user\", \"content\": \"Tell me a joke\"},\n ]\n response = completion(\n model=model_name,\n messages=messages,\n max_tokens=256,\n n=1,\n logger_fn=logger_fn,\n )\n # Add any assertions here to check the response\n print(response)\n cost = completion_cost(completion_response=response)\n assert cost > 0.0\n print(\n \"Cost for completion call together-computer/llama-2-70b: \",\n f\"${float(cost):.10f}\",\n )\n except litellm.Timeout as e:\n pass\n except litellm.ServiceUnavailableError as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_together_ai_mixtral()\n\n\ndef test_completion_together_ai_llama():\n litellm.set_verbose = True\n model_name = \"together_ai/meta-llama/Llama-3.3-70B-Instruct-Turbo\"\n try:\n messages = [\n {\"role\": \"user\", \"content\": \"What llm are you?\"},\n ]\n response = completion(model=model_name, messages=messages, max_tokens=5)\n # Add any assertions here to check the response\n print(response)\n cost = completion_cost(completion_response=response)\n assert cost > 0.0\n print(\n \"Cost for completion call together-computer/llama-2-70b: \",\n f\"${float(cost):.10f}\",\n )\n except litellm.Timeout as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_together_ai_yi_chat()\n\n\n# test_completion_together_ai()\ndef test_customprompt_together_ai():\n try:\n litellm.set_verbose = False\n litellm.num_retries = 0\n print(\"in test_customprompt_together_ai\")\n print(litellm.success_callback)\n print(litellm._async_success_callback)\n response = completion(\n model=\"together_ai/Qwen/Qwen2.5-7B-Instruct-Turbo\",\n messages=messages,\n roles={\n \"system\": {\n \"pre_message\": \"<|im_start|>system\\n\",\n \"post_message\": \"<|im_end|>\",\n },\n \"assistant\": {\n \"pre_message\": \"<|im_start|>assistant\\n\",\n \"post_message\": \"<|im_end|>\",\n },\n \"user\": {\n \"pre_message\": \"<|im_start|>user\\n\",\n \"post_message\": \"<|im_end|>\",\n },\n },\n )\n print(response)\n except litellm.exceptions.Timeout as e:\n print(f\"Timeout Error\")\n pass\n except Exception as e:\n print(f\"ERROR TYPE {type(e)}\")\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_customprompt_together_ai()\n\n\ndef response_format_tests(response: litellm.ModelResponse):\n assert isinstance(response.id, str)\n assert response.id != \"\"\n\n assert isinstance(response.object, str)\n assert response.object != \"\"\n\n assert isinstance(response.created, int)\n\n assert isinstance(response.model, str)\n assert response.model != \"\"\n\n assert isinstance(response.choices, list)\n assert len(response.choices) == 1\n choice = response.choices[0]\n assert isinstance(choice, litellm.Choices)\n assert isinstance(choice.get(\"index\"), int)\n\n message = choice.get(\"message\")\n assert isinstance(message, litellm.Message)\n assert isinstance(message.get(\"role\"), str)\n assert message.get(\"role\") != \"\"\n assert isinstance(message.get(\"content\"), str)\n assert message.get(\"content\") != \"\"\n\n assert choice.get(\"logprobs\") is None\n assert isinstance(choice.get(\"finish_reason\"), str)\n assert choice.get(\"finish_reason\") != \"\"\n\n assert isinstance(response.usage, litellm.Usage) # type: ignore\n assert isinstance(response.usage.prompt_tokens, int) # type: ignore\n assert isinstance(response.usage.completion_tokens, int) # type: ignore\n assert isinstance(response.usage.total_tokens, int) # type: ignore\n\n\n@pytest.mark.parametrize(\n \"model\",\n [\n \"bedrock/mistral.mistral-large-2407-v1:0\",\n \"bedrock/cohere.command-r-plus-v1:0\",\n \"anthropic.claude-3-sonnet-20240229-v1:0\",\n \"mistral.mistral-7b-instruct-v0:2\",\n \"meta.llama3-8b-instruct-v1:0\",\n ],\n)\n@pytest.mark.parametrize(\"sync_mode\", [True, False])\n@pytest.mark.asyncio\nasync def test_completion_bedrock_httpx_models(sync_mode, model):\n litellm.set_verbose = True\n try:\n\n if sync_mode:\n response = completion(\n model=model,\n messages=[{\"role\": \"user\", \"content\": \"Hey! how's it going?\"}],\n temperature=0.2,\n max_tokens=200,\n )\n\n assert isinstance(response, litellm.ModelResponse)\n\n response_format_tests(response=response)\n else:\n response = await litellm.acompletion(\n model=model,\n messages=[{\"role\": \"user\", \"content\": \"Hey! how's it going?\"}],\n temperature=0.2,\n max_tokens=100,\n )\n\n assert isinstance(response, litellm.ModelResponse)\n\n print(f\"response: {response}\")\n response_format_tests(response=response)\n\n print(f\"response: {response}\")\n except litellm.RateLimitError as e:\n print(\"got rate limit error=\", e)\n pass\n except Exception as e:\n pytest.fail(f\"An error occurred - {str(e)}\")\n\n\n# test_completion_bedrock_titan()\n\n\n# test_completion_bedrock_claude()\n\n\n# def test_completion_bedrock_claude_stream():\n# print(\"calling claude\")\n# litellm.set_verbose = False\n# try:\n# response = completion(\n# model=\"bedrock/anthropic.claude-instant-v1\",\n# messages=messages,\n# stream=True\n# )\n# # Add any assertions here to check the response\n# print(response)\n# for chunk in response:\n# print(chunk)\n# except RateLimitError:\n# pass\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n# test_completion_bedrock_claude_stream()\n\n\n######## Test VLLM ########\n# def test_completion_vllm():\n# try:\n# response = completion(\n# model=\"vllm/facebook/opt-125m\",\n# messages=messages,\n# temperature=0.2,\n# max_tokens=80,\n# )\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_completion_vllm()\n\n# def test_completion_hosted_chatCompletion():\n# # this tests calling a server where vllm is hosted\n# # this should make an openai.Completion() call to the specified api_base\n# # send a request to this proxy server: https://replit.com/@BerriAI/openai-proxy#main.py\n# # it checks if model == facebook/opt-125m and returns test passed\n# try:\n# litellm.set_verbose = True\n# response = completion(\n# model=\"facebook/opt-125m\",\n# messages=messages,\n# temperature=0.2,\n# max_tokens=80,\n# api_base=\"https://openai-proxy.berriai.repl.co\",\n# custom_llm_provider=\"openai\"\n# )\n# print(response)\n\n# if response['choices'][0]['message']['content'] != \"passed\":\n# # see https://replit.com/@BerriAI/openai-proxy#main.py\n# pytest.fail(f\"Error occurred: proxy server did not respond\")\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_completion_hosted_chatCompletion()\n\n# def test_completion_custom_api_base():\n# try:\n# response = completion(\n# model=\"custom/meta-llama/Llama-2-13b-hf\",\n# messages=messages,\n# temperature=0.2,\n# max_tokens=10,\n# api_base=\"https://api.autoai.dev/inference\",\n# request_timeout=300,\n# )\n# # Add any assertions here to check the response\n# print(\"got response\\n\", response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_completion_custom_api_base()\n\n\ndef test_completion_with_fallbacks():\n print(f\"RUNNING TEST COMPLETION WITH FALLBACKS - test_completion_with_fallbacks\")\n fallbacks = [\"gpt-3.5-turbo\", \"gpt-3.5-turbo\", \"command-nightly\"]\n try:\n response = completion(\n model=\"bad-model\", messages=messages, force_timeout=120, fallbacks=fallbacks\n )\n # Add any assertions here to check the response\n print(response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_with_fallbacks()\n\n\n# @pytest.mark.parametrize(\n# \"function_call\",\n# [\n# [{\"role\": \"function\", \"name\": \"get_capital\", \"content\": \"Kokoko\"}],\n# [\n# {\"role\": \"function\", \"name\": \"get_capital\", \"content\": \"Kokoko\"},\n# {\"role\": \"function\", \"name\": \"get_capital\", \"content\": \"Kokoko\"},\n# ],\n# ],\n# )\n# @pytest.mark.parametrize(\n# \"tool_call\",\n# [\n# [{\"role\": \"tool\", \"tool_call_id\": \"1234\", \"content\": \"Kokoko\"}],\n# [\n# {\"role\": \"tool\", \"tool_call_id\": \"12344\", \"content\": \"Kokoko\"},\n# {\"role\": \"tool\", \"tool_call_id\": \"1214\", \"content\": \"Kokoko\"},\n# ],\n# ],\n# )\ndef test_completion_anthropic_hanging():\n litellm.set_verbose = True\n litellm.modify_params = True\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"What's the capital of fictional country Ubabababababaaba? Use your tools.\",\n },\n {\n \"role\": \"assistant\",\n \"function_call\": {\n \"name\": \"get_capital\",\n \"arguments\": '{\"country\": \"Ubabababababaaba\"}',\n },\n },\n {\"role\": \"function\", \"name\": \"get_capital\", \"content\": \"Kokoko\"},\n ]\n\n converted_messages = anthropic_messages_pt(\n messages, model=\"claude-3-sonnet-20240229\", llm_provider=\"anthropic\"\n )\n\n print(f\"converted_messages: {converted_messages}\")\n\n ## ENSURE USER / ASSISTANT ALTERNATING\n for i, msg in enumerate(converted_messages):\n if i < len(converted_messages) - 1:\n assert msg[\"role\"] != converted_messages[i + 1][\"role\"]\n\n\n@pytest.mark.skip(reason=\"anyscale stopped serving public api endpoints\")\ndef test_completion_anyscale_api():\n try:\n # litellm.set_verbose = True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n ]\n response = completion(\n model=\"anyscale/meta-llama/Llama-2-7b-chat-hf\",\n messages=messages,\n )\n print(response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"anyscale stopped serving public api endpoints\")\ndef test_completion_anyscale_2():\n try:\n # litellm.set_verbose = True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n ]\n response = completion(\n model=\"anyscale/meta-llama/Llama-2-7b-chat-hf\", messages=messages\n )\n print(response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"anyscale stopped serving public api endpoints\")\ndef test_mistral_anyscale_stream():\n litellm.set_verbose = False\n response = completion(\n model=\"anyscale/mistralai/Mistral-7B-Instruct-v0.1\",\n messages=[{\"content\": \"hello, good morning\", \"role\": \"user\"}],\n stream=True,\n )\n for chunk in response:\n # print(chunk)\n print(chunk[\"choices\"][0][\"delta\"].get(\"content\", \"\"), end=\"\")\n\n\n# test_completion_with_fallbacks_multiple_keys()\ndef test_petals():\n try:\n from litellm.llms.custom_httpx.http_handler import HTTPHandler\n\n client = HTTPHandler()\n with patch.object(client, \"post\") as mock_post:\n try:\n completion(\n model=\"petals-team/StableBeluga2\",\n messages=messages,\n client=client,\n api_base=\"https://api.petals.dev\",\n )\n except Exception as e:\n print(f\"Error occurred: {e}\")\n mock_post.assert_called_once()\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# def test_baseten():\n# try:\n\n# response = completion(model=\"baseten/7qQNLDB\", messages=messages, logger_fn=logger_fn)\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_baseten()\n# def test_baseten_falcon_7bcompletion():\n# model_name = \"qvv0xeq\"\n# try:\n# response = completion(model=model_name, messages=messages, custom_llm_provider=\"baseten\")\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# def test_baseten_falcon_7bcompletion_withbase():\n# model_name = \"qvv0xeq\"\n# litellm.api_base = \"https://app.baseten.co\"\n# try:\n# response = completion(model=model_name, messages=messages)\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n# litellm.api_base = None\n\n# test_baseten_falcon_7bcompletion_withbase()\n\n\n# def test_baseten_wizardLMcompletion_withbase():\n# model_name = \"q841o8w\"\n# litellm.api_base = \"https://app.baseten.co\"\n# try:\n# response = completion(model=model_name, messages=messages)\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n# test_baseten_wizardLMcompletion_withbase()\n\n# def test_baseten_mosaic_ML_completion_withbase():\n# model_name = \"31dxrj3\",\n# litellm.api_base = \"https://app.baseten.co\"\n# try:\n# response = completion(model=model_name, messages=messages)\n# # Add any assertions here to check the response\n# print(response)\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_ai21()\n# test_completion_ai21()\n## test deep infra\n@pytest.mark.parametrize(\"drop_params\", [True, False])\ndef test_completion_deep_infra(drop_params):\n \"\"\"Test that DeepInfra requests are shaped correctly without making real API calls.\"\"\"\n from unittest.mock import MagicMock, patch\n from openai import OpenAI\n from openai.types.chat import ChatCompletion, ChatCompletionMessage\n from openai.types.chat.chat_completion import Choice\n import httpx\n\n litellm.set_verbose = False\n model_name = \"deepinfra/meta-llama/Llama-2-70b-chat-hf\"\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_current_weather\",\n \"description\": \"Get the current weather in a given location\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n },\n \"unit\": {\"type\": \"string\", \"enum\": [\"celsius\", \"fahrenheit\"]},\n },\n \"required\": [\"location\"],\n },\n },\n }\n ]\n messages = [\n {\n \"role\": \"user\",\n \"content\": \"What's the weather like in Boston today in Fahrenheit?\",\n }\n ]\n\n mock_response = ChatCompletion(\n id=\"chatcmpl-mock\",\n choices=[\n Choice(\n finish_reason=\"stop\",\n index=0,\n message=ChatCompletionMessage(content=\"It's sunny.\", role=\"assistant\"),\n )\n ],\n created=1234567890,\n model=\"meta-llama/Llama-2-70b-chat-hf\",\n object=\"chat.completion\",\n usage={\"completion_tokens\": 5, \"prompt_tokens\": 20, \"total_tokens\": 25},\n )\n\n mock_raw = MagicMock()\n mock_raw.parse.return_value = mock_response\n mock_raw.headers = httpx.Headers({\"content-type\": \"application/json\"})\n mock_raw.status_code = 200\n\n with patch(\n \"litellm.llms.openai.openai.OpenAIChatCompletion.make_sync_openai_chat_completion_request\",\n return_value=(mock_raw, mock_response),\n ) as mock_create:\n if drop_params is False:\n # DeepInfra doesn't support tool_choice, should raise UnsupportedParamsError\n with pytest.raises(litellm.exceptions.UnsupportedParamsError):\n completion(\n model=model_name,\n messages=messages,\n temperature=0,\n max_tokens=10,\n tools=tools,\n tool_choice={\n \"type\": \"function\",\n \"function\": {\"name\": \"get_current_weather\"},\n },\n drop_params=drop_params,\n api_key=\"fake-api-key\",\n )\n return\n\n response = completion(\n model=model_name,\n messages=messages,\n temperature=0,\n max_tokens=10,\n tools=tools,\n tool_choice={\n \"type\": \"function\",\n \"function\": {\"name\": \"get_current_weather\"},\n },\n drop_params=drop_params,\n api_key=\"fake-api-key\",\n )\n\n # Verify the call was made\n mock_create.assert_called_once()\n call_kwargs = mock_create.call_args.kwargs\n\n # Verify request shape\n data = call_kwargs[\"data\"]\n assert data[\"model\"] == \"meta-llama/Llama-2-70b-chat-hf\"\n assert data[\"messages\"] == messages\n assert data[\"temperature\"] == 0\n assert data[\"max_tokens\"] == 10\n # tool_choice should be dropped for unsupported params\n assert \"tool_choice\" not in data\n\n\n# test_completion_deep_infra()\n\n\ndef test_completion_deep_infra_mistral():\n \"\"\"Test that DeepInfra Mistral requests are shaped correctly without making real API calls.\"\"\"\n from unittest.mock import MagicMock, patch\n from openai.types.chat import ChatCompletion, ChatCompletionMessage\n from openai.types.chat.chat_completion import Choice\n import httpx\n\n model_name = \"deepinfra/mistralai/Mistral-7B-Instruct-v0.1\"\n\n mock_response = ChatCompletion(\n id=\"chatcmpl-mock\",\n choices=[\n Choice(\n finish_reason=\"stop\",\n index=0,\n message=ChatCompletionMessage(content=\"Hello!\", role=\"assistant\"),\n )\n ],\n created=1234567890,\n model=\"mistralai/Mistral-7B-Instruct-v0.1\",\n object=\"chat.completion\",\n usage={\"completion_tokens\": 5, \"prompt_tokens\": 20, \"total_tokens\": 25},\n )\n\n mock_raw = MagicMock()\n mock_raw.parse.return_value = mock_response\n mock_raw.headers = httpx.Headers({\"content-type\": \"application/json\"})\n mock_raw.status_code = 200\n\n with patch(\n \"litellm.llms.openai.openai.OpenAIChatCompletion.make_sync_openai_chat_completion_request\",\n return_value=(mock_raw, mock_response),\n ) as mock_create:\n response = completion(\n model=model_name,\n messages=messages,\n temperature=0.01,\n max_tokens=10,\n api_key=\"fake-api-key\",\n )\n\n mock_create.assert_called_once()\n call_kwargs = mock_create.call_args.kwargs\n data = call_kwargs[\"data\"]\n assert data[\"model\"] == \"mistralai/Mistral-7B-Instruct-v0.1\"\n assert data[\"temperature\"] == 0.01\n assert data[\"max_tokens\"] == 10\n\n\n# test_completion_deep_infra_mistral()\n\n\n@pytest.mark.skip(reason=\"Local test - don't have a volcengine account as yet\")\ndef test_completion_volcengine():\n litellm.set_verbose = True\n model_name = \"volcengine/\"\n try:\n response = completion(\n model=model_name,\n messages=[\n {\n \"role\": \"user\",\n \"content\": \"What's the weather like in Boston today in Fahrenheit?\",\n }\n ],\n api_key=\"\",\n )\n # Add any assertions here to check the response\n print(response)\n\n except litellm.exceptions.Timeout as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# Gemini tests\n@pytest.mark.parametrize(\n \"model\",\n [\n # \"gemini-1.0-pro\",\n \"gemini-2.5-flash-lite\",\n ],\n)\n@pytest.mark.flaky(retries=3, delay=1)\ndef test_completion_gemini(model):\n litellm.set_verbose = True\n model_name = \"gemini/{}\".format(model)\n messages = [\n {\"role\": \"system\", \"content\": \"Be a good bot!\"},\n {\"role\": \"user\", \"content\": \"Hey, how's it going?\"},\n ]\n try:\n response = completion(\n model=model_name,\n messages=messages,\n safety_settings=[\n {\n \"category\": \"HARM_CATEGORY_HARASSMENT\",\n \"threshold\": \"BLOCK_NONE\",\n },\n {\n \"category\": \"HARM_CATEGORY_HATE_SPEECH\",\n \"threshold\": \"BLOCK_NONE\",\n },\n {\n \"category\": \"HARM_CATEGORY_SEXUALLY_EXPLICIT\",\n \"threshold\": \"BLOCK_NONE\",\n },\n {\n \"category\": \"HARM_CATEGORY_DANGEROUS_CONTENT\",\n \"threshold\": \"BLOCK_NONE\",\n },\n ],\n )\n # Add any assertions,here to check the response\n print(response)\n assert response.choices[0][\"index\"] == 0\n except litellm.RateLimitError:\n pass\n except litellm.APIError:\n pass\n except Exception as e:\n if \"InternalServerError\" in str(e):\n pass\n else:\n pytest.fail(f\"Error occurred:{e}\")\n\n\n# test_completion_gemini()\n\n\n@pytest.mark.asyncio\nasync def test_acompletion_gemini():\n litellm.set_verbose = True\n model_name = \"gemini/gemini-2.5-flash-lite\"\n messages = [{\"role\": \"user\", \"content\": \"Hey, how's it going?\"}]\n try:\n response = await litellm.acompletion(model=model_name, messages=messages)\n # Add any assertions here to check the response\n print(f\"response: {response}\")\n except litellm.Timeout as e:\n pass\n except litellm.APIError as e:\n pass\n except Exception as e:\n if \"InternalServerError\" in str(e):\n pass\n else:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# Deepseek tests\ndef test_completion_deepseek():\n litellm.set_verbose = True\n model_name = \"deepseek/deepseek-chat\"\n tools = [\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"get_weather\",\n \"description\": \"Get weather of an location, the user shoud supply a location first\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"location\": {\n \"type\": \"string\",\n \"description\": \"The city and state, e.g. San Francisco, CA\",\n }\n },\n \"required\": [\"location\"],\n },\n },\n },\n ]\n messages = [{\"role\": \"user\", \"content\": \"How's the weather in Hangzhou?\"}]\n try:\n response = completion(model=model_name, messages=messages, tools=tools)\n # Add any assertions here to check the response\n print(response)\n except litellm.APIError as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"Account deleted by IBM.\")\ndef test_completion_watsonx_error():\n litellm.set_verbose = True\n model_name = \"watsonx_text/ibm/granite-13b-chat-v2\"\n\n response = completion(\n model=model_name,\n messages=messages,\n stop=[\"stop\"],\n max_tokens=20,\n stream=True,\n )\n\n for chunk in response:\n print(chunk)\n # Add any assertions here to check the response\n print(response)\n\n\n@pytest.mark.skip(reason=\"Skip test. account deleted.\")\ndef test_completion_stream_watsonx():\n litellm.set_verbose = True\n model_name = \"watsonx/ibm/granite-13b-chat-v2\"\n try:\n response = completion(\n model=model_name,\n messages=messages,\n stop=[\"stop\"],\n max_tokens=20,\n stream=True,\n )\n for chunk in response:\n print(chunk)\n except litellm.APIError as e:\n pass\n except litellm.RateLimitError as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.parametrize(\n \"provider, model, project, region_name, token\",\n [\n (\"azure\", \"chatgpt-v-3\", None, None, \"test-token\"),\n (\"vertex_ai\", \"anthropic-claude-3\", \"adroit-crow-1\", \"us-east1\", None),\n (\"watsonx\", \"ibm/granite\", \"96946574\", \"dallas\", \"1234\"),\n (\"bedrock\", \"anthropic.claude-3\", None, \"us-east-1\", None),\n ],\n)\ndef test_unified_auth_params(provider, model, project, region_name, token):\n \"\"\"\n Check if params = [\"project\", \"region_name\", \"token\"]\n are correctly translated for = [\"azure\", \"vertex_ai\", \"watsonx\", \"aws\"]\n\n tests get_optional_params\n \"\"\"\n data = {\n \"project\": project,\n \"region_name\": region_name,\n \"token\": token,\n \"custom_llm_provider\": provider,\n \"model\": model,\n }\n\n translated_optional_params = litellm.utils.get_optional_params(**data)\n\n if provider == \"azure\":\n special_auth_params = (\n litellm.AzureOpenAIConfig().get_mapped_special_auth_params()\n )\n elif provider == \"bedrock\":\n special_auth_params = (\n litellm.AmazonBedrockGlobalConfig().get_mapped_special_auth_params()\n )\n elif provider == \"vertex_ai\":\n special_auth_params = litellm.VertexAIConfig().get_mapped_special_auth_params()\n elif provider == \"watsonx\":\n special_auth_params = (\n litellm.IBMWatsonXAIConfig().get_mapped_special_auth_params()\n )\n\n for param, value in special_auth_params.items():\n assert param in data\n assert value in translated_optional_params\n\n\n@pytest.mark.skip(reason=\"Local test\")\n@pytest.mark.asyncio\nasync def test_acompletion_watsonx():\n litellm.set_verbose = True\n model_name = \"watsonx/ibm/granite-13b-chat-v2\"\n print(\"testing watsonx\")\n try:\n response = await litellm.acompletion(\n model=model_name,\n messages=messages,\n temperature=0.2,\n max_tokens=80,\n )\n # Add any assertions here to check the response\n print(response)\n except litellm.RateLimitError as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.skip(reason=\"Local test\")\n@pytest.mark.asyncio\nasync def test_acompletion_stream_watsonx():\n litellm.set_verbose = True\n model_name = \"watsonx/ibm/granite-13b-chat-v2\"\n print(\"testing watsonx\")\n try:\n response = await litellm.acompletion(\n model=model_name,\n messages=messages,\n temperature=0.2,\n max_tokens=80,\n stream=True,\n )\n # Add any assertions here to check the response\n async for chunk in response:\n print(chunk)\n except litellm.RateLimitError as e:\n pass\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_palm_stream()\n\n# test_completion_deep_infra()\n# test_completion_ai21()\n# test config file with completion #\n# def test_completion_openai_config():\n# try:\n# litellm.config_path = \"../config.json\"\n# litellm.set_verbose = True\n# response = litellm.config_completion(messages=messages)\n# # Add any assertions here to check the response\n# print(response)\n# litellm.config_path = None\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n\n\n# def test_maritalk():\n# messages = [{\"role\": \"user\", \"content\": \"Hey\"}]\n# try:\n# response = completion(\"maritalk\", messages=messages)\n# print(f\"response: {response}\")\n# except Exception as e:\n# pytest.fail(f\"Error occurred: {e}\")\n# test_maritalk()\n\n\ndef test_completion_together_ai_stream():\n litellm.set_verbose = True\n user_message = \"Write 1pg about YC & litellm\"\n messages = [{\"content\": user_message, \"role\": \"user\"}]\n try:\n response = completion(\n model=\"together_ai/Qwen/Qwen2.5-7B-Instruct-Turbo\",\n messages=messages,\n stream=True,\n max_tokens=5,\n )\n print(response)\n for chunk in response:\n print(chunk)\n # print(string_response)\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n# test_completion_together_ai_stream()\n\n\ndef test_moderation():\n response = litellm.moderation(input=\"i'm ishaan cto of litellm\")\n print(response)\n output = response.results[0]\n print(output)\n return output\n\n\n@pytest.mark.parametrize(\"stream\", [False, True])\n@pytest.mark.parametrize(\"sync_mode\", [False, True])\n@pytest.mark.asyncio\nasync def test_dynamic_azure_params(stream, sync_mode):\n \"\"\"\n If dynamic params are given, which are different from the initialized client, use a new client\n \"\"\"\n from openai import AsyncAzureOpenAI, AzureOpenAI\n\n if sync_mode:\n client = AzureOpenAI(\n api_key=\"my-test-key\",\n base_url=\"my-test-base\",\n api_version=\"my-test-version\",\n )\n mock_client = MagicMock(return_value=\"Hello world!\")\n else:\n client = AsyncAzureOpenAI(\n api_key=\"my-test-key\",\n base_url=\"my-test-base\",\n api_version=\"my-test-version\",\n )\n mock_client = AsyncMock(return_value=\"Hello world!\")\n\n ## CHECK IF CLIENT IS USED (NO PARAM CHANGE)\n with patch.object(\n client.chat.completions.with_raw_response, \"create\", new=mock_client\n ) as mock_client:\n try:\n # client.chat.completions.with_raw_response.create = mock_client\n if sync_mode:\n _ = completion(\n model=\"azure/chatgpt-v2\",\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n client=client,\n stream=stream,\n )\n else:\n _ = await litellm.acompletion(\n model=\"azure/chatgpt-v2\",\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n client=client,\n stream=stream,\n )\n except Exception:\n pass\n\n mock_client.assert_called()\n\n ## recreate mock client\n if sync_mode:\n new_mock_client = MagicMock(return_value=\"Hello world!\")\n else:\n new_mock_client = AsyncMock(return_value=\"Hello world!\")\n\n ## CHECK IF NEW CLIENT IS USED (PARAM CHANGE)\n with patch.object(\n client.chat.completions.with_raw_response, \"create\", new=new_mock_client\n ) as new_mock_client:\n try:\n if sync_mode:\n _ = completion(\n model=\"azure/chatgpt-v2\",\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n client=client,\n api_version=\"my-new-version\",\n stream=stream,\n )\n else:\n _ = await litellm.acompletion(\n model=\"azure/chatgpt-v2\",\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n client=client,\n api_version=\"my-new-version\",\n stream=stream,\n )\n except Exception:\n pass\n\n try:\n new_mock_client.assert_called()\n except Exception as e:\n raise e\n\n\n@pytest.mark.parametrize(\n \"model\",\n [\"gpt-4o\", \"azure/gpt-4.1-mini\"],\n)\n@pytest.mark.parametrize(\n \"stream\",\n [False, True],\n)\n@pytest.mark.flaky(retries=3, delay=1)\ndef test_completion_response_ratelimit_headers(model, stream):\n response = completion(\n model=model,\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n stream=stream,\n )\n hidden_params = response._hidden_params\n additional_headers = hidden_params.get(\"additional_headers\", {})\n\n print(additional_headers)\n for k, v in additional_headers.items():\n assert v != \"None\" and v is not None\n assert \"x-ratelimit-remaining-requests\" in additional_headers\n assert \"x-ratelimit-remaining-tokens\" in additional_headers\n\n if model == \"azure/gpt-4.1-mini\":\n # Azure OpenAI header\n assert \"llm_provider-azureml-model-session\" in additional_headers\n if model == \"claude-3-sonnet-20240229\":\n # anthropic header\n assert \"llm_provider-anthropic-ratelimit-requests-reset\" in additional_headers\n\n\ndef _openai_hallucinated_tool_call_mock_response(\n *args, **kwargs\n) -> litellm.ModelResponse:\n new_response = MagicMock()\n new_response.headers = {\"hello\": \"world\"}\n\n response_object = {\n \"id\": \"chatcmpl-123\",\n \"object\": \"chat.completion\",\n \"created\": 1677652288,\n \"model\": \"gpt-3.5-turbo-0125\",\n \"system_fingerprint\": \"fp_44709d6fcb\",\n \"choices\": [\n {\n \"index\": 0,\n \"message\": {\n \"content\": None,\n \"role\": \"assistant\",\n \"tool_calls\": [\n {\n \"function\": {\n \"arguments\": '{\"tool_uses\":[{\"recipient_name\":\"product_title\",\"parameters\":{\"content\":\"Story Scribe\"}},{\"recipient_name\":\"one_liner\",\"parameters\":{\"content\":\"Transform interview transcripts into actionable user stories\"}}]}',\n \"name\": \"multi_tool_use.parallel\",\n },\n \"id\": \"call_IzGXwVa5OfBd9XcCJOkt2q0s\",\n \"type\": \"function\",\n }\n ],\n },\n \"logprobs\": None,\n \"finish_reason\": \"stop\",\n }\n ],\n \"usage\": {\"prompt_tokens\": 9, \"completion_tokens\": 12, \"total_tokens\": 21},\n }\n from openai import OpenAI\n from openai.types.chat.chat_completion import ChatCompletion\n\n pydantic_obj = ChatCompletion(**response_object) # type: ignore\n pydantic_obj.choices[0].message.role = None # type: ignore\n new_response.parse.return_value = pydantic_obj\n return new_response\n\n\ndef test_openai_hallucinated_tool_call():\n \"\"\"\n Patch for this issue: https://community.openai.com/t/model-tries-to-call-unknown-function-multi-tool-use-parallel/490653\n\n Handle openai invalid tool calling response.\n\n OpenAI assistant will sometimes return an invalid tool calling response, which needs to be parsed\n\n - \"arguments\": \"{\\\"tool_uses\\\":[{\\\"recipient_name\\\":\\\"product_title\\\",\\\"parameters\\\":{\\\"content\\\":\\\"Story Scribe\\\"}},{\\\"recipient_name\\\":\\\"one_liner\\\",\\\"parameters\\\":{\\\"content\\\":\\\"Transform interview transcripts into actionable user stories\\\"}}]}\",\n\n To extract actual tool calls:\n\n 1. Parse arguments JSON object\n 2. Iterate over tool_uses array to call functions:\n - get function name from recipient_name value\n - parameters will be JSON object for function arguments\n \"\"\"\n import openai\n\n openai_client = openai.OpenAI()\n with patch.object(\n openai_client.chat.completions,\n \"create\",\n side_effect=_openai_hallucinated_tool_call_mock_response,\n ) as mock_response:\n response = litellm.completion(\n model=\"gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"Hey! how's it going?\"}],\n client=openai_client,\n )\n print(f\"response: {response}\")\n\n response_dict = response.model_dump()\n\n tool_calls = response_dict[\"choices\"][0][\"message\"][\"tool_calls\"]\n\n print(f\"tool_calls: {tool_calls}\")\n\n for idx, tc in enumerate(tool_calls):\n if idx == 0:\n print(f\"tc in test_openai_hallucinated_tool_call: {tc}\")\n assert tc == {\n \"function\": {\n \"arguments\": '{\"content\": \"Story Scribe\"}',\n \"name\": \"product_title\",\n },\n \"id\": \"call_IzGXwVa5OfBd9XcCJOkt2q0s_0\",\n \"type\": \"function\",\n }\n elif idx == 1:\n assert tc == {\n \"function\": {\n \"arguments\": '{\"content\": \"Transform interview transcripts into actionable user stories\"}',\n \"name\": \"one_liner\",\n },\n \"id\": \"call_IzGXwVa5OfBd9XcCJOkt2q0s_1\",\n \"type\": \"function\",\n }\n\n\n@pytest.mark.parametrize(\n \"function_name, expect_modification\",\n [\n (\"multi_tool_use.parallel\", True),\n (\"my-fake-function\", False),\n ],\n)\ndef test_openai_hallucinated_tool_call_util(function_name, expect_modification):\n \"\"\"\n Patch for this issue: https://community.openai.com/t/model-tries-to-call-unknown-function-multi-tool-use-parallel/490653\n\n Handle openai invalid tool calling response.\n\n OpenAI assistant will sometimes return an invalid tool calling response, which needs to be parsed\n\n - \"arguments\": \"{\\\"tool_uses\\\":[{\\\"recipient_name\\\":\\\"product_title\\\",\\\"parameters\\\":{\\\"content\\\":\\\"Story Scribe\\\"}},{\\\"recipient_name\\\":\\\"one_liner\\\",\\\"parameters\\\":{\\\"content\\\":\\\"Transform interview transcripts into actionable user stories\\\"}}]}\",\n\n To extract actual tool calls:\n\n 1. Parse arguments JSON object\n 2. Iterate over tool_uses array to call functions:\n - get function name from recipient_name value\n - parameters will be JSON object for function arguments\n \"\"\"\n from litellm.utils import _handle_invalid_parallel_tool_calls\n from litellm.types.utils import ChatCompletionMessageToolCall\n\n response = _handle_invalid_parallel_tool_calls(\n tool_calls=[\n ChatCompletionMessageToolCall(\n **{\n \"function\": {\n \"arguments\": '{\"tool_uses\":[{\"recipient_name\":\"product_title\",\"parameters\":{\"content\":\"Story Scribe\"}},{\"recipient_name\":\"one_liner\",\"parameters\":{\"content\":\"Transform interview transcripts into actionable user stories\"}}]}',\n \"name\": function_name,\n },\n \"id\": \"call_IzGXwVa5OfBd9XcCJOkt2q0s\",\n \"type\": \"function\",\n }\n )\n ]\n )\n\n print(f\"response: {response}\")\n\n if expect_modification:\n for idx, tc in enumerate(response):\n if idx == 0:\n assert tc.model_dump() == {\n \"function\": {\n \"arguments\": '{\"content\": \"Story Scribe\"}',\n \"name\": \"product_title\",\n },\n \"id\": \"call_IzGXwVa5OfBd9XcCJOkt2q0s_0\",\n \"type\": \"function\",\n }\n elif idx == 1:\n assert tc.model_dump() == {\n \"function\": {\n \"arguments\": '{\"content\": \"Transform interview transcripts into actionable user stories\"}',\n \"name\": \"one_liner\",\n },\n \"id\": \"call_IzGXwVa5OfBd9XcCJOkt2q0s_1\",\n \"type\": \"function\",\n }\n else:\n assert len(response) == 1\n assert response[0].function.name == function_name\n\n\ndef test_langfuse_completion(monkeypatch):\n monkeypatch.setenv(\n \"LANGFUSE_PUBLIC_KEY\", \"pk-lf-b3db7e8e-c2f6-4fc7-825c-a541a8fbe003\"\n )\n monkeypatch.setenv(\n \"LANGFUSE_SECRET_KEY\", \"sk-lf-b11ef3a8-361c-4445-9652-12318b8596e4\"\n )\n monkeypatch.setenv(\"LANGFUSE_HOST\", \"https://us.cloud.langfuse.com\")\n litellm.set_verbose = True\n resp = litellm.completion(\n model=\"langfuse/gpt-3.5-turbo\",\n langfuse_public_key=os.getenv(\"LANGFUSE_PUBLIC_KEY\"),\n langfuse_secret_key=os.getenv(\"LANGFUSE_SECRET_KEY\"),\n langfuse_host=\"https://us.cloud.langfuse.com\",\n prompt_id=\"test-chat-prompt\",\n prompt_variables={\"user_message\": \"this is used\"},\n messages=[{\"role\": \"user\", \"content\": \"this is ignored\"}],\n )\n\n\ndef test_completion_novita_ai():\n litellm.set_verbose = True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n ]\n\n from openai import OpenAI\n\n openai_client = OpenAI(api_key=\"fake-key\")\n\n with patch.object(\n openai_client.chat.completions, \"create\", new=MagicMock()\n ) as mock_call:\n try:\n completion(\n model=\"novita/meta-llama/llama-3.3-70b-instruct\",\n messages=messages,\n client=openai_client,\n api_base=\"https://api.novita.ai/v3/openai\",\n )\n\n mock_call.assert_called_once()\n\n # Verify model is passed correctly\n assert (\n mock_call.call_args.kwargs[\"model\"]\n == \"meta-llama/llama-3.3-70b-instruct\"\n )\n # Verify messages are passed correctly\n assert mock_call.call_args.kwargs[\"messages\"] == messages\n\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\n@pytest.mark.parametrize(\"api_key\", [\"my-bad-api-key\"])\ndef test_completion_novita_ai_dynamic_params(api_key):\n try:\n litellm.set_verbose = True\n messages = [\n {\"role\": \"system\", \"content\": \"You're a good bot\"},\n {\n \"role\": \"user\",\n \"content\": \"Hey\",\n },\n ]\n\n from openai import OpenAI\n\n openai_client = OpenAI(api_key=\"fake-key\")\n\n with patch.object(\n openai_client.chat.completions,\n \"create\",\n side_effect=Exception(\"Invalid API key\"),\n ) as mock_call:\n try:\n completion(\n model=\"novita/meta-llama/llama-3.3-70b-instruct\",\n messages=messages,\n api_key=api_key,\n client=openai_client,\n api_base=\"https://api.novita.ai/v3/openai\",\n )\n pytest.fail(f\"This call should have failed!\")\n except Exception as e:\n # This should fail with the mocked exception\n assert \"Invalid API key\" in str(e)\n\n mock_call.assert_called_once()\n except Exception as e:\n pytest.fail(f\"Unexpected error: {e}\")\n\n\ndef test_deepseek_reasoning_content_completion():\n try:\n litellm.set_verbose = True\n litellm._turn_on_debug()\n resp = litellm.completion(\n timeout=5,\n model=\"deepseek/deepseek-reasoner\",\n messages=[{\"role\": \"user\", \"content\": \"Tell me a joke.\"}],\n )\n\n assert resp.choices[0].message.reasoning_content is not None\n except litellm.Timeout:\n pytest.skip(\"Model is timing out\")\n\n\ndef test_qwen_text_completion():\n # litellm._turn_on_debug()\n resp = litellm.completion(\n model=\"gpt-3.5-turbo-instruct\",\n messages=[{\"content\": \"hello\", \"role\": \"user\"}],\n stream=False,\n logprobs=1,\n )\n assert resp.choices[0].message.content is not None\n assert resp.choices[0].logprobs.token_logprobs[0] is not None\n print(\n f\"resp.choices[0].logprobs.token_logprobs[0]: {resp.choices[0].logprobs.token_logprobs[0]}\"\n )\n\n\n@pytest.mark.parametrize(\n \"enable_preview_features\",\n [True, False],\n)\ndef test_completion_openai_metadata(monkeypatch, enable_preview_features):\n from openai import OpenAI\n\n client = OpenAI()\n\n litellm.set_verbose = True\n\n monkeypatch.setattr(litellm, \"enable_preview_features\", enable_preview_features)\n with patch.object(\n client.chat.completions.with_raw_response, \"create\", return_value=MagicMock()\n ) as mock_completion:\n try:\n resp = litellm.completion(\n model=\"openai/gpt-3.5-turbo\",\n messages=[{\"role\": \"user\", \"content\": \"Hello world\"}],\n metadata={\"my-test-key\": \"my-test-value\"},\n client=client,\n )\n except Exception as e:\n print(f\"Error: {e}\")\n\n mock_completion.assert_called_once()\n if enable_preview_features:\n assert mock_completion.call_args.kwargs[\"metadata\"] == {\n \"my-test-key\": \"my-test-value\"\n }\n else:\n assert \"metadata\" not in mock_completion.call_args.kwargs\n\n\ndef test_completion_o3_mini_temperature():\n try:\n litellm.set_verbose = True\n resp = litellm.completion(\n model=\"o3-mini\",\n temperature=0.0,\n messages=[\n {\n \"role\": \"user\",\n \"content\": \"Hello, world!\",\n }\n ],\n drop_params=True,\n )\n assert resp.choices[0].message.content is not None\n except Exception as e:\n pytest.fail(f\"Error occurred: {e}\")\n\n\ndef test_completion_gpt_4o_empty_str():\n litellm._turn_on_debug()\n from openai import OpenAI\n from unittest.mock import MagicMock\n\n client = OpenAI()\n\n # Create response object matching OpenAI's format\n mock_response_data = {\n \"id\": \"chatcmpl-B0W3vmiM78Xkgx7kI7dr7PC949DMS\",\n \"choices\": [\n {\n \"finish_reason\": \"stop\",\n \"index\": 0,\n \"logprobs\": None,\n \"message\": {\n \"content\": \"\",\n \"refusal\": None,\n \"role\": \"assistant\",\n \"audio\": None,\n \"function_call\": None,\n \"tool_calls\": None,\n },\n }\n ],\n \"created\": 1739462947,\n \"model\": \"gpt-4o-mini-2024-07-18\",\n \"object\": \"chat.completion\",\n \"service_tier\": \"default\",\n \"system_fingerprint\": \"fp_bd83329f63\",\n \"usage\": {\n \"completion_tokens\": 1,\n \"prompt_tokens\": 121,\n \"total_tokens\": 122,\n \"completion_tokens_details\": {\n \"accepted_prediction_tokens\": 0,\n \"audio_tokens\": 0,\n \"reasoning_tokens\": 0,\n \"rejected_prediction_tokens\": 0,\n },\n \"prompt_tokens_details\": {\"audio_tokens\": 0, \"cached_tokens\": 0},\n },\n }\n\n # Create a mock response object\n mock_raw_response = MagicMock()\n mock_raw_response.headers = {\n \"x-request-id\": \"123\",\n \"openai-organization\": \"org-123\",\n \"x-ratelimit-limit-requests\": \"100\",\n \"x-ratelimit-remaining-requests\": \"99\",\n }\n mock_raw_response.parse.return_value = mock_response_data\n\n # Set up the mock completion\n mock_completion = MagicMock()\n mock_completion.return_value = mock_raw_response\n\n with patch.object(\n client.chat.completions.with_raw_response, \"create\", mock_completion\n ) as mock_create:\n resp = litellm.completion(\n model=\"gpt-4o-mini\",\n messages=[{\"role\": \"user\", \"content\": \"\"}],\n )\n assert resp.choices[0].message.content is not None\n\n\ndef test_edit_note():\n litellm.callbacks = [\"langfuse_otel\"]\n response = completion(\n model=\"gpt-4o\",\n messages=[\n {\n \"role\": \"system\",\n \"content\": \"Your only job is to call the edit_note tool with the content specified in the user's message.\",\n },\n {\n \"role\": \"user\",\n \"content\": \"Edit the note with the content: 'This is a test note.'\",\n },\n ],\n tools=[\n {\n \"type\": \"function\",\n \"function\": {\n \"name\": \"edit_note\",\n \"description\": \"Edit the note with the content specified in the user's message.\",\n \"parameters\": {\n \"type\": \"object\",\n \"properties\": {\n \"content\": {\"type\": \"string\"},\n },\n },\n },\n },\n ],\n )\n\n return response\n"}}}}