Skip to content

Commit 26f82ee

Browse files
fulfarodevclaude
andcommitted
chore: merge develop -> main for v0.28.3
OpenAICompatProvider fails fast with a clear ModelProviderError when no API key is configured, instead of the cryptic httpx "Illegal header value b'Bearer '". See CHANGELOG.md [v0.28.3]. Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2 parents 7752568 + 82542fa commit 26f82ee

5 files changed

Lines changed: 43 additions & 4 deletions

File tree

CHANGELOG.md

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,11 @@ and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0
77

88
## [Unreleased]
99

10+
## [v0.28.3] - 2026-05-22
11+
12+
### Fixed
13+
- `OpenAICompatProvider` now fails fast with a clear `ModelProviderError` (`code=model_missing_api_key`) when no API key is configured. Previously it built an empty `Authorization: Bearer ` header and the request died with the cryptic `httpx.LocalProtocolError: Illegal header value b'Bearer '`; the router surfaced that as the last-error string with no hint that a key was simply missing. The error now reads `No API key for OpenAI-compatible provider (model '...')` and names the expected env var. Router fallback is unaffected — the provider raises at request time like any other failure (`astromesh/providers/openai_compat.py`)
14+
1015
## [v0.28.2] - 2026-05-20
1116

1217
No changes to the `astromesh` core package; this release tracks the

astromesh/__init__.py

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,3 +1,3 @@
11
"""Astromesh Agent Runtime Platform."""
22

3-
__version__ = "0.28.2"
3+
__version__ = "0.28.3"

astromesh/providers/openai_compat.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88

99
import httpx
1010

11+
from astromesh.errors import ModelProviderError
12+
1113
from .base import CompletionChunk, CompletionResponse
1214

1315
# Pricing per 1 000 tokens (input, output) in USD
@@ -29,14 +31,24 @@ def __init__(self, config: dict[str, Any] | None = None) -> None:
2931
self.model: str = config.get("model", "gpt-4o")
3032
self.timeout: float = config.get("timeout", 120.0)
3133

34+
env_var = config.get("api_key_env", "OPENAI_API_KEY")
3235
api_key = config.get("api_key")
3336
if not api_key:
34-
env_var = config.get("api_key_env", "OPENAI_API_KEY")
3537
api_key = os.environ.get(env_var, "")
3638
self.api_key: str = api_key
39+
self.api_key_env: str = env_var
3740
self._client: httpx.AsyncClient | None = None
3841

3942
async def _get_client(self) -> httpx.AsyncClient:
43+
if not self.api_key:
44+
raise ModelProviderError(
45+
f"No API key for OpenAI-compatible provider (model '{self.model}').",
46+
hint=(
47+
f"Set the {self.api_key_env} environment variable, or pass "
48+
f"'api_key' in the provider config. Endpoint: {self.base_url}"
49+
),
50+
code="model_missing_api_key",
51+
)
4052
if self._client is None or self._client.is_closed:
4153
self._client = httpx.AsyncClient(
4254
base_url=self.base_url,

pyproject.toml

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
[project]
22
name = "astromesh"
3-
version = "0.28.2"
3+
version = "0.28.3"
44
description = "Astromesh Agent Runtime Platform — multi-model, multi-pattern AI agent runtime"
55
readme = "README.md"
66
requires-python = ">=3.12"
@@ -76,7 +76,7 @@ line-length = 100
7676

7777
[tool.commitizen]
7878
name = "cz_conventional_commits"
79-
version = "0.28.2"
79+
version = "0.28.3"
8080
version_files = [
8181
"pyproject.toml:version",
8282
"astromesh/__init__.py:__version__",

tests/test_providers.py

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,10 @@
33
from __future__ import annotations
44

55
import httpx
6+
import pytest
67
import respx
78

9+
from astromesh.errors import ModelProviderError
810
from astromesh.providers.base import CompletionResponse
911
from astromesh.providers.hf_tgi_provider import HFTGIProvider
1012
from astromesh.providers.llamacpp_provider import LlamaCppProvider
@@ -122,6 +124,26 @@ def test_openai_compat_supports_tools():
122124
assert provider.supports_tools() is True
123125

124126

127+
async def test_openai_compat_missing_api_key_fails_fast(monkeypatch):
128+
"""No API key → fail fast with a clear ModelProviderError instead of an
129+
empty 'Authorization: Bearer ' header that httpx rejects as illegal."""
130+
monkeypatch.delenv("ANTHROPIC_API_KEY", raising=False)
131+
132+
provider = OpenAICompatProvider(
133+
{
134+
"base_url": "https://api.anthropic.com/v1",
135+
"model": "claude-sonnet-4-6",
136+
"api_key_env": "ANTHROPIC_API_KEY",
137+
}
138+
)
139+
140+
with pytest.raises(ModelProviderError) as exc_info:
141+
await provider.complete(MESSAGES)
142+
143+
assert exc_info.value.code == "model_missing_api_key"
144+
assert "ANTHROPIC_API_KEY" in exc_info.value.hint
145+
146+
125147
# ===================================================================
126148
# vLLM
127149
# ===================================================================

0 commit comments

Comments
 (0)