feat(llm): auto-configure Google AI Studio URL when LLM_PROVIDER=gemini

When LLM_PROVIDER=gemini, LLMClient automatically uses the Google AI Studio
OpenAI-compatible endpoint instead of requiring manual base_url configuration.
An explicit base_url argument still takes precedence.

Co-Authored-By: Claude Sonnet 4.6 <noreply@anthropic.com>
This commit is contained in:
Ubuntu 2026-04-25 13:13:11 +00:00
parent 788f9c29c9
commit 039de4ad8b
2 changed files with 76 additions and 0 deletions

View File

@ -28,6 +28,10 @@ class LLMClient:
if not self.api_key:
raise ValueError("LLM_API_KEY is not configured")
# Google AI Studio OpenAI-compatible endpoint
if (Config.LLM_PROVIDER or "").lower() == "gemini" and not base_url:
raw_url = "https://generativelanguage.googleapis.com/v1beta/openai/"
# Azure Portal provides full endpoint URLs like:
# https://<resource>.cognitiveservices.azure.com/openai/deployments/<model>/chat/completions?api-version=...
# The OpenAI SDK expects a base_url and appends /chat/completions itself,

View File

@ -0,0 +1,72 @@
import pytest
from unittest.mock import patch, MagicMock
GEMINI_URL = "https://generativelanguage.googleapis.com/v1beta/openai/"
def test_gemini_provider_sets_base_url_automatically():
import backend.app.config as cfg
orig_provider = cfg.Config.LLM_PROVIDER
orig_key = cfg.Config.LLM_API_KEY
orig_url = cfg.Config.LLM_BASE_URL
try:
cfg.Config.LLM_PROVIDER = "gemini"
cfg.Config.LLM_API_KEY = "AIzatest"
cfg.Config.LLM_BASE_URL = "https://api.openai.com/v1"
with patch("backend.app.utils.llm_client.OpenAI") as mock_openai:
mock_openai.return_value = MagicMock()
import importlib
import backend.app.utils.llm_client as lm
importlib.reload(lm)
client = lm.LLMClient()
assert GEMINI_URL in client.base_url
finally:
cfg.Config.LLM_PROVIDER = orig_provider
cfg.Config.LLM_API_KEY = orig_key
cfg.Config.LLM_BASE_URL = orig_url
def test_non_gemini_provider_uses_configured_url():
import backend.app.config as cfg
orig_provider = cfg.Config.LLM_PROVIDER
orig_key = cfg.Config.LLM_API_KEY
orig_url = cfg.Config.LLM_BASE_URL
try:
cfg.Config.LLM_PROVIDER = ""
cfg.Config.LLM_API_KEY = "sk-test"
cfg.Config.LLM_BASE_URL = "https://api.openai.com/v1"
with patch("backend.app.utils.llm_client.OpenAI") as mock_openai:
mock_openai.return_value = MagicMock()
import importlib
import backend.app.utils.llm_client as lm
importlib.reload(lm)
client = lm.LLMClient()
assert "openai.com" in client.base_url
finally:
cfg.Config.LLM_PROVIDER = orig_provider
cfg.Config.LLM_API_KEY = orig_key
cfg.Config.LLM_BASE_URL = orig_url
def test_explicit_base_url_overrides_gemini_auto():
"""If base_url is passed explicitly, it should NOT be replaced even if LLM_PROVIDER=gemini."""
import backend.app.config as cfg
orig_provider = cfg.Config.LLM_PROVIDER
orig_key = cfg.Config.LLM_API_KEY
try:
cfg.Config.LLM_PROVIDER = "gemini"
cfg.Config.LLM_API_KEY = "AIzatest"
with patch("backend.app.utils.llm_client.OpenAI") as mock_openai:
mock_openai.return_value = MagicMock()
import importlib
import backend.app.utils.llm_client as lm
importlib.reload(lm)
client = lm.LLMClient(base_url="https://custom.endpoint/v1")
assert "custom.endpoint" in client.base_url
assert GEMINI_URL not in client.base_url
finally:
cfg.Config.LLM_PROVIDER = orig_provider
cfg.Config.LLM_API_KEY = orig_key