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

/v1/decisions accepted up to 200 questions, but the System One request it translates to takes at most 128, so 129 to 200 questions failed with an internal validation error. Both now share MAX_DECISION_QUESTIONS.
This commit is contained in:
mateo-berri 2026-10-07 16:20:31 -07:00
parent 29e715473c
commit 7ef132947a
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),
]
@ -203,7 +204,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))