diff --git a/litellm/llms/openai/common_utils.py b/litellm/llms/openai/common_utils.py index ffaf987d44e..505e1e45bdd 100644 --- a/litellm/llms/openai/common_utils.py +++ b/litellm/llms/openai/common_utils.py @@ -163,9 +163,10 @@ class OpenAISdkKeyParams(TypedDict, total=False): def openai_sdk_key_params(api_key: str | None) -> OpenAISdkKeyParams: if not _SDK_READS_ADMIN_KEY_ENV: return {"api_key": api_key} - if not api_key: + sdk_api_key: Final = os.environ.get("OPENAI_API_KEY") if api_key is None else api_key + if not sdk_api_key: raise openai.OpenAIError(_MISSING_OPENAI_API_KEY_MESSAGE) - return {"api_key": api_key, "admin_api_key": api_key} + return {"api_key": sdk_api_key, "admin_api_key": sdk_api_key} _DECODED_JSON: Final = TypeAdapter(object) diff --git a/tests/integration/authorization/_responses_compact.py b/tests/integration/authorization/_responses_compact.py index 8270d729667..e33aa749c42 100644 --- a/tests/integration/authorization/_responses_compact.py +++ b/tests/integration/authorization/_responses_compact.py @@ -322,7 +322,7 @@ def assert_compacted(compacted: CompactedResponse, model: str, text: str) -> Non "encrypted_content": COMPACT_STATE, } ], - "usage": dict(COMPACT_USAGE), + "usage": {**COMPACT_USAGE, "input_tokens_details": {"cache_write_tokens": 0, "cached_tokens": 0}}, }, compacted.model_dump_json() diff --git a/tests/integration/providers/test_responses_compact_wire.py b/tests/integration/providers/test_responses_compact_wire.py index 88fbec27c07..0c446d8dcc5 100644 --- a/tests/integration/providers/test_responses_compact_wire.py +++ b/tests/integration/providers/test_responses_compact_wire.py @@ -282,7 +282,7 @@ def test_sdk_compact_reaches_the_compact_endpoint_with_its_full_body(gateway: Ga "output": [output_item], "usage": { "input_tokens": 7, - "input_tokens_details": {"cached_tokens": 0}, + "input_tokens_details": {"cache_write_tokens": 0, "cached_tokens": 0}, "output_tokens": 3, "output_tokens_details": {"reasoning_tokens": 0}, "total_tokens": 10, diff --git a/tests/integration/translation/responses/bases/gemini.py b/tests/integration/translation/responses/bases/gemini.py index 597dbc25ab7..16915b16079 100644 --- a/tests/integration/translation/responses/bases/gemini.py +++ b/tests/integration/translation/responses/bases/gemini.py @@ -577,6 +577,7 @@ GEMINI_3_5_FLASH_TEST_CASE: Final = TranslationTestCase( "image_tokens": None, "text_tokens": 10, "video_tokens": None, + "cache_write_tokens": 0, }, "output_tokens": 127, "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 125, "text_tokens": 2}, diff --git a/tests/integration/translation/responses/bases/vertex_ai.py b/tests/integration/translation/responses/bases/vertex_ai.py index 59637e380ce..b68eec3f06b 100644 --- a/tests/integration/translation/responses/bases/vertex_ai.py +++ b/tests/integration/translation/responses/bases/vertex_ai.py @@ -472,6 +472,7 @@ GEMINI_3_5_FLASH_TEST_CASE: Final = TranslationTestCase( "image_tokens": None, "text_tokens": 9, "video_tokens": None, + "cache_write_tokens": 0, }, "output_tokens": 86, "output_tokens_details": {"audio_tokens": None, "reasoning_tokens": 84, "text_tokens": 2}, diff --git a/tests/unit/llms/azure/test_azure_common_utils.py b/tests/unit/llms/azure/test_azure_common_utils.py index ecd2823b503..260f61ab838 100644 --- a/tests/unit/llms/azure/test_azure_common_utils.py +++ b/tests/unit/llms/azure/test_azure_common_utils.py @@ -11,6 +11,7 @@ from unittest.mock import ANY, MagicMock, patch import httpx import pytest +import respx from openai import AsyncAzureOpenAI import litellm @@ -2652,3 +2653,37 @@ def test_azure_region_lookup_sends_the_key_and_bearer_headers(monkeypatch: pytes params: Final = LiteLLM_Params(model="azure/gpt-region", api_key="azure-key", api_base=api_base) assert get_model_region(litellm_params=params, mode="chat") == "eastus" + + +@pytest.mark.parametrize("is_async", [False, True]) +@pytest.mark.asyncio +async def test_azure_v1_deployment_keyed_only_by_the_openai_api_key_env_sends_that_key( + monkeypatch: pytest.MonkeyPatch, respx_mock: respx.MockRouter, is_async: bool +) -> None: + for name in ("AZURE_API_KEY", "AZURE_OPENAI_API_KEY", "AZURE_AD_TOKEN", "AZURE_OPENAI_AD_TOKEN", "AZURE_CLIENT_ID"): + monkeypatch.delenv(name, raising=False) + monkeypatch.setenv("OPENAI_API_KEY", "azure-key-in-openai-env") + monkeypatch.setenv("OPENAI_ADMIN_KEY", "sk-admin-key-for-org-apis") + monkeypatch.setenv("DISABLE_AIOHTTP_TRANSPORT", "True") + api_base: Final = f"https://envkey-{is_async}.openai.azure.com" + route: Final = respx_mock.post(f"{api_base}/openai/v1/chat/completions").respond( + 200, + json={ + "id": "chatcmpl-envkey", + "object": "chat.completion", + "created": 0, + "model": "gpt-4o-mini", + "choices": [{"index": 0, "finish_reason": "stop", "message": {"role": "assistant", "content": "hi"}}], + }, + ) + request: Final = { + "model": "azure/gpt-4o-mini", + "messages": [{"role": "user", "content": "hi"}], + "api_base": api_base, + "api_version": "v1", + } + + response: Final = await litellm.acompletion(**request) if is_async else litellm.completion(**request) + + assert response.choices[0].message.content == "hi" + assert route.calls.last.request.headers["authorization"] == "Bearer azure-key-in-openai-env"