Skip to content

Commit 40f5b3d

Browse files
committed
refac
1 parent 869cf9e commit 40f5b3d

1 file changed

Lines changed: 124 additions & 8 deletions

File tree

backend/open_webui/utils/anthropic.py

Lines changed: 124 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -181,17 +181,133 @@ def convert_anthropic_to_openai_payload(anthropic_payload: dict) -> dict:
181181
)
182182
elif block_type == 'tool_result':
183183
# Tool results become separate tool messages in OpenAI format
184-
tool_content = block.get('content', '')
185-
if isinstance(tool_content, list):
186-
tool_text_parts = []
187-
for tc in tool_content:
188-
if isinstance(tc, dict) and tc.get('type') == 'text':
189-
tool_text_parts.append(tc.get('text', ''))
190-
tool_content = '\n'.join(tool_text_parts)
184+
tool_result_content = block.get('content', '')
185+
tool_content: str | list = ''
186+
187+
if isinstance(tool_result_content, str):
188+
tool_content = tool_result_content
189+
elif isinstance(tool_result_content, list):
190+
# Build a multimodal content array to preserve
191+
# images and other non-text content types.
192+
converted_parts = []
193+
for content_block in tool_result_content:
194+
if not isinstance(content_block, dict):
195+
continue
196+
content_type = content_block.get('type', 'text')
197+
198+
if content_type == 'text':
199+
converted_parts.append(
200+
{
201+
'type': 'text',
202+
'text': content_block.get('text', ''),
203+
}
204+
)
205+
elif content_type == 'image':
206+
source = content_block.get('source', {})
207+
if source.get('type') == 'base64':
208+
media_type = source.get(
209+
'media_type', 'image/png'
210+
)
211+
data = source.get('data', '')
212+
converted_parts.append(
213+
{
214+
'type': 'image_url',
215+
'image_url': {
216+
'url': f'data:{media_type};base64,{data}',
217+
},
218+
}
219+
)
220+
elif source.get('type') == 'url':
221+
converted_parts.append(
222+
{
223+
'type': 'image_url',
224+
'image_url': {
225+
'url': source.get('url', ''),
226+
},
227+
}
228+
)
229+
elif content_type == 'document':
230+
# Documents have no direct OpenAI equivalent;
231+
# convert to a text representation.
232+
document_source = content_block.get(
233+
'source', {}
234+
)
235+
document_title = content_block.get(
236+
'title', 'Document'
237+
)
238+
document_context = content_block.get(
239+
'context', ''
240+
)
241+
document_text = (
242+
f'[Document: {document_title}]'
243+
)
244+
if document_context:
245+
document_text += f'\n{document_context}'
246+
if (
247+
document_source.get('type') == 'text'
248+
and document_source.get('data')
249+
):
250+
document_text += (
251+
f'\n{document_source["data"]}'
252+
)
253+
converted_parts.append(
254+
{'type': 'text', 'text': document_text}
255+
)
256+
elif content_type == 'search_result':
257+
# Convert search results to a text
258+
# representation with source attribution.
259+
search_title = content_block.get('title', '')
260+
search_url = content_block.get('source', '')
261+
search_content_blocks = content_block.get(
262+
'content', []
263+
)
264+
search_texts = []
265+
for search_block in search_content_blocks:
266+
if (
267+
isinstance(search_block, dict)
268+
and search_block.get('type') == 'text'
269+
):
270+
search_texts.append(
271+
search_block.get('text', '')
272+
)
273+
search_body = '\n'.join(search_texts)
274+
search_text = (
275+
f'[Search Result: {search_title}]'
276+
)
277+
if search_url:
278+
search_text += f'\nSource: {search_url}'
279+
if search_body:
280+
search_text += f'\n{search_body}'
281+
converted_parts.append(
282+
{'type': 'text', 'text': search_text}
283+
)
284+
285+
# Flatten to string when only text parts are present
286+
if all(
287+
part.get('type') == 'text'
288+
for part in converted_parts
289+
):
290+
tool_content = '\n'.join(
291+
part.get('text', '')
292+
for part in converted_parts
293+
)
294+
elif converted_parts:
295+
tool_content = converted_parts
296+
else:
297+
tool_content = ''
191298

192299
# Propagate error status if present
193300
if block.get('is_error'):
194-
tool_content = f'Error: {tool_content}'
301+
if isinstance(tool_content, str):
302+
tool_content = f'Error: {tool_content}'
303+
elif isinstance(tool_content, list):
304+
tool_content.insert(
305+
0,
306+
{
307+
'type': 'text',
308+
'text': 'Error: ',
309+
},
310+
)
195311

196312
messages.append(
197313
{

0 commit comments

Comments
 (0)