async def handle_function_calls_live(
    invocation_context: InvocationContext,
    function_call_event: Event,
    tools_dict: dict[str, BaseTool],
) -> Event:
  """Calls the functions and returns the function response event."""
  from ...agents.llm_agent import LlmAgent

  agent = cast(LlmAgent, invocation_context.agent)
  function_calls = function_call_event.get_function_calls()

  function_response_events: list[Event] = []
  for function_call in function_calls:
    tool, tool_context = _get_tool_and_context(
        invocation_context, function_call_event, function_call, tools_dict
    )
    with tracer.start_as_current_span(f'execute_tool {tool.name}'):
      # do not use "args" as the variable name, because it is a reserved keyword
      # in python debugger.
      function_args = function_call.args or {}
      function_response = None
      # # Calls the tool if before_tool_callback does not exist or returns None.
      # if agent.before_tool_callback:
      #   function_response = agent.before_tool_callback(
      #       tool, function_args, tool_context
      #   )
      if agent.before_tool_callback:
        function_response = agent.before_tool_callback(
            tool=tool, args=function_args, tool_context=tool_context
        )
        if inspect.isawaitable(function_response):
          function_response = await function_response

      if not function_response:
        function_response = await _process_function_live_helper(
            tool, tool_context, function_call, function_args, invocation_context
        )

      # Calls after_tool_callback if it exists.
      # if agent.after_tool_callback:
      #   new_response = agent.after_tool_callback(
      #       tool,
      #       function_args,
      #       tool_context,
      #       function_response,
      #   )
      #   if new_response:
      #     function_response = new_response
      if agent.after_tool_callback:
        altered_function_response = agent.after_tool_callback(
            tool=tool,
            args=function_args,
            tool_context=tool_context,
            tool_response=function_response,
        )
        if inspect.isawaitable(altered_function_response):
          altered_function_response = await altered_function_response
        if altered_function_response is not None:
          function_response = altered_function_response

      if tool.is_long_running:
        # Allow async function to return None to not provide function response.
        if not function_response:
          continue

      # Builds the function response event.
      function_response_event = __build_response_event(
          tool, function_response, tool_context, invocation_context
      )
      trace_tool_call(
          tool=tool,
          args=function_args,
          response_event_id=function_response_event.id,
          function_response=function_response,
      )
      function_response_events.append(function_response_event)

  if not function_response_events:
    return None
  merged_event = merge_parallel_function_response_events(
      function_response_events
  )
  if len(function_response_events) > 1:
    # this is needed for debug traces of parallel calls
    # individual response with tool.name is traced in __build_response_event
    # (we drop tool.name from span name here as this is merged event)
    with tracer.start_as_current_span('execute_tool (merged)'):
      trace_merged_tool_calls(
          response_event_id=merged_event.id,
          function_response_event=merged_event,
      )
  return merged_event
Calls the functions and returns the function response event.
src/google/adk/flows/llm_flows/functions.py