Repository: pydantic/pydantic-ai
File: pydantic_ai_slim/pydantic_ai/_agent_graph.py (Python)

Code before:
                f'The `return_value` of tool {tool_call.tool_name!r} contains invalid nested `MultiModalContentTypes` objects. '
                f'Please use `content` instead.'
            )

        tool_part.content = tool_result.return_value  # type: ignore
        tool_part.metadata = tool_result.metadata
        if tool_result.content:
            user_parts.append(
                _messages.UserPromptPart(
                    content=tool_result.content,
                    part_kind='user-prompt',
                )
            )
    else:

        def process_content(content: Any) -> Any:
            if isinstance(content, _messages.ToolReturn):
                raise exceptions.UserError(
                    f'The return value of tool {tool_call.tool_name!r} contains invalid nested `ToolReturn` objects. '
                    f'`ToolReturn` should be used directly.'
                )
            elif isinstance(content, _messages.MultiModalContentTypes):
                if isinstance(content, _messages.BinaryContent):
                    identifier = content.identifier or multi_modal_content_identifier(content.data)
                else:
                    identifier = content.identifier or multi_modal_content_identifier(content.url)

                user_parts.append(
                    _messages.UserPromptPart(
                        content=[f'This is file {identifier}:', content],
                        part_kind='user-prompt',
                    )
                )
                return f'See file {identifier}'

            return content

        if isinstance(tool_result, list):
            contents = cast(list[Any], tool_result)
            tool_part.content = [process_content(content) for content in contents]
        else:
            tool_part.content = process_content(tool_result)

    return (tool_part, user_parts)


@dataclasses.dataclass
class _RunMessages:
    messages: list[_messages.ModelMessage]
    used: bool = False


Review comment (refactor):
We can move this to an `if content.identifier:` branch before the current 2, so we can assume we need to generate it here

Code after:
                'Tool call results need to be provided for all deferred tool calls. '
                f'Expected: {tool_call_ids_to_run}, got: {result_tool_call_ids}'
            )

    deferred_calls: dict[Literal['external', 'unapproved'], list[_messages.ToolCallPart]] = defaultdict(list)

    if calls_to_run:
        async for event in _call_tools(
            tool_manager,
            calls_to_run,
            deferred_tool_results,
            ctx.deps.tracer,
            output_parts,
            deferred_calls,
        ):
            yield event

    # Finally, we handle deferred tool calls (unless they were already included in the run because results were provided)
    if not deferred_tool_results:
        if final_result:
            for call in [*tool_calls_by_kind['external'], *tool_calls_by_kind['unapproved']]:
                output_parts.append(
                    _messages.ToolReturnPart(
                        tool_name=call.tool_name,
                        content='Tool not executed - a final result was already processed.',
                        tool_call_id=call.tool_call_id,
                    )
                )
        else:
            for call in tool_calls_by_kind['external']:
                deferred_calls['external'].append(call)
                yield _messages.FunctionToolCallEvent(call)

            for call in tool_calls_by_kind['unapproved']:
                deferred_calls['unapproved'].append(call)
                yield _messages.FunctionToolCallEvent(call)

    if not final_result and deferred_calls:
        if not ctx.deps.output_schema.allows_deferred_tools:
            raise exceptions.UserError(
                'A deferred tool call was present, but `DeferredToolRequests` is not among output types. To resolve this, add `DeferredToolRequests` to the list of output types for this agent.'
            )
        deferred_tool_requests = _output.DeferredToolRequests(
            calls=deferred_calls['external'],
            approvals=deferred_calls['unapproved'],
        )

        final_result = result.FinalResult(cast(NodeRunEndT, deferred_tool_requests), None, None)

    if final_result:
        output_final_result.append(final_result)