Skip to content

Commit de6c95f

Browse files
committed
models - litellm - stop reasoning
1 parent eef11cc commit de6c95f

File tree

4 files changed

+70
-15
lines changed

4 files changed

+70
-15
lines changed

src/strands/models/litellm.py

Lines changed: 21 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -139,22 +139,22 @@ async def stream(
139139

140140
logger.debug("got response from model")
141141
yield self.format_chunk({"chunk_type": "message_start"})
142-
yield self.format_chunk({"chunk_type": "content_start", "data_type": "text"})
143142

144143
tool_calls: dict[int, list[Any]] = {}
144+
started_reasoning = False
145+
started_text = False
145146

146147
async for event in response:
147148
# Defensive: skip events with empty or missing choices
148149
if not getattr(event, "choices", None):
149150
continue
150151
choice = event.choices[0]
151152

152-
if choice.delta.content:
153-
yield self.format_chunk(
154-
{"chunk_type": "content_delta", "data_type": "text", "data": choice.delta.content}
155-
)
156-
157153
if hasattr(choice.delta, "reasoning_content") and choice.delta.reasoning_content:
154+
if not started_reasoning:
155+
yield self.format_chunk({"chunk_type": "content_start", "data_type": "reasoning_content"})
156+
started_reasoning = True
157+
158158
yield self.format_chunk(
159159
{
160160
"chunk_type": "content_delta",
@@ -163,14 +163,27 @@ async def stream(
163163
}
164164
)
165165

166+
if choice.delta.content:
167+
if started_reasoning:
168+
yield self.format_chunk({"chunk_type": "content_stop", "data_type": "reasoning_content"})
169+
started_reasoning = False
170+
171+
if not started_text:
172+
yield self.format_chunk({"chunk_type": "content_start", "data_type": "text"})
173+
started_text = True
174+
175+
yield self.format_chunk(
176+
{"chunk_type": "content_delta", "data_type": "text", "data": choice.delta.content}
177+
)
178+
166179
for tool_call in choice.delta.tool_calls or []:
167180
tool_calls.setdefault(tool_call.index, []).append(tool_call)
168181

169182
if choice.finish_reason:
183+
if started_text:
184+
yield self.format_chunk({"chunk_type": "content_stop", "data_type": "text"})
170185
break
171186

172-
yield self.format_chunk({"chunk_type": "content_stop", "data_type": "text"})
173-
174187
for tool_deltas in tool_calls.values():
175188
yield self.format_chunk({"chunk_type": "content_start", "data_type": "tool", "data": tool_deltas[0]})
176189

tests/strands/models/test_litellm.py

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -182,6 +182,8 @@ async def test_stream(litellm_acompletion, api_key, model_id, model, agenerator,
182182
{"messageStart": {"role": "assistant"}},
183183
{"contentBlockStart": {"start": {}}},
184184
{"contentBlockDelta": {"delta": {"reasoningContent": {"text": "\nI'm thinking"}}}},
185+
{"contentBlockStop": {}},
186+
{"contentBlockStart": {"start": {}}},
185187
{"contentBlockDelta": {"delta": {"text": "I'll calculate"}}},
186188
{"contentBlockDelta": {"delta": {"text": "that for you"}}},
187189
{"contentBlockStop": {}},
@@ -251,8 +253,6 @@ async def test_stream_empty(litellm_acompletion, api_key, model_id, model, agene
251253
tru_events = await alist(response)
252254
exp_events = [
253255
{"messageStart": {"role": "assistant"}},
254-
{"contentBlockStart": {"start": {}}},
255-
{"contentBlockStop": {}},
256256
{"messageStop": {"stopReason": "end_turn"}},
257257
]
258258

tests_integ/models/providers.py

Lines changed: 34 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -67,10 +67,26 @@ def __init__(self):
6767
"api_key": os.getenv("ANTHROPIC_API_KEY"),
6868
},
6969
model_id="claude-3-7-sonnet-20250219",
70-
max_tokens=512,
70+
max_tokens=2048,
71+
params={
72+
"thinking": {
73+
"type": "enabled",
74+
"budget_tokens": 1024,
75+
},
76+
},
77+
),
78+
)
79+
bedrock = ProviderInfo(
80+
id="bedrock",
81+
factory=lambda: BedrockModel(
82+
additional_request_fields={
83+
"thinking": {
84+
"type": "enabled",
85+
"budget_tokens": 1024,
86+
},
87+
},
7188
),
7289
)
73-
bedrock = ProviderInfo(id="bedrock", factory=lambda: BedrockModel())
7490
cohere = ProviderInfo(
7591
id="cohere",
7692
environment_variable="COHERE_API_KEY",
@@ -84,7 +100,16 @@ def __init__(self):
84100
),
85101
)
86102
litellm = ProviderInfo(
87-
id="litellm", factory=lambda: LiteLLMModel(model_id="bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0")
103+
id="litellm",
104+
factory=lambda: LiteLLMModel(
105+
model_id="bedrock/us.anthropic.claude-3-7-sonnet-20250219-v1:0",
106+
params={
107+
"thinking": {
108+
"budget_tokens": 1024,
109+
"type": "enabled",
110+
},
111+
},
112+
),
88113
)
89114
llama = ProviderInfo(
90115
id="llama",
@@ -133,7 +158,12 @@ def __init__(self):
133158
factory=lambda: GeminiModel(
134159
api_key=os.getenv("GOOGLE_API_KEY"),
135160
model_id="gemini-2.5-flash",
136-
params={"temperature": 0.7},
161+
params={
162+
"temperature": 0.7,
163+
"thinking_config": {
164+
"include_thoughts": True,
165+
},
166+
},
137167
),
138168
)
139169

tests_integ/models/test_conformance.py

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1+
import json
12
from unittest import SkipTest
23

34
import pytest
45
from pydantic import BaseModel
56

67
from strands import Agent
78
from strands.models import Model
8-
from tests_integ.models.providers import ProviderInfo, all_providers, cohere, llama, mistral
9+
from tests_integ.models.providers import ProviderInfo, all_providers, cohere, gemini, llama, mistral, openai, writer
910

1011

1112
def get_models():
@@ -60,3 +61,14 @@ class Weather(BaseModel):
6061

6162
assert len(result.time) > 0
6263
assert len(result.weather) > 0
64+
65+
66+
def test_stream_reasoning(skip_for, model):
67+
skip_for([cohere, gemini, llama, mistral, openai, writer], "reasoning is not supported")
68+
69+
70+
agent = Agent(model)
71+
result = agent("Please reason about the equation 2+2.")
72+
73+
assert "reasoningContent" in result.message["content"][0]
74+
assert result.message["content"][0]["reasoningContent"]["reasoningText"]["text"]

0 commit comments

Comments
 (0)