fix(decisions): cap OpenAI-format questions at the System One limit

Backport of the #45184 follow-up to stable/1.104.x.
Cherry-picked from 7ef132947a (litellm_systemone_endpoint).
This commit is contained in:
mateo-berri 2026-10-07 16:27:22 -07:00
parent 77d57c3948
commit 5f4588dec4
2 changed files with 35 additions and 2 deletions

View file

@ -8,6 +8,7 @@ from litellm.types.llms.base import LiteLLMPydanticObjectBase
DecisionsJSON: TypeAlias = str | Mapping[str, object] | Sequence[object]
NoulCriteria: TypeAlias = Mapping[Literal["true", "false"], DecisionsJSON | None]
MAX_DECISION_QUESTIONS: Final = 128
class NoulQuestion(LiteLLMPydanticObjectBase):
@ -47,7 +48,7 @@ DecisionQuestion: TypeAlias = Annotated[
DecisionQuestionMap: TypeAlias = Annotated[
Mapping[Annotated[str, Field(min_length=1)], DecisionQuestion],
Field(min_length=1, max_length=128),
Field(min_length=1, max_length=MAX_DECISION_QUESTIONS),
]
@ -199,7 +200,7 @@ OpenAIDecisionQuestion: TypeAlias = Annotated[
class OpenAIDecisionRequestBody(LiteLLMPydanticObjectBase):
input: str | Sequence[OpenAIDecisionInputMessage]
questions: Annotated[Sequence[OpenAIDecisionQuestion], Field(min_length=1, max_length=200)]
questions: Annotated[Sequence[OpenAIDecisionQuestion], Field(min_length=1, max_length=MAX_DECISION_QUESTIONS)]
safety_identifier: str | None = None
model_config = ConfigDict(extra="allow", frozen=True)

View file

@ -0,0 +1,32 @@
from collections.abc import Mapping
from typing import Final
import pytest
from pydantic import TypeAdapter, ValidationError
from litellm.decisions.openai_transformation import to_systemone_request
from litellm.types.decisions import MAX_DECISION_QUESTIONS, DecisionsRequestBody, OpenAIDecisionRequestBody
_OPENAI_BODY: Final[TypeAdapter[OpenAIDecisionRequestBody]] = TypeAdapter(OpenAIDecisionRequestBody)
_SYSTEMONE_BODY: Final[TypeAdapter[DecisionsRequestBody]] = TypeAdapter(DecisionsRequestBody)
def _openai_request(question_count: int) -> Mapping[str, object]:
return {
"model": "decider",
"input": "The package arrived with a broken screen.",
"questions": [{"type": "predicate", "instructions": f"Question {index}?"} for index in range(question_count)],
}
def test_the_largest_openai_request_accepted_translates_to_a_valid_systemone_request() -> None:
raw: Final = _openai_request(MAX_DECISION_QUESTIONS)
translated: Final = _SYSTEMONE_BODY.validate_python(to_systemone_request(raw, _OPENAI_BODY.validate_python(raw)))
assert len(translated.questions) == MAX_DECISION_QUESTIONS
def test_an_openai_request_with_more_questions_than_systemone_takes_is_rejected_before_translation() -> None:
with pytest.raises(ValidationError, match="questions"):
_OPENAI_BODY.validate_python(_openai_request(MAX_DECISION_QUESTIONS + 1))