Skip to content

Why Answer A is Correct: ToolsToFinalOutputFunction Loop Termination

The Question

When using ToolsToFinalOutputFunction with an agent that has a custom output_type, what determines whether the agent loop continues or terminates?

A. Whether the function returns is_final_output=True regardless of the output type match
B. Whether the final_output matches the agent's output_type AND is_final_output=True

Code Analysis

1. ToolsToFinalOutputResult Dataclass

Python
@dataclass
class ToolsToFinalOutputResult:
    is_final_output: bool
    """Whether this is the final output. If False, the LLM will run again and receive the tool call
    output.
    """

    final_output: Any | None = None
    """The final output. Can be None if `is_final_output` is False, otherwise must match the
    `output_type` of the agent.
    """

Key Point: The docstring says final_output must match the output_type of the agent but this is a documentation requirement, not an enforced runtime check.

2. ToolsToFinalOutputFunction TypeAlias

Python
1
2
3
4
5
6
7
ToolsToFinalOutputFunction: TypeAlias = Callable[
    [RunContextWrapper[TContext], list[FunctionToolResult]],
    MaybeAwaitable[ToolsToFinalOutputResult],
]
"""A function that takes a run context and a list of tool results, and returns a
`ToolsToFinalOutputResult`.
"""

This defines the signature for custom tool use behavior functions that return ToolsToFinalOutputResult.

3. Critical Implementation Logic

Python
# Next, we'll check if the tool use should result in a final output
check_tool_use = await cls._check_for_final_output_from_tools(
    agent=agent,
    tool_results=function_results,
    context_wrapper=context_wrapper,
    config=run_config,
)

if check_tool_use.is_final_output:
    # If the output type is str, then let's just stringify it
    if not agent.output_type or agent.output_type is str:
        check_tool_use.final_output = str(check_tool_use.final_output)

    if check_tool_use.final_output is None:
        logger.error(
            "Model returned a final output of None. Not raising an error because we assume"
            "you know what you're doing."
        )

    return await cls.execute_final_output(
        agent=agent,
        original_input=original_input,
        new_response=new_response,
        pre_step_items=pre_step_items,
        new_step_items=new_step_items,
        final_output=check_tool_use.final_output,
        hooks=hooks,
        context_wrapper=context_wrapper,
    )

Analysis: 1. The loop terminates only if check_tool_use.is_final_output is True. 2. If there's no output_type or it's str, the system stringifies the output. 3. If final_output is None, it logs an error but still terminates. 4. The loop ends by calling execute_final_output().

4. Custom Tool Use Behavior Execution

Python
elif callable(agent.tool_use_behavior):
    if inspect.iscoroutinefunction(agent.tool_use_behavior):
        return await cast(
            Awaitable[ToolsToFinalOutputResult],
            agent.tool_use_behavior(context_wrapper, tool_results),
        )
    else:
        return cast(
            ToolsToFinalOutputResult, agent.tool_use_behavior(context_wrapper, tool_results)
        )

Custom ToolsToFinalOutputFunction returns a ToolsToFinalOutputResult directly, with no additional validation.

Why Answer A Is Correct

  • No Type Validation: There is no runtime type check on final_output from a ToolsToFinalOutputFunction.
  • Single Condition: The system only checks is_final_output to terminate.
  • Documentation vs Implementation: Type matching is suggested in docs, but not enforced.
  • Error Handling: Even if final_output is None or mismatched, the loop still terminates (just logs an error).

Conclusion

The agent loop termination is determined solely by is_final_output=True, with no runtime validation of final_output against the agent's output_type.

Notes

Type validation logic applies only to model responses, not tool function results. This happens in separate code paths when is_final_output is False.