Reject unknown chat model providers and rename gemini to google

This commit is contained in:
Yiorgis Gozadinos 2026-08-27 11:03:11 +03:00
parent d0c66eef8f
commit ad100ecd4d
No known key found for this signature in database
5 changed files with 75 additions and 12 deletions

View file

@ -2,6 +2,10 @@
## [Unreleased]
### Changed
- Chat model provider `gemini` renamed to `google`, matching pydantic-ai. Update `provider: gemini` to `provider: google`.
### Added
- `lancedb.databases` configures a named set of local or remote databases.
@ -33,6 +37,7 @@
- Inspector search results mark truncated previews with an ellipsis.
- Document titles, URIs, headings and database names render as text, not Rich
markup, in `search` output, chat citations and the chat document filter.
- An unrecognized chat model provider raises `Unknown model provider '<name>'` instead of reaching pydantic-ai as a `provider:name` string, and outranks the `api_key` check, so an unusable provider is no longer reported as a missing vendor environment variable.
## [0.78.0] - 2026-08-24

View file

@ -127,7 +127,7 @@ qa:
Same mechanism, opposite direction. Without `extra_body` the Gemma-4 chat template defaults to non-thinking and dumps a verbose answer straight into `content`. With it on, vLLM (started with `--reasoning-parser`) populates the parsed `reasoning` field and leaves `content` as the concise final answer.
**Provider support:** honored by openai, ollama, anthropic, and groq via pydantic-ai's `ModelSettings.extra_body`. Silently ignored by gemini and bedrock.
**Provider support:** honored by openai, ollama, anthropic, and groq via pydantic-ai's `ModelSettings.extra_body`. Silently ignored by google and bedrock.
## Embedding Providers
@ -393,7 +393,7 @@ Any provider supported by Pydantic AI can be used. Examples:
# Google Gemini
qa:
model:
provider: gemini
provider: google
name: gemini-1.5-flash
# Groq

View file

@ -36,7 +36,7 @@ class ModelConfig(ConfigModel):
`ModelSettings.extra_body`. Provider-side escape hatch for
keys haiku.rag doesn't model explicitly (e.g. vLLM's
`chat_template_kwargs.enable_thinking: false` for Qwen3).
Honored by openai/ollama/anthropic/groq; ignored by gemini/bedrock.
Honored by openai/ollama/anthropic/groq; ignored by google/bedrock.
"""
provider: str = "ollama"

View file

@ -62,6 +62,32 @@ def check_api_key_supported(
)
def _check_provider_known(provider: str) -> None:
"""Reject a chat provider pydantic-ai cannot resolve.
Providers we do not branch on reach pydantic-ai as a `provider:name` string,
so without this an unusable name fails deep inside pydantic-ai with nothing
naming the config key it came from. Asking pydantic-ai's own resolver rather
than keeping a list here leaves a newly added provider working with no
release of ours.
"""
from pydantic_ai.providers import infer_provider_class
try:
infer_provider_class(provider)
except ImportError:
# pydantic-ai knows the name, its SDK is just not installed here. That
# failure names the extra to install, so leave it to be raised in place.
return
except ValueError:
raise ValueError(
f"Unknown model provider '{provider}'. See "
"https://ai.pydantic.dev/models/ for the providers pydantic-ai "
"supports. An OpenAI-compatible server (vLLM, sglang, LM Studio) "
"uses provider 'openai' with base_url."
) from None
def cosine_similarity(vec1: list[float], vec2: list[float]) -> float:
"""Compute cosine similarity between two vectors."""
dot_product = sum(a * b for a, b in zip(vec1, vec2))
@ -169,6 +195,7 @@ def get_model(
provider = model_config.provider
model = model_config.name
_check_provider_known(provider)
check_api_key_supported(model_config, {"openai", "ollama"})
if provider == "ollama":
@ -261,7 +288,7 @@ def get_model(
return AnthropicModel(model_name=model, settings=anthropic_settings)
elif provider == "gemini":
elif provider == "google":
from pydantic_ai.models.google import GoogleModel
return GoogleModel(

View file

@ -390,23 +390,23 @@ def test_get_model_anthropic_thinking_off_disables_adaptive_models():
@pytest.mark.skipif(not HAS_GOOGLE, reason="Google not installed")
def test_get_model_gemini():
"""Test get_model returns GoogleModel for Gemini."""
def test_get_model_google():
"""Test get_model returns GoogleModel for Google."""
from pydantic_ai.models.google import GoogleModel
model_config = ModelConfig(provider="gemini", name="gemini-2.0-flash-exp")
model_config = ModelConfig(provider="google", name="gemini-2.0-flash-exp")
result = get_model(model_config)
assert isinstance(result, GoogleModel)
@pytest.mark.skipif(not HAS_GOOGLE, reason="Google not installed")
@pytest.mark.parametrize("enable_thinking", [True, False])
def test_get_model_gemini_with_thinking(enable_thinking):
"""Test get_model configures thinking for Gemini."""
def test_get_model_google_with_thinking(enable_thinking):
"""Test get_model configures thinking for Google."""
from pydantic_ai.models.google import GoogleModel
model_config = ModelConfig(
provider="gemini",
provider="google",
name="gemini-2.0-flash-thinking-exp",
enable_thinking=enable_thinking,
)
@ -536,14 +536,45 @@ def test_get_model_bedrock_rejects_mantle_only_model():
get_model(model_config)
def test_get_model_unknown_provider():
"""Test get_model returns string format for unknown providers."""
def test_get_model_passthrough_for_unbranched_provider():
"""A provider pydantic-ai knows but we do not branch on passes through as a
string, so a new pydantic-ai provider needs no haiku.rag release."""
model_config = ModelConfig(provider="mistral", name="mistral-large-latest")
result = get_model(model_config)
assert isinstance(result, str)
assert result == "mistral:mistral-large-latest"
def test_get_model_accepts_provider_whose_sdk_is_missing(monkeypatch):
"""A missing vendor SDK is not an unknown provider: that ImportError names
the extra to install, so it must reach the caller unchanged.
Uses a provider whose SDK *is* installed, so the patch is what produces the
ImportError rather than the environment.
"""
import pydantic_ai.providers
def _missing_sdk(provider: str):
raise ImportError("Please install the `cohere` package")
monkeypatch.setattr(pydantic_ai.providers, "infer_provider_class", _missing_sdk)
result = get_model(ModelConfig(provider="cohere", name="command-r"))
assert result == "cohere:command-r"
@pytest.mark.parametrize("provider", ["nonsense", "vllm", "gemini"])
def test_get_model_rejects_unknown_provider(provider):
"""An unknown provider is named here rather than passed through to fail
inside pydantic-ai, where nothing identifies the config it came from.
`vllm` and `gemini` get no special case: both were haiku.rag's own
vocabulary, and both fail the same way as a typo.
"""
with pytest.raises(ValueError, match=provider):
get_model(ModelConfig(provider=provider, name="whatever"))
def test_get_package_versions():
"""Test get_package_versions returns expected keys."""
from haiku.rag.utils import get_package_versions