Add Open AI Comatible API option in model configuration

Add unit tests for various UI components and context hooks

- Created tests for Progress, RadioGroup, Select, Separator, Sheet, Sidebar, Skeleton, Sonner, Switch, Table, Tabs, Textarea, Tooltip components.
- Implemented tests for UserConfigContext and other context hooks.
- Added utility tests for filters and utils functions.
- Set up MSW for API mocking in tests.
- Configured Vitest for testing environment with necessary setup.
This commit is contained in:
Chris Briddock 2026-05-25 19:26:41 +01:00
parent bbb4f91a27
commit 20617db37a
No known key found for this signature in database
44 changed files with 8426 additions and 5661 deletions

View file

@ -181,30 +181,58 @@ class UserConfigurationValidator:
api_key = service_config.api_key
try:
if not self._check_api_key(provider, api_key):
if not self._check_api_key(provider, api_key, service_config):
return [
{"model": service_name, "message": f"Invalid {provider} API key"}
{
"model": service_name,
"message": (
f"Invalid {provider} API key. Please verify your API key is "
f"correct, has not expired, and has the required permissions."
),
}
]
except ValueError as e:
return [{"model": service_name, "message": str(e)}]
return []
def _check_api_key(self, provider: str, api_key: str) -> bool:
def _check_api_key(
self, provider: str, api_key: str, service_config: Optional[ServiceConfig] = None
) -> bool:
"""Check if an API key for a provider is valid."""
validator = self._validator_map.get(provider)
if not validator:
return False
if provider in (
ServiceProviders.OPENAI.value,
ServiceProviders.OPENAI_REALTIME.value,
):
return validator(provider, api_key, service_config)
return validator(provider, api_key)
def _check_openai_api_key(self, model: str, api_key: str) -> bool:
client = openai.OpenAI(api_key=api_key)
def _check_openai_api_key(
self, model: str, api_key: str, service_config: Optional[ServiceConfig] = None
) -> bool:
client_kwargs: dict[str, str] = {"api_key": api_key}
base_url = getattr(service_config, "base_url", None) if service_config else None
if base_url:
client_kwargs["base_url"] = base_url
client = openai.OpenAI(**client_kwargs)
try:
client.models.list()
return True
except openai.AuthenticationError:
return False
if base_url and "openai.com" not in base_url:
raise ValueError(
f"Invalid OpenAI API key. The key was rejected by the API at {base_url}. "
"Please check that your API key is correct and has not been revoked."
)
raise ValueError(
"Invalid OpenAI API key. The key was rejected by the OpenAI API. "
"Please check that your API key is correct and has not been revoked. "
"You can verify your keys at https://platform.openai.com/api-keys."
)
def _check_deepgram_api_key(self, model: str, api_key: str) -> bool:
try:
@ -212,7 +240,11 @@ class UserConfigurationValidator:
deepgram.manage.v1.projects.list()
return True
except Exception:
return False
raise ValueError(
"Invalid Deepgram API key. The key was rejected by the Deepgram API. "
"Please check that your API key is correct and active. "
"You can verify your keys at https://console.deepgram.com/."
)
def _check_groq_api_key(self, model: str, api_key: str) -> bool:
client = Groq(api_key=api_key)
@ -220,7 +252,11 @@ class UserConfigurationValidator:
client.models.list()
return True
except Exception:
return False
raise ValueError(
"Invalid Groq API key. The key was rejected by the Groq API. "
"Please check that your API key is correct and active. "
"You can verify your keys at https://console.groq.com/keys."
)
def _validate_elevenlabs_api_key(self, model: str, api_key: str) -> bool:
return True

View file

@ -290,6 +290,10 @@ class OpenAILLMService(BaseLLMConfiguration):
description="OpenAI chat model to use.",
json_schema_extra={"examples": OPENAI_MODELS, "allow_custom_input": True},
)
base_url: str = Field(
default="https://api.openai.com/v1",
description="Override only if using an OpenAI-compatible API (e.g. local LLM, proxy).",
)
@register_llm

View file

@ -504,6 +504,9 @@ def create_llm_service_from_provider(
"""
logger.info(f"Creating LLM service: provider={provider}, model={model}")
if provider == ServiceProviders.OPENAI.value:
kwargs = {}
if base_url:
kwargs["base_url"] = base_url
if "gpt-5" in model:
return OpenAILLMService(
api_key=api_key,
@ -511,10 +514,12 @@ def create_llm_service_from_provider(
model=model,
extra={"reasoning_effort": "minimal", "verbosity": "low"},
),
**kwargs,
)
return OpenAILLMService(
api_key=api_key,
settings=OpenAILLMSettings(model=model, temperature=0.1),
**kwargs,
)
elif provider == ServiceProviders.GROQ.value:
return GroqLLMService(
@ -709,7 +714,9 @@ def create_llm_service(user_config):
api_key = user_config.llm.api_key
kwargs = {}
if provider == ServiceProviders.OPENROUTER.value:
if provider == ServiceProviders.OPENAI.value:
kwargs["base_url"] = user_config.llm.base_url
elif provider == ServiceProviders.OPENROUTER.value:
kwargs["base_url"] = user_config.llm.base_url
elif provider == ServiceProviders.AZURE.value:
kwargs["endpoint"] = user_config.llm.endpoint