Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 6 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,12 @@ the GitHub Release body, so a release with no entry here fails.

Versioning follows [docs/versioning.md](docs/versioning.md).

## [0.11.2] - 2026-09-25

### Fixed

- `OpenAIClient` and `OpenAIResponsesClient` now treat an empty `api_key` as unset and fall back to `OPENAI_API_KEY`, matching the behavior of `None`.

## [0.11.1] - 2026-09-24

### Added
Expand Down
5 changes: 4 additions & 1 deletion agent_core/providers/openai_chat.py
Original file line number Diff line number Diff line change
Expand Up @@ -180,7 +180,10 @@ def __init__(
# (see ``mirror_session_query``) — mirror a construction-time session
# header into ``default_query`` so it rides every request's URL.
self._client = AsyncOpenAI(
api_key=api_key,
# The SDK consults OPENAI_API_KEY only for ``None``; an empty
# string (a config read before the environment was populated)
# raises "Missing credentials" even with the variable set.
api_key=api_key or None,
base_url=base_url or None,
timeout=timeout,
default_headers=default_headers,
Expand Down
5 changes: 4 additions & 1 deletion agent_core/providers/openai_responses.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,10 @@ def __init__(
self._reasoning = reasoning or None
self._store = store
self._client = AsyncOpenAI(
api_key=api_key,
# The SDK consults OPENAI_API_KEY only for ``None``; an empty
# string (a config read before the environment was populated)
# raises "Missing credentials" even with the variable set.
api_key=api_key or None,
base_url=base_url or None,
timeout=timeout,
default_headers=default_headers,
Expand Down
2 changes: 1 addition & 1 deletion pyproject.toml
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[project]
name = "apodex-agent-core"
version = "0.11.1"
version = "0.11.2"
description = "Shared, product-neutral runtime primitives for Apodex agents"
readme = "README.md"
license = "Apache-2.0"
Expand Down
28 changes: 28 additions & 0 deletions tests/test_openai_empty_api_key.py
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
"""An empty ``api_key`` must fall back to ``OPENAI_API_KEY`` like ``None``."""

from __future__ import annotations

import pytest

from agent_core.providers.openai_chat import OpenAIClient
from agent_core.providers.openai_responses import OpenAIResponsesClient


@pytest.mark.parametrize("client_cls", [OpenAIClient, OpenAIResponsesClient])
def test_empty_api_key_uses_the_environment(
client_cls: type, monkeypatch: pytest.MonkeyPatch
) -> None:
monkeypatch.setenv("OPENAI_API_KEY", "from-env")

client = client_cls("gpt-test", api_key="")

assert client._client.api_key == "from-env"


@pytest.mark.parametrize("client_cls", [OpenAIClient, OpenAIResponsesClient])
def test_explicit_api_key_wins(client_cls: type, monkeypatch: pytest.MonkeyPatch) -> None:
monkeypatch.setenv("OPENAI_API_KEY", "from-env")

client = client_cls("gpt-test", api_key="explicit")

assert client._client.api_key == "explicit"
2 changes: 1 addition & 1 deletion uv.lock

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

Loading