diff --git a/src/openai/_client.py b/src/openai/_client.py index 499a62dfe5..667c9e5502 100644 --- a/src/openai/_client.py +++ b/src/openai/_client.py @@ -106,6 +106,7 @@ class OpenAI(SyncAPIClient): api_key: str admin_api_key: str | None workload_identity: WorkloadIdentity | None + _api_key_is_set: bool organization: str | None project: str | None webhook_secret: str | None @@ -165,6 +166,7 @@ def __init__( if workload_identity is not None: self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER + self._api_key_is_set = True self._api_key_provider = None self._workload_identity_auth = WorkloadIdentityAuth( workload_identity=workload_identity, @@ -172,6 +174,7 @@ def __init__( else: if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") + self._api_key_is_set = api_key is not None if callable(api_key): self.api_key = "" self._api_key_provider: Callable[[], str] | None = api_key # type: ignore[no-redef] @@ -186,7 +189,7 @@ def __init__( if ( _enforce_credentials - and not self.api_key + and not self._api_key_is_set and self._api_key_provider is None and workload_identity is None and self.admin_api_key is None @@ -489,6 +492,9 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return + if self._api_key_is_set and not self.api_key: + return + raise TypeError( '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) @@ -612,6 +618,7 @@ class AsyncOpenAI(AsyncAPIClient): api_key: str admin_api_key: str | None workload_identity: WorkloadIdentity | None + _api_key_is_set: bool organization: str | None project: str | None webhook_secret: str | None @@ -671,6 +678,7 @@ def __init__( if workload_identity is not None: self.api_key = WORKLOAD_IDENTITY_API_KEY_PLACEHOLDER + self._api_key_is_set = True self._api_key_provider = None self._workload_identity_auth = WorkloadIdentityAuth( workload_identity=workload_identity, @@ -678,6 +686,7 @@ def __init__( else: if api_key is None: api_key = os.environ.get("OPENAI_API_KEY") + self._api_key_is_set = api_key is not None if callable(api_key): self.api_key = "" self._api_key_provider: Callable[[], Awaitable[str]] | None = api_key # type: ignore[no-redef] @@ -692,7 +701,7 @@ def __init__( if ( _enforce_credentials - and not self.api_key + and not self._api_key_is_set and self._api_key_provider is None and workload_identity is None and self.admin_api_key is None @@ -995,6 +1004,9 @@ def _validate_headers(self, headers: Headers, custom_headers: Headers) -> None: if _has_header(headers, "Authorization") or _has_omitted_header(custom_headers, "Authorization"): return + if self._api_key_is_set and not self.api_key: + return + raise TypeError( '"Could not resolve authentication method. Expected either api_key or admin_api_key to be set. Or for one of the `Authorization` or `Authorization` headers to be explicitly omitted"' ) diff --git a/tests/test_client.py b/tests/test_client.py index e2bb6ea966..49acf644b6 100644 --- a/tests/test_client.py +++ b/tests/test_client.py @@ -498,6 +498,16 @@ def test_validate_headers(self) -> None: "OPENAI_ADMIN_KEY": Omit(), } ): + local_server = OpenAI( + base_url=base_url, + api_key="", + admin_api_key=None, + _strict_response_validation=True, + ) + local_server_request = local_server._build_request(FinalRequestOptions(method="get", url="/foo")) + assert "Authorization" not in local_server_request.headers + local_server.close() + no_credentials = OpenAI( base_url=base_url, api_key=None, @@ -1762,6 +1772,16 @@ async def test_validate_headers(self) -> None: "OPENAI_ADMIN_KEY": Omit(), } ): + local_server = AsyncOpenAI( + base_url=base_url, + api_key="", + admin_api_key=None, + _strict_response_validation=True, + ) + local_server_request = local_server._build_request(FinalRequestOptions(method="get", url="/foo")) + assert "Authorization" not in local_server_request.headers + await local_server.close() + no_credentials = AsyncOpenAI( base_url=base_url, api_key=None,