From bda26625624c576c7ad0eccf78deab19d0826309 Mon Sep 17 00:00:00 2001 From: asemabdallah Date: Mon, 28 Sep 2026 15:43:29 +0300 Subject: [PATCH] Answer on a plain OpenAI key from the AG2 Bot Compose writes OPENAI_BASE_URL empty when the model screen chose a plain OpenAI key, and the OpenAI SDK only defaults an absent URL, so the Bot's client was given "" as its address and every run failed. It now falls back to https://api.openai.com/v1 for an empty value, as its Anthropic branch already does for ANTHROPIC_BASE_URL. --- CHANGELOG.md | 7 +++++++ agent-ag2/src/main.py | 5 ++++- agent-ag2/tests/test_main.py | 37 ++++++++++++++++++++++++++++++++++++ 3 files changed, 48 insertions(+), 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index af2dece18..340c08908 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -67,6 +67,13 @@ Anthropic's OpenAI-compatible endpoint, which may ignore the request for bare JS reply containing multiple objects fell back to offering every tool; replies with no valid `skills` list still do. +### The AG2 Bot answers on a plain OpenAI key + +Picked with an OpenAI key, the AG2 Bot failed every run. Compose writes `OPENAI_BASE_URL` empty when +the choice is a plain OpenAI key, and the OpenAI SDK only defaults an absent URL, so it was given "" +as the address. The Bot now falls back to `https://api.openai.com/v1` for an empty value, as its +Anthropic branch already did for `ANTHROPIC_BASE_URL`. An OpenAI-compatible endpoint is unchanged. + ### Dictate messages and talk to a coworker in a live voice call Deployments can configure transcription separately from their Bots' models, with a waveform composer diff --git a/agent-ag2/src/main.py b/agent-ag2/src/main.py index 5828e4ab9..796bc31bb 100644 --- a/agent-ag2/src/main.py +++ b/agent-ag2/src/main.py @@ -23,7 +23,10 @@ def _config() -> AnthropicConfig | OpenAIConfig: # Compose exports missing overrides as ""; the SDK only defaults an absent URL. base_url = (os.environ.get("ANTHROPIC_BASE_URL") or "").strip() or "https://api.anthropic.com" return AnthropicConfig(model=model, base_url=base_url) - return OpenAIConfig(model=model) + # The same "" for OPENAI_BASE_URL, which Compose writes whenever the model screen chose a plain + # OpenAI key. The SDK only defaults an absent URL; given "", every request fails to connect. + base_url = (os.environ.get("OPENAI_BASE_URL") or "").strip() or "https://api.openai.com/v1" + return OpenAIConfig(model=model, base_url=base_url) agent = Agent( diff --git a/agent-ag2/tests/test_main.py b/agent-ag2/tests/test_main.py index 7f7341d3c..93cf26a8c 100644 --- a/agent-ag2/tests/test_main.py +++ b/agent-ag2/tests/test_main.py @@ -229,3 +229,40 @@ async def respond(transport, request): assert '"RUN_FINISHED"' in response.text assert '"RUN_ERROR"' not in response.text assert "hello" in response.text + + +def test_an_openai_key_uses_the_official_endpoint_when_compose_sets_a_blank_url(monkeypatch): + # The same "" Compose writes for ANTHROPIC_BASE_URL above, on the other provider: the model screen + # sets only the key for a plain OpenAI key, and the SDK treats "" as an address, not as absent. + seen = [] + provider_seen = [] + provider_app = _provider_app(provider_seen) + + async def respond(transport, request): + seen.append((str(request.url), request.headers.get("authorization"))) + async with httpx2.ASGITransport(app=provider_app) as local_provider: + return await local_provider.handle_async_request(request) + + # Keep the real AG2 and OpenAI clients; replace only the network transport. + monkeypatch.setattr(httpx2.AsyncHTTPTransport, "handle_async_request", respond) + monkeypatch.setenv("MANAGED_AGENT_TOKEN", TOKEN) + monkeypatch.setenv("BOT_PROVIDER", "openai") + monkeypatch.setenv("BOT_MODEL", "gpt-5.5") + monkeypatch.setenv("OPENAI_API_KEY", "test-key") + monkeypatch.setenv("OPENAI_BASE_URL", "") + monkeypatch.setenv("ANTHROPIC_API_KEY", "") + monkeypatch.setenv("ANTHROPIC_BASE_URL", "") + + from src import main + + main = importlib.reload(main) + response = TestClient(main.app).post( + "/", json=RUN, headers={"x-openbot-agent-token": TOKEN} + ) + + assert seen == [("https://api.openai.com/v1/chat/completions", "Bearer test-key")] + assert provider_seen == [("openai", "gpt-5.5")] + assert response.status_code == 200 + assert '"RUN_FINISHED"' in response.text + assert '"RUN_ERROR"' not in response.text + assert "hello" in response.text