Skip to content

Commit 4ecd3b8

Browse files
authored
Model Config: relax model validation to warning (#902)
Co-authored-by: Prajna Prayas <gituprajna20@gmail.com> Merged quickly to test it out on staging
1 parent b397f4f commit 4ecd3b8

2 files changed

Lines changed: 29 additions & 57 deletions

File tree

backend/app/crud/model_config.py

Lines changed: 10 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,11 @@
1+
import logging
12
from typing import Any, Literal
23

34
from fastapi import HTTPException
45
from sqlmodel import Session, select
56

7+
logger = logging.getLogger(__name__)
8+
69
from app.models import ModelConfig
710
from app.models.llm.request import ConfigBlob
811
from app.models.model_config import CompletionType
@@ -120,31 +123,12 @@ def validate_blob_model_or_raise(session: Session, blob: ConfigBlob) -> None:
120123
model_name=model_name,
121124
)
122125
if model_row is None:
123-
raise HTTPException(
124-
status_code=400,
125-
detail=f"Model '{model_name}' not found for provider='{provider}'.",
126-
)
127-
128-
if not is_model_supported(
129-
session=session,
130-
provider=provider, # type: ignore[arg-type]
131-
completion_type=completion_type,
132-
model_name=model_name,
133-
):
134-
allowed = list_supported_models(
135-
session=session,
136-
provider=provider, # type: ignore[arg-type]
137-
completion_type=completion_type,
138-
)
139-
raise HTTPException(
140-
status_code=400,
141-
detail=(
142-
f"Model '{model_name}' is not supported for provider='{provider}' "
143-
f"type='{completion_type}'. Allowed: {allowed}"
144-
),
126+
logger.warning(
127+
f"[validate_blob_model_or_raise] Model '{model_name}' not found for provider='{provider}'."
128+
"Kaapi does not yet support this model, but will forward as long as the `model` field has no typos and the model is not deprecated by the provider"
145129
)
146130

147-
if completion_type == "tts":
131+
if completion_type == "tts" and model_row is not None:
148132
voice = (completion.params or {}).get("voice")
149133
voice_spec = (
150134
model_row.config.get("voice")
@@ -155,12 +139,9 @@ def validate_blob_model_or_raise(session: Session, blob: ConfigBlob) -> None:
155139
voice_spec.get("options") if isinstance(voice_spec, dict) else None
156140
)
157141
if voice and allowed_voices and voice not in allowed_voices:
158-
raise HTTPException(
159-
status_code=400,
160-
detail=(
161-
f"Voice '{voice}' is not supported for provider='{provider}' "
162-
f"model='{model_name}'. Allowed: {allowed_voices}"
163-
),
142+
logger.warning(
143+
f"[validate_blob_model_or_raise] Voice '{voice}' is not supported for provider='{provider}' "
144+
f"model='{model_name}'. Allowed: {allowed_voices}."
164145
)
165146

166147

backend/app/tests/crud/test_model_config.py

Lines changed: 19 additions & 28 deletions
Original file line numberDiff line numberDiff line change
@@ -227,22 +227,23 @@ def test_validate_blob_missing_model_raises(monkeypatch: pytest.MonkeyPatch) ->
227227
assert "model is required" in exc.value.detail
228228

229229

230-
def test_validate_blob_model_not_found_raises(
230+
def test_validate_blob_model_not_found_warns_and_continues(
231231
monkeypatch: pytest.MonkeyPatch,
232+
caplog: pytest.LogCaptureFixture,
232233
) -> None:
233-
"""Model that doesn't exist in model_config raises 400 with model name in detail."""
234+
"""Missing model logs a warning and lets the request proceed."""
234235
_patch_validators(monkeypatch, row=None, supported=False)
235236
blob = _make_blob("openai", "text", {"model": "gpt-4-turbo"})
236-
with pytest.raises(HTTPException) as exc:
237+
with caplog.at_level("WARNING"):
237238
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
238-
assert exc.value.status_code == 400
239-
assert "gpt-4-turbo" in exc.value.detail
239+
assert "gpt-4-turbo" in caplog.text
240+
assert "not found" in caplog.text
240241

241242

242-
def test_validate_blob_wrong_type_for_model_raises(
243+
def test_validate_blob_wrong_type_for_model_passes(
243244
monkeypatch: pytest.MonkeyPatch,
244245
) -> None:
245-
"""Model that exists but is wrong type (e.g. TTS model used as text) raises 400 with allowed list."""
246+
"""Wrong completion type no longer validated — request proceeds silently."""
246247
row = SimpleNamespace(config={})
247248
_patch_validators(
248249
monkeypatch,
@@ -251,11 +252,7 @@ def test_validate_blob_wrong_type_for_model_raises(
251252
allowed=["gpt-4o", "gpt-4o-mini"],
252253
)
253254
blob = _make_blob("openai", "text", {"model": "some-audio-model"})
254-
with pytest.raises(HTTPException) as exc:
255-
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
256-
assert exc.value.status_code == 400
257-
assert "some-audio-model" in exc.value.detail
258-
assert "gpt-4o" in exc.value.detail
255+
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
259256

260257

261258
def test_validate_blob_supported_text_passes(monkeypatch: pytest.MonkeyPatch) -> None:
@@ -265,9 +262,11 @@ def test_validate_blob_supported_text_passes(monkeypatch: pytest.MonkeyPatch) ->
265262
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
266263

267264

268-
def test_validate_blob_tts_invalid_voice_raises(
265+
def test_validate_blob_tts_invalid_voice_warns(
269266
monkeypatch: pytest.MonkeyPatch,
267+
caplog: pytest.LogCaptureFixture,
270268
) -> None:
269+
"""Invalid TTS voice logs a warning but does not raise."""
271270
row = SimpleNamespace(
272271
config={"voice": {"type": "enum", "options": ["Kore", "Orus"]}}
273272
)
@@ -277,11 +276,10 @@ def test_validate_blob_tts_invalid_voice_raises(
277276
"tts",
278277
{"model": "gemini-2.5-flash-preview-tts", "voice": "Sarah"},
279278
)
280-
with pytest.raises(HTTPException) as exc:
279+
with caplog.at_level("WARNING"):
281280
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
282-
assert exc.value.status_code == 400
283-
assert "Sarah" in exc.value.detail
284-
assert "Kore" in exc.value.detail
281+
assert "Sarah" in caplog.text
282+
assert "Kore" in caplog.text
285283

286284

287285
def test_validate_blob_tts_valid_voice_passes(
@@ -309,26 +307,19 @@ def test_validate_blob_tts_no_voice_spec_passes(
309307
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
310308

311309

312-
def test_validate_blob_stt_model_rejected_for_text_type(
310+
def test_validate_blob_stt_model_passes_for_text_type(
313311
monkeypatch: pytest.MonkeyPatch,
314312
) -> None:
315-
"""STT-only model (audio input) must be rejected when type=text.
316-
317-
Regression: previously only stt/tts triggered is_model_supported; type=text
318-
only checked model existence, so gemini-2.5-pro (STT) passed as a text model.
319-
"""
313+
"""Completion-type mismatch no longer enforced — STT model passes for type=text."""
320314
row = SimpleNamespace(config={})
321315
_patch_validators(
322316
monkeypatch,
323317
row=row,
324-
supported=False, # modality filter excludes AUDIO-input models for type=text
318+
supported=False,
325319
allowed=["gpt-4o", "gpt-4o-mini"],
326320
)
327321
blob = _make_blob("google", "text", {"model": "gemini-2.5-pro"})
328-
with pytest.raises(HTTPException) as exc:
329-
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
330-
assert exc.value.status_code == 400
331-
assert "gemini-2.5-pro" in exc.value.detail
322+
model_config_crud.validate_blob_model_or_raise(session=None, blob=blob) # type: ignore[arg-type]
332323

333324

334325
def test_validate_blob_text_model_accepted_for_text_type(

0 commit comments

Comments
 (0)