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
7 changes: 7 additions & 0 deletions CHANGELOG.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
5 changes: 4 additions & 1 deletion agent-ag2/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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(
Expand Down
37 changes: 37 additions & 0 deletions agent-ag2/tests/test_main.py
Original file line number Diff line number Diff line change
Expand Up @@ -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
Loading