2020from ..responses import (
2121 convert_completion_messages_to_responses_items ,
2222 convert_responses_items_to_completion_messages ,
23+ make_reasoning_item ,
2324)
2425from ..types import AgentCapability
2526
@@ -373,13 +374,23 @@ def _has_any_image(msgs: List[Dict[str, Any]]) -> bool:
373374 if _on_usage :
374375 await _on_usage (usage )
375376
376- # Parse tool call from text; then convert to responses items via fake tool_calls
377+ # Extract response data
377378 resp_dict = response .model_dump () # type: ignore
378379 choice = (resp_dict .get ("choices" ) or [{}])[0 ]
379- content_text = ((choice .get ("message" ) or {}).get ("content" )) or ""
380- tool_call = _parse_tool_call_from_text (content_text )
380+ message = choice .get ("message" ) or {}
381+ content_text = message .get ("content" ) or ""
382+ tool_calls_array = message .get ("tool_calls" ) or []
383+ reasoning_text = message .get ("reasoning" ) or ""
381384
382385 output_items : List [Dict [str , Any ]] = []
386+
387+ # Add reasoning if present (Ollama Cloud format)
388+ if reasoning_text :
389+ output_items .append (make_reasoning_item (reasoning_text ))
390+
391+ # Priority 1: Try to parse tool call from content text (OpenRouter format)
392+ tool_call = _parse_tool_call_from_text (content_text )
393+
383394 if tool_call and isinstance (tool_call , dict ):
384395 fn_name = tool_call .get ("name" ) or "computer"
385396 raw_args = tool_call .get ("arguments" ) or {}
@@ -405,8 +416,50 @@ def _has_any_image(msgs: List[Dict[str, Any]]) -> bool:
405416 ],
406417 }
407418 output_items .extend (convert_completion_messages_to_responses_items ([fake_cm ]))
419+ elif tool_calls_array :
420+ # Priority 2: Use tool_calls field if present (Ollama Cloud format)
421+ # Process and unnormalize coordinates in tool calls
422+ processed_tool_calls = []
423+ for tc in tool_calls_array :
424+ function = tc .get ("function" , {})
425+ fn_name = function .get ("name" , "computer" )
426+ args_str = function .get ("arguments" , "{}" )
427+
428+ try :
429+ args = json .loads (args_str )
430+
431+ # Unnormalize coordinates if present
432+ if "coordinate" in args and last_rw is not None and last_rh is not None :
433+ args = await _unnormalize_coordinate (args , (last_rw , last_rh ))
434+
435+ # Convert Qwen format to Computer Calls format if this is a computer tool
436+ if fn_name == "computer" :
437+ converted_action = convert_qwen_tool_args_to_computer_action (args )
438+ if converted_action :
439+ args = converted_action
440+
441+ processed_tool_calls .append (
442+ {
443+ "type" : tc .get ("type" , "function" ),
444+ "id" : tc .get ("id" , "call_0" ),
445+ "function" : {
446+ "name" : fn_name ,
447+ "arguments" : json .dumps (args ),
448+ },
449+ }
450+ )
451+ except json .JSONDecodeError :
452+ # Keep original if parsing fails
453+ processed_tool_calls .append (tc )
454+
455+ fake_cm = {
456+ "role" : "assistant" ,
457+ "content" : content_text if content_text else "" ,
458+ "tool_calls" : processed_tool_calls ,
459+ }
460+ output_items .extend (convert_completion_messages_to_responses_items ([fake_cm ]))
408461 else :
409- # Fallback: just return assistant text
462+ # No tool calls found in either format, return text response
410463 fake_cm = {"role" : "assistant" , "content" : content_text }
411464 output_items .extend (convert_completion_messages_to_responses_items ([fake_cm ]))
412465
0 commit comments