fix(openai): read OPENAI_API_KEY from the env before raising for a missing key, as SDK 2 did

An Azure v1 deployment keyed only through OPENAI_API_KEY, and a Secrets Manager
read that comes back empty while the env holds the key, both went from 200 to a
local 401 because the helper raised before the SDK could read the env. Responses
usage expectations in the integration suite now include cache_write_tokens.
This commit is contained in:
mateo-berri 2026-10-29 18:48:01 -07:00
parent 10647ebfcb
commit f9c87d4839
6 changed files with 42 additions and 4 deletions

View file

@ -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)

View file

@ -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()

View file

@ -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,

View file

@ -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},

View file

@ -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},

View file

@ -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"