From 26411eb1d274441fb83dbf9eff58a4316696c75b Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 15 Sep 2025 19:26:29 -0400 Subject: [PATCH 1/3] fix: litellm structured_output test with more descriptive model --- src/strands/models/litellm.py | 7 ++++--- tests_integ/models/test_model_litellm.py | 14 ++++++++++---- 2 files changed, 14 insertions(+), 7 deletions(-) diff --git a/src/strands/models/litellm.py b/src/strands/models/litellm.py index 6bcc1359e..fbfa3a79e 100644 --- a/src/strands/models/litellm.py +++ b/src/strands/models/litellm.py @@ -204,6 +204,9 @@ async def structured_output( Yields: Model events with the last being the structured output. """ + if not supports_response_schema(self.get_config()["model_id"]): + raise ValueError("Model does not support response_format") + response = await litellm.acompletion( **self.client_args, model=self.get_config()["model_id"], @@ -211,11 +214,9 @@ async def structured_output( response_format=output_model, ) - if not supports_response_schema(self.get_config()["model_id"]): - raise ValueError("Model does not support response_format") if len(response.choices) > 1: raise ValueError("Multiple choices found in the response.") - + # Find the first choice with tool_calls for choice in response.choices: if choice.finish_reason == "tool_calls": diff --git a/tests_integ/models/test_model_litellm.py b/tests_integ/models/test_model_litellm.py index efdd6a5ed..0385fa352 100644 --- a/tests_integ/models/test_model_litellm.py +++ b/tests_integ/models/test_model_litellm.py @@ -43,16 +43,22 @@ class Weather(pydantic.BaseModel): @pytest.fixture def yellow_color(): class Color(pydantic.BaseModel): - """Describes a color.""" + """Describes a color with its basic name. + + Used to extract and normalize color names from text or images. + The color name should be a simple, common color like 'red', 'blue', 'yellow', etc. + """ - name: str + simple_color_name: str = pydantic.Field( + description="The basic color name (e.g., 'red', 'blue', 'yellow', 'green', 'orange', 'purple', 'black', 'white')" + ) - @pydantic.field_validator("name", mode="after") + @pydantic.field_validator("simple_color_name", mode="after") @classmethod def lower(_, value): return value.lower() - return Color(name="yellow") + return Color(simple_color_name="yellow") def test_agent_invoke(agent): From ce0ddb4ebe198189e45722c006a4bb1d5abd8856 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 15 Sep 2025 19:32:22 -0400 Subject: [PATCH 2/3] formatting --- src/strands/models/litellm.py | 2 +- tests/strands/models/test_litellm.py | 12 ++++++++++++ tests_integ/models/test_model_litellm.py | 4 ++-- 3 files changed, 15 insertions(+), 3 deletions(-) diff --git a/src/strands/models/litellm.py b/src/strands/models/litellm.py index fbfa3a79e..17ededa14 100644 --- a/src/strands/models/litellm.py +++ b/src/strands/models/litellm.py @@ -216,7 +216,7 @@ async def structured_output( if len(response.choices) > 1: raise ValueError("Multiple choices found in the response.") - + # Find the first choice with tool_calls for choice in response.choices: if choice.finish_reason == "tool_calls": diff --git a/tests/strands/models/test_litellm.py b/tests/strands/models/test_litellm.py index f345ba003..bc81fc819 100644 --- a/tests/strands/models/test_litellm.py +++ b/tests/strands/models/test_litellm.py @@ -289,6 +289,18 @@ async def test_structured_output(litellm_acompletion, model, test_output_model_c assert tru_result == exp_result +@pytest.mark.asyncio +async def test_structured_output_unsupported_model(litellm_acompletion, model, test_output_model_cls): + messages = [{"role": "user", "content": [{"text": "Generate a person"}]}] + + with unittest.mock.patch.object(strands.models.litellm, "supports_response_schema", return_value=False): + with pytest.raises(ValueError, match="Model does not support response_format"): + stream = model.structured_output(test_output_model_cls, messages) + await stream.__anext__() + + litellm_acompletion.assert_not_called() + + def test_config_validation_warns_on_unknown_keys(litellm_acompletion, captured_warnings): """Test that unknown config keys emit a warning.""" LiteLLMModel(client_args={"api_key": "test"}, model_id="test-model", invalid_param="test") diff --git a/tests_integ/models/test_model_litellm.py b/tests_integ/models/test_model_litellm.py index 0385fa352..9ef6054ee 100644 --- a/tests_integ/models/test_model_litellm.py +++ b/tests_integ/models/test_model_litellm.py @@ -44,13 +44,13 @@ class Weather(pydantic.BaseModel): def yellow_color(): class Color(pydantic.BaseModel): """Describes a color with its basic name. - + Used to extract and normalize color names from text or images. The color name should be a simple, common color like 'red', 'blue', 'yellow', etc. """ simple_color_name: str = pydantic.Field( - description="The basic color name (e.g., 'red', 'blue', 'yellow', 'green', 'orange', 'purple', 'black', 'white')" + description="The basic color name (e.g., 'red', 'blue', 'yellow', 'green', 'orange', 'purple')" ) @pydantic.field_validator("simple_color_name", mode="after") From c1337dccc1ad5ec50508d216fa4178ae20449716 Mon Sep 17 00:00:00 2001 From: Dean Schmigelski Date: Mon, 15 Sep 2025 19:50:07 -0400 Subject: [PATCH 3/3] enhance weather description --- tests_integ/models/test_model_litellm.py | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/tests_integ/models/test_model_litellm.py b/tests_integ/models/test_model_litellm.py index 9ef6054ee..6cfdd3038 100644 --- a/tests_integ/models/test_model_litellm.py +++ b/tests_integ/models/test_model_litellm.py @@ -34,8 +34,8 @@ def weather(): class Weather(pydantic.BaseModel): """Extracts the time and weather from the user's message with the exact strings.""" - time: str - weather: str + time: str = pydantic.Field(description="The time in HH:MM format (e.g., '12:00', '09:30')") + weather: str = pydantic.Field(description="The weather condition (e.g., 'sunny', 'rainy', 'cloudy')") return Weather(time="12:00", weather="sunny")