From 669566a05fe798a131a2139ace2ece8fc9408558 Mon Sep 17 00:00:00 2001 From: Ishaan Jaff Date: Tue, 29 Apr 2025 21:27:37 -0700 Subject: [PATCH] Contributor PR - Support OPENAI_BASE_URL in addition to OPENAI_API_BASE (#9995) (#10423) * Support OPENAI_BASE_URL in addition to OPENAI_API_BASE (#9995) * Support OPENAI_BASE_URL in addition to OPENAI_API_BASE Signed-off-by: Adrian Cole * exact Signed-off-by: Adrian Cole * feedback * less change Signed-off-by: Adrian Cole --------- Signed-off-by: Adrian Cole * doc fix OPENAI_API_BASE --------- Signed-off-by: Adrian Cole Co-authored-by: Adrian Cole <64215+codefromthecrypt@users.noreply.github.com> --- .env.example | 2 +- docs/my-website/docs/providers/openai.md | 8 ++-- docs/my-website/docs/proxy/config_settings.md | 1 + docs/my-website/docs/proxy_server.md | 4 +- docs/my-website/docs/set_keys.md | 2 +- .../docs/tutorials/lm_evaluation_harness.md | 10 ++--- litellm/assistants/main.py | 8 ++++ litellm/batches/main.py | 4 ++ litellm/files/main.py | 5 +++ litellm/fine_tuning/main.py | 4 ++ .../llms/openai/chat/gpt_transformation.py | 1 + .../llms/openai/responses/transformation.py | 1 + litellm/main.py | 6 +++ tests/litellm/test_main.py | 45 ++++++++++++++++++- 14 files changed, 86 insertions(+), 15 deletions(-) diff --git a/.env.example b/.env.example index 54986a97cd..c6df78cafe 100644 --- a/.env.example +++ b/.env.example @@ -1,6 +1,6 @@ # OpenAI OPENAI_API_KEY = "" -OPENAI_API_BASE = "" +OPENAI_BASE_URL = "" # Cohere COHERE_API_KEY = "" # OpenRouter diff --git a/docs/my-website/docs/providers/openai.md b/docs/my-website/docs/providers/openai.md index a4aee5dbf7..4fd75035fb 100644 --- a/docs/my-website/docs/providers/openai.md +++ b/docs/my-website/docs/providers/openai.md @@ -156,7 +156,7 @@ print(response) ```python import os os.environ["OPENAI_ORGANIZATION"] = "your-org-id" # OPTIONAL -os.environ["OPENAI_API_BASE"] = "openaiai-api-base" # OPTIONAL +os.environ["OPENAI_BASE_URL"] = "https://your_host/v1" # OPTIONAL ``` ### OpenAI Chat Completion Models @@ -194,7 +194,7 @@ os.environ["OPENAI_API_BASE"] = "openaiai-api-base" # OPTIONAL | gpt-4-32k-0613 | `response = completion(model="gpt-4-32k-0613", messages=messages)` | -These also support the `OPENAI_API_BASE` environment variable, which can be used to specify a custom API endpoint. +These also support the `OPENAI_BASE_URL` environment variable, which can be used to specify a custom API endpoint. ## OpenAI Vision Models | Model Name | Function Call | @@ -620,8 +620,8 @@ os.environ["OPENAI_API_KEY"] = "" # set custom api base to your proxy # either set .env or litellm.api_base -# os.environ["OPENAI_API_BASE"] = "" -litellm.api_base = "your-openai-proxy-url" +# os.environ["OPENAI_BASE_URL"] = "https://your_host/v1" +litellm.api_base = "https://your_host/v1" messages = [{ "content": "Hello, how are you?","role": "user"}] diff --git a/docs/my-website/docs/proxy/config_settings.md b/docs/my-website/docs/proxy/config_settings.md index 0f88c79cf2..5173841f1f 100644 --- a/docs/my-website/docs/proxy/config_settings.md +++ b/docs/my-website/docs/proxy/config_settings.md @@ -462,6 +462,7 @@ router_settings: | NO_DOCS | Flag to disable documentation generation | NO_PROXY | List of addresses to bypass proxy | OAUTH_TOKEN_INFO_ENDPOINT | Endpoint for OAuth token info retrieval +| OPENAI_BASE_URL | Base URL for OpenAI API | OPENAI_API_BASE | Base URL for OpenAI API | OPENAI_API_KEY | API key for OpenAI services | OPENAI_ORGANIZATION | Organization identifier for OpenAI diff --git a/docs/my-website/docs/proxy_server.md b/docs/my-website/docs/proxy_server.md index 0d08db7444..e23d64e443 100644 --- a/docs/my-website/docs/proxy_server.md +++ b/docs/my-website/docs/proxy_server.md @@ -337,7 +337,7 @@ export OPENAI_API_KEY="sk-1234" ``` ```shell -export OPENAI_API_BASE="http://0.0.0.0:8000" +export OPENAI_BASE_URL="http://0.0.0.0:8000" ``` ```shell python3 run.py --task "a script that says hello world" --name "hello world" @@ -572,7 +572,7 @@ export OPENAI_API_KEY="sk-1234" ``` ```shell -export OPENAI_API_BASE="http://0.0.0.0:8000" +export OPENAI_BASE_URL="http://0.0.0.0:8000" ``` ```shell python3 run.py --task "a script that says hello world" --name "hello world" diff --git a/docs/my-website/docs/set_keys.md b/docs/my-website/docs/set_keys.md index 693cf5f7f4..295d9ec550 100644 --- a/docs/my-website/docs/set_keys.md +++ b/docs/my-website/docs/set_keys.md @@ -44,7 +44,7 @@ os.environ['AZURE_API_VERSION'] = "2023-05-15" # [OPTIONAL] os.environ['AZURE_API_TYPE'] = "azure" # [OPTIONAL] # for openai -os.environ['OPENAI_API_BASE'] = "https://openai-gpt-4-test2-v-12.openai.azure.com/" +os.environ['OPENAI_BASE_URL'] = "https://your_host/v1" ``` ### Setting Project, Location, Token diff --git a/docs/my-website/docs/tutorials/lm_evaluation_harness.md b/docs/my-website/docs/tutorials/lm_evaluation_harness.md index c28f2dac77..01fdb4b304 100644 --- a/docs/my-website/docs/tutorials/lm_evaluation_harness.md +++ b/docs/my-website/docs/tutorials/lm_evaluation_harness.md @@ -39,7 +39,7 @@ pip install openai==0.28.01 **Step 3: Set OpenAI API Base & Key** ```shell -$ export OPENAI_API_BASE=http://0.0.0.0:8000 +$ export OPENAI_BASE_URL=http://0.0.0.0:8000 ``` LM Harness requires you to set an OpenAI API key `OPENAI_API_SECRET_KEY` for running benchmarks @@ -74,7 +74,7 @@ $ litellm --model huggingface/bigcode/starcoder **Step 2: Set OpenAI API Base & Key** ```shell -$ export OPENAI_API_BASE=http://0.0.0.0:8000 +$ export OPENAI_BASE_URL=http://0.0.0.0:8000 ``` Set this to anything since the proxy has the credentials @@ -93,12 +93,12 @@ cd FastEval **Set API Base on FastEval** -On FastEval make the following **2 line code change** to set `OPENAI_API_BASE` +On FastEval make the following **2 line code change** to set `OPENAI_BASE_URL` https://github.com/FastEval/FastEval/pull/90/files ```python try: - api_base = os.environ["OPENAI_API_BASE"] #changed: read api base from .env + api_base = os.environ["OPENAI_BASE_URL"] #changed: read api base from .env if api_base == None: api_base = "https://api.openai.com/v1" response = await self.reply_two_attempts_with_different_max_new_tokens( @@ -130,7 +130,7 @@ $ litellm --model huggingface/bigcode/starcoder **Step 2: Set OpenAI API Base & Key** ```shell -$ export OPENAI_API_BASE=http://0.0.0.0:8000 +$ export OPENAI_BASE_URL=http://0.0.0.0:8000 ``` **Step 3 Run with FLASK** diff --git a/litellm/assistants/main.py b/litellm/assistants/main.py index 928b6e8ac2..cb9375e6b8 100644 --- a/litellm/assistants/main.py +++ b/litellm/assistants/main.py @@ -110,6 +110,7 @@ def get_assistants( api_base = ( optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -314,6 +315,7 @@ def create_assistants( api_base = ( optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -490,6 +492,7 @@ def delete_assistant( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -678,6 +681,7 @@ def create_thread( api_base = ( optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -833,6 +837,7 @@ def get_thread( api_base = ( optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -1021,6 +1026,7 @@ def add_message( api_base = ( optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -1182,6 +1188,7 @@ def get_messages( api_base = ( optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -1380,6 +1387,7 @@ def run_thread( api_base = ( optional_params.api_base # for deepinfra/perplexity/anyscale/groq we check in get_llm_provider and pass in the api base from there or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) diff --git a/litellm/batches/main.py b/litellm/batches/main.py index f4f74c72fb..0be9667790 100644 --- a/litellm/batches/main.py +++ b/litellm/batches/main.py @@ -157,6 +157,7 @@ def create_batch( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -361,6 +362,7 @@ def retrieve_batch( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -556,6 +558,7 @@ def list_batches( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -713,6 +716,7 @@ def cancel_batch( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) diff --git a/litellm/files/main.py b/litellm/files/main.py index ebe79c1079..ded74cc653 100644 --- a/litellm/files/main.py +++ b/litellm/files/main.py @@ -164,6 +164,7 @@ def create_file( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -343,6 +344,7 @@ def file_retrieve( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -496,6 +498,7 @@ def file_delete( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -649,6 +652,7 @@ def file_list( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -809,6 +813,7 @@ def file_content( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) diff --git a/litellm/fine_tuning/main.py b/litellm/fine_tuning/main.py index 09c070fffb..b7efcb40d4 100644 --- a/litellm/fine_tuning/main.py +++ b/litellm/fine_tuning/main.py @@ -142,6 +142,7 @@ def create_fine_tuning_job( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -363,6 +364,7 @@ def cancel_fine_tuning_job( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -524,6 +526,7 @@ def list_fine_tuning_jobs( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -678,6 +681,7 @@ def retrieve_fine_tuning_job( api_base = ( optional_params.api_base or litellm.api_base + or os.getenv("OPENAI_BASE_URL") or os.getenv("OPENAI_API_BASE") or "https://api.openai.com/v1" ) diff --git a/litellm/llms/openai/chat/gpt_transformation.py b/litellm/llms/openai/chat/gpt_transformation.py index e8f60357a6..f7462d11f0 100644 --- a/litellm/llms/openai/chat/gpt_transformation.py +++ b/litellm/llms/openai/chat/gpt_transformation.py @@ -384,6 +384,7 @@ class OpenAIGPTConfig(BaseLLMModelInfo, BaseConfig): return ( api_base or litellm.api_base + or get_secret_str("OPENAI_BASE_URL") or get_secret_str("OPENAI_API_BASE") or "https://api.openai.com/v1" ) diff --git a/litellm/llms/openai/responses/transformation.py b/litellm/llms/openai/responses/transformation.py index 8cbdf6bdcc..bdbdcf99fd 100644 --- a/litellm/llms/openai/responses/transformation.py +++ b/litellm/llms/openai/responses/transformation.py @@ -119,6 +119,7 @@ class OpenAIResponsesAPIConfig(BaseResponsesAPIConfig): api_base = ( api_base or litellm.api_base + or get_secret_str("OPENAI_BASE_URL") or get_secret_str("OPENAI_API_BASE") or "https://api.openai.com/v1" ) diff --git a/litellm/main.py b/litellm/main.py index 489ed25880..28c2ddce4d 100644 --- a/litellm/main.py +++ b/litellm/main.py @@ -1548,6 +1548,7 @@ def completion( # type: ignore # noqa: PLR0915 api_base = ( api_base or litellm.api_base + or get_secret("OPENAI_BASE_URL") or get_secret("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -1704,6 +1705,7 @@ def completion( # type: ignore # noqa: PLR0915 api_base = ( api_base # for deepinfra/perplexity/anyscale/groq/friendliai we check in get_llm_provider and pass in the api base from there or litellm.api_base + or get_secret("OPENAI_BASE_URL") or get_secret("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -1757,6 +1759,7 @@ def completion( # type: ignore # noqa: PLR0915 api_base = ( api_base # for deepinfra/perplexity/anyscale/groq/friendliai we check in get_llm_provider and pass in the api base from there or litellm.api_base + or get_secret("OPENAI_BASE_URL") or get_secret("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -3543,6 +3546,7 @@ def embedding( # noqa: PLR0915 api_base = ( api_base or litellm.api_base + or get_secret_str("OPENAI_BASE_URL") or get_secret_str("OPENAI_API_BASE") or "https://api.openai.com/v1" ) @@ -5251,6 +5255,7 @@ def transcription( api_base = ( api_base or litellm.api_base + or get_secret("OPENAI_BASE_URL") or get_secret("OPENAI_API_BASE") or "https://api.openai.com/v1" ) # type: ignore @@ -5421,6 +5426,7 @@ def speech( # noqa: PLR0915 api_base = ( api_base # for deepinfra/perplexity/anyscale/groq/friendliai we check in get_llm_provider and pass in the api base from there or litellm.api_base + or get_secret("OPENAI_BASE_URL") or get_secret("OPENAI_API_BASE") or "https://api.openai.com/v1" ) # type: ignore diff --git a/tests/litellm/test_main.py b/tests/litellm/test_main.py index b3e085df6c..5c3c14e965 100644 --- a/tests/litellm/test_main.py +++ b/tests/litellm/test_main.py @@ -277,7 +277,7 @@ def set_openrouter_api_key(): async def test_extra_body_with_fallback(respx_mock: respx.MockRouter, set_openrouter_api_key): """ test regression for https://github.com/BerriAI/litellm/issues/8425. - + This was perhaps a wider issue with the acompletion function not passing kwargs such as extra_body correctly when fallbacks are specified. """ # Set up test parameters @@ -341,4 +341,45 @@ async def test_extra_body_with_fallback(respx_mock: respx.MockRouter, set_openro # Verify the response assert response is not None assert response.choices[0].message.content == "Hello from mocked response!" - \ No newline at end of file + + +@pytest.mark.parametrize("env_base", ["OPENAI_BASE_URL", "OPENAI_API_BASE"]) +@pytest.mark.asyncio +async def test_openai_env_base( + respx_mock: respx.MockRouter, env_base, openai_api_response, monkeypatch +): + "This tests OpenAI env variables are honored, including legacy OPENAI_API_BASE" + + expected_base_url = "http://localhost:12345/v1" + + # Assign the environment variable based on env_base, and use a fake API key. + monkeypatch.setenv(env_base, expected_base_url) + monkeypatch.setenv("OPENAI_API_KEY", "fake_openai_api_key") + + model = "gpt-4o" + messages = [{"role": "user", "content": "Hello, how are you?"}] + + respx_mock.post(f"{expected_base_url}/chat/completions").respond( + json={ + "id": "chatcmpl-123", + "object": "chat.completion", + "created": 1677652288, + "model": model, + "choices": [ + { + "index": 0, + "message": { + "role": "assistant", + "content": "Hello from mocked response!", + }, + "finish_reason": "stop", + } + ], + "usage": {"prompt_tokens": 9, "completion_tokens": 12, "total_tokens": 21}, + } + ) + + response = await litellm.acompletion(model=model, messages=messages) + + # verify we had a response + assert response.choices[0].message.content == "Hello from mocked response!"