mirror of
https://github.com/tiennm99/litellm.git
synced 2026-08-03 10:23:28 +00:00
fix(translation): close the together_ai gpt-4-name parity break (verifier-wave1a F2)
v1's TogetherAIConfig.get_supported_openai_params crashes with a bare ValueError (list.remove) for any model literally named gpt-4 or gpt-3.5-turbo-16k without the model-map supports_function_calling flag, and it runs inside get_optional_params on EVERY request -- so v2 serving a plain-text request on those names was a flag-on parity break. The together gate now falls back typed on the name+non-fc combination before any param check, with the reason naming the v1 ValueError mechanism (this also fixes the wrong-exception-class reason the old rf-name fallback carried for these rows). Four differential rows assert the v1 ValueError in-process (exact class, not an UnsupportedParamsError subclass) across the orderings the verifier measured: plain text on both names, temperature-only, and response_format; the report generator emits them as 'FALLBACK (v1 raises ValueError)'.
This commit is contained in:
@@ -27,7 +27,10 @@ from typing import Literal
|
||||
|
||||
from ...deps import TranslationDeps
|
||||
from ...ir import ChatRequest
|
||||
from ..openai_compat.params import unsupported_response_format
|
||||
from ..openai_compat.params import (
|
||||
RESPONSE_FORMAT_UNSUPPORTED_MODELS,
|
||||
unsupported_response_format,
|
||||
)
|
||||
|
||||
CompatSdkProvider = Literal[
|
||||
"together_ai",
|
||||
@@ -147,11 +150,20 @@ def together_ai_unsupported(request: ChatRequest, deps: TranslationDeps) -> str
|
||||
base list unless ``supports_function_calling(model, "together_ai")`` is
|
||||
True (together_ai/chat.py); parallel_tool_calls stays supported either
|
||||
way (v1 truth, not an oversight here)."""
|
||||
allowed = (
|
||||
_BASE_LIST
|
||||
if supports_together_tools(request.model, deps)
|
||||
else _BASE_LIST - _FUNCTION_CALLING_KEYS
|
||||
)
|
||||
supports_tools = supports_together_tools(request.model, deps)
|
||||
if request.model in RESPONSE_FORMAT_UNSUPPORTED_MODELS and not supports_tools:
|
||||
# The base list already dropped response_format for this model name,
|
||||
# so the non-fc fork's list.remove("response_format") crashes inside
|
||||
# get_supported_openai_params -- which _check_valid_arg runs on EVERY
|
||||
# together_ai request, plain text included (verifier-wave1a F2).
|
||||
return (
|
||||
f"model {request.model!r} on together_ai without the model-map "
|
||||
"supports_function_calling flag: v1's TogetherAIConfig."
|
||||
"get_supported_openai_params raises ValueError (list.remove on "
|
||||
"a base list that already dropped response_format for this "
|
||||
"model name) on every request; v1 raises its own error"
|
||||
)
|
||||
allowed = _BASE_LIST if supports_tools else _BASE_LIST - _FUNCTION_CALLING_KEYS
|
||||
return unsupported_against(
|
||||
request,
|
||||
provider="together_ai",
|
||||
|
||||
@@ -13,7 +13,7 @@ from __future__ import annotations
|
||||
|
||||
from ...ir import ChatRequest
|
||||
|
||||
_RESPONSE_FORMAT_UNSUPPORTED_MODELS = ("gpt-4", "gpt-3.5-turbo-16k")
|
||||
RESPONSE_FORMAT_UNSUPPORTED_MODELS = ("gpt-4", "gpt-3.5-turbo-16k")
|
||||
"""v1's get_supported_openai_params excludes response_format for exactly
|
||||
these two model names (gpt_transformation.py:172-175) and then DROPS or
|
||||
RAISES on it in get_optional_params; fail closed instead of re-deriving the
|
||||
@@ -52,7 +52,7 @@ def unsupported_params(request: ChatRequest) -> str | None:
|
||||
def unsupported_response_format(request: ChatRequest) -> str | None:
|
||||
if request.response_format.is_none():
|
||||
return None
|
||||
if request.model in _RESPONSE_FORMAT_UNSUPPORTED_MODELS:
|
||||
if request.model in RESPONSE_FORMAT_UNSUPPORTED_MODELS:
|
||||
return (
|
||||
f"response_format on {request.model}: outside v1's supported set "
|
||||
"(gpt_transformation.py:172-175); v1 raises or drops it"
|
||||
|
||||
@@ -297,6 +297,20 @@ def _compat_sdk_rows(lines: list) -> int:
|
||||
failures += 0 if ok else 1
|
||||
label = "FALLBACK (v1 raises UnsupportedParamsError)" if ok else "DIVERGENT"
|
||||
lines.append(f"- {label}: {name} ({reason})")
|
||||
for name in sorted(
|
||||
k for k in req.V1_RAISES_VALUE_ERROR if k.startswith(f"{provider}:")
|
||||
):
|
||||
p, case, reason = req.V1_RAISES_VALUE_ERROR[name]
|
||||
result = req._v2(p, case)
|
||||
try:
|
||||
corpus.run_v1_request_transform(p, case)
|
||||
raised = False
|
||||
except ValueError as err:
|
||||
raised = type(err) is ValueError
|
||||
ok = result.is_error() and reason in result.error.summary and raised
|
||||
failures += 0 if ok else 1
|
||||
label = "FALLBACK (v1 raises ValueError)" if ok else "DIVERGENT"
|
||||
lines.append(f"- {label}: {name} ({reason})")
|
||||
for name in sorted(
|
||||
k for k in req.EXPECTED_FALLBACKS if k.startswith(f"{provider}:")
|
||||
):
|
||||
|
||||
@@ -159,6 +159,40 @@ V1_RAISES.update(
|
||||
}
|
||||
)
|
||||
|
||||
# Rows where v1 CRASHES with a bare ValueError BEFORE any param gate:
|
||||
# together's non-fc fork runs list.remove("response_format") on a base list
|
||||
# that already dropped the key for these model names, inside the
|
||||
# get_supported_openai_params call _check_valid_arg makes on EVERY request
|
||||
# (verifier-wave1a F2). v2 must fall back typed on every shape, plain text
|
||||
# included, so v1 raises its own error instead of v2 serving what v1 crashes
|
||||
# on.
|
||||
V1_RAISES_VALUE_ERROR = {
|
||||
"together_ai:plain_text_on_gpt4_name": (
|
||||
"together_ai",
|
||||
{"model": "gpt-4", "messages": _USER},
|
||||
"ValueError",
|
||||
),
|
||||
"together_ai:plain_text_on_gpt35_16k_name": (
|
||||
"together_ai",
|
||||
{"model": "gpt-3.5-turbo-16k", "messages": _USER},
|
||||
"ValueError",
|
||||
),
|
||||
"together_ai:temperature_only_on_gpt4_name": (
|
||||
"together_ai",
|
||||
{"model": "gpt-4", "temperature": 0.2, "messages": _USER},
|
||||
"ValueError",
|
||||
),
|
||||
"together_ai:response_format_on_gpt4_name": (
|
||||
"together_ai",
|
||||
{
|
||||
"model": "gpt-4",
|
||||
"response_format": {"type": "json_object"},
|
||||
"messages": _USER,
|
||||
},
|
||||
"ValueError",
|
||||
),
|
||||
}
|
||||
|
||||
# Typed fallbacks where v1 SERVES the request (v1 is not invoked: the seam
|
||||
# routes these to v1 untouched). Shared raw-guard/parse shapes per provider
|
||||
# plus the provider-specific ones.
|
||||
@@ -296,6 +330,20 @@ def test_v1_raise_rows_fall_back_typed(name: str) -> None:
|
||||
run_v1_request_transform(provider, case)
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", sorted(V1_RAISES_VALUE_ERROR))
|
||||
def test_v1_value_error_rows_fall_back_typed(name: str) -> None:
|
||||
"""The together gpt-4-name corner: v1 crashes with a bare ValueError on
|
||||
EVERY request for these names (not UnsupportedParamsError), asserted
|
||||
in-process; v2 serving any of them would be a parity break."""
|
||||
provider, case, reason_fragment = V1_RAISES_VALUE_ERROR[name]
|
||||
result = _v2(provider, case)
|
||||
assert result.is_error(), f"{name} unexpectedly translated: {result.ok!r}"
|
||||
assert reason_fragment in result.error.summary, result.error.summary
|
||||
with pytest.raises(ValueError) as excinfo:
|
||||
run_v1_request_transform(provider, case)
|
||||
assert type(excinfo.value) is ValueError, excinfo.value
|
||||
|
||||
|
||||
@pytest.mark.parametrize("name", sorted(EXPECTED_FALLBACKS))
|
||||
def test_unsupported_shape_is_a_typed_fallback(name: str) -> None:
|
||||
provider, case, reason_fragment = EXPECTED_FALLBACKS[name]
|
||||
|
||||
Reference in New Issue
Block a user