defmodule AI.ToolExecutor do @moduledoc """ Executes tool calls concurrently. """ @doc """ Executes tool calls in parallel and returns their results in order. """ def execute_parallel(tool_calls, tools, context) do tool_calls |> Task.async_stream( fn tool_call -> execute_one(tool_call, tools, context) end, ordered: true, timeout: :infinity ) |> Enum.map(fn {:ok, result} -> result {:exit, reason} -> raise reason end) end defp execute_one(%{name: name, input: input, id: id}, tools, context) do tool = Map.get(tools, name) if is_nil(tool) do raise AI.Errors.NoSuchToolError, message: "no such tool", tool_name: name end validated = AI.Schema.validate!(tool.input_schema, input, name) if is_nil(tool.execute) do raise AI.Errors.ProviderError, message: "tool has no execute function", provider: :tool, details: %{tool: name} end output = case tool.execute do fun when is_function(fun, 2) -> fun.(validated, %{tool_call_id: id, messages: context[:messages]}) fun when is_function(fun, 1) -> fun.(validated) _ -> raise AI.Errors.ProviderError, message: "tool execute must be a function", provider: :tool, details: %{tool: name} end %{id: id, name: name, output: output, is_error?: false} end end