Skip to content

Commit e577329

Browse files
committed
fix: merge reasoning_details to preserve signatures
1 parent 8dfddbf commit e577329

1 file changed

Lines changed: 80 additions & 23 deletions

File tree

backend/open_webui/utils/middleware.py

Lines changed: 80 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -157,6 +157,40 @@ def output_id(prefix: str) -> str:
157157
return f'{prefix}_{uuid4().hex[:24]}'
158158

159159

160+
def merge_reasoning_details(details: list, chunk) -> list:
161+
"""Merge streamed reasoning_details fragments in place, keyed by index.
162+
163+
Concatenates text/summary for matching indexes and overwrites other fields
164+
(e.g. signature) so signed/encrypted blocks stay intact instead of
165+
fragmenting across chunks.
166+
"""
167+
items = (
168+
chunk if isinstance(chunk, list) else ([chunk] if isinstance(chunk, dict) else [])
169+
)
170+
for item in items:
171+
if not isinstance(item, dict):
172+
continue
173+
index = item.get('index')
174+
existing = (
175+
next((e for e in details if e.get('index') == index), None)
176+
if isinstance(index, int) and index >= 0
177+
else None
178+
)
179+
if existing is None:
180+
entry = dict(item)
181+
entry.setdefault('type', 'reasoning.text')
182+
details.append(entry)
183+
continue
184+
for key, value in item.items():
185+
if key in ('text', 'summary'):
186+
if isinstance(value, str):
187+
base = existing.get(key)
188+
existing[key] = (base + value) if isinstance(base, str) else value
189+
else:
190+
existing[key] = value
191+
return details
192+
193+
160194
def _split_tool_calls(
161195
tool_calls: list[dict],
162196
) -> list[dict]:
@@ -4251,7 +4285,7 @@ async def queue_pending_delta_data(delta_data: dict, delta_type: str):
42514285
reasoning_content = ''
42524286
reasoning_details = None
42534287

4254-
if reasoning_content or reasoning_details:
4288+
if reasoning_content:
42554289
if not output or output[-1].get('type') != 'reasoning':
42564290
reasoning_item = {
42574291
'type': 'reasoning',
@@ -4268,30 +4302,53 @@ async def queue_pending_delta_data(delta_data: dict, delta_type: str):
42684302
else:
42694303
reasoning_item = output[-1]
42704304

4271-
if reasoning_content:
4272-
# Append to reasoning content
4273-
parts = reasoning_item.get('content', [])
4274-
if parts and parts[-1].get('type') == 'output_text':
4275-
parts[-1]['text'] += reasoning_content
4276-
else:
4277-
reasoning_item['content'] = [
4278-
{
4279-
'type': 'output_text',
4280-
'text': reasoning_content,
4281-
}
4282-
]
4305+
# Append to reasoning content
4306+
parts = reasoning_item.get('content', [])
4307+
if parts and parts[-1].get('type') == 'output_text':
4308+
parts[-1]['text'] += reasoning_content
4309+
else:
4310+
reasoning_item['content'] = [
4311+
{
4312+
'type': 'output_text',
4313+
'text': reasoning_content,
4314+
}
4315+
]
42834316

4284-
data = {
4285-
'output': full_output(),
4317+
data = {
4318+
'output': full_output(),
4319+
}
4320+
delta_type = 'content'
4321+
4322+
if reasoning_details:
4323+
# Keep signatures attached to their reasoning text
4324+
# (they may stream later); create an item if encrypted.
4325+
reasoning_target = next(
4326+
(
4327+
item
4328+
for item in reversed(output)
4329+
if item.get('type') == 'reasoning'
4330+
),
4331+
None,
4332+
)
4333+
if reasoning_target is None:
4334+
reasoning_target = {
4335+
'type': 'reasoning',
4336+
'id': output_id('r'),
4337+
'status': 'in_progress',
4338+
'start_tag': '<think>',
4339+
'end_tag': '</think>',
4340+
'attributes': {'type': 'reasoning_content'},
4341+
'content': [],
4342+
'summary': None,
4343+
'started_at': time.time(),
42864344
}
4287-
delta_type = 'content'
4288-
4289-
if reasoning_details:
4290-
reasoning_item.setdefault('reasoning_details', []).extend(
4291-
reasoning_details
4292-
if isinstance(reasoning_details, list)
4293-
else [reasoning_details]
4294-
)
4345+
output.append(reasoning_target)
4346+
merge_reasoning_details(
4347+
reasoning_target.setdefault(
4348+
'reasoning_details', []
4349+
),
4350+
reasoning_details,
4351+
)
42954352

42964353
if value:
42974354
if (

0 commit comments

Comments
 (0)