fix(guardrails): declare api_base/api_key on GenericGuardrailAPIConfigModel

The dashboard renders a provider's configuration form from the fields its
config model declares (GET /guardrails/ui/provider_specific_params). For
generic_guardrail_api that model only declared optional_params, and the form
deliberately skips a nested optional_params block, so selecting the provider
rendered no configuration fields at all - not even the api_base the endpoint
cannot work without.

api_base and api_key are read from litellm_params at runtime, so they never
needed to be model fields to work from config.yaml; they do need to be fields
for the UI to offer them. Six other providers (compresr, headroom,
crowdstrike_aidr, alice, aporia_ai, cato_networks) already declare them this
way, so this makes the generic provider consistent rather than inventing a
mechanism.

Fixes #34927
This commit is contained in:
zunami 2026-10-28 08:20:04 +01:00
parent e32d8a4d03
commit 5e67cf2b9f
2 changed files with 28 additions and 0 deletions

View file

@ -130,6 +130,14 @@ class GenericGuardrailAPIConfigModel(
):
"""Configuration parameters for the Generic Guardrail API guardrail"""
api_base: str | None = Field(
default=None,
description="Base URL of the guardrail endpoint to call (e.g. http://llm-guard:8000/analyze). Required when creating the guardrail: without it there is nowhere to send the request.",
)
api_key: str | None = Field(
default=None,
description="API key sent to the guardrail endpoint. Optional; omit it when the endpoint does not authenticate the caller.",
)
optional_params: GenericGuardrailAPIOptionalParams | None = Field(
default_factory=GenericGuardrailAPIOptionalParams,
description="Optional parameters for the Generic Guardrail API guardrail",

View file

@ -1545,6 +1545,26 @@ class TestGenericGuardrailAPIStreamingConfig:
assert GenericGuardrailAPI.get_config_model() is GenericGuardrailAPIConfigModel
def test_config_model_declares_connection_fields(self):
"""The dashboard builds a provider's form from this model.
``get_provider_specific_params`` renders whatever the config model
declares, and the form only offers those keys, so ``api_base`` and
``api_key`` have to be fields here even though the initializer reads
them off ``litellm_params`` at runtime. Without them the provider shows
no configuration fields at all (BerriAI/litellm#34927).
"""
from litellm.types.proxy.guardrails.guardrail_hooks.generic_guardrail_api import (
GenericGuardrailAPIConfigModel,
)
fields = GenericGuardrailAPIConfigModel.model_json_schema().get("properties", {})
for field_name in ("api_base", "api_key"):
assert field_name in fields, f"{field_name} must be a config model field"
assert fields[field_name].get("description"), f"{field_name} needs a description for the form"
assert field_name not in GenericGuardrailAPIConfigModel.model_json_schema().get("required", [])
def test_streaming_transform_mode_defaults_block_only(self):
guardrail = GenericGuardrailAPI(
api_base="https://api.test.guardrail.com",