Current section
Files
Jump to
Current section
Files
lib/gemini/tools.ex
defmodule Gemini.Tools do
@moduledoc """
High-level facade for tool registration and execution in the Gemini client.
This module provides a convenient interface for developers to register tool
implementations and execute function calls returned by the Gemini API. It
integrates with the ALTAR LATER runtime for robust tool execution.
## Usage
# Register a tool
{:ok, declaration} = Altar.ADM.new_function_declaration(%{
name: "get_weather",
description: "Gets weather for a location",
parameters: %{}
})
:ok = Gemini.Tools.register(declaration, &MyApp.Tools.get_weather/1)
# Execute function calls from API response
function_calls = [%Altar.ADM.FunctionCall{...}]
{:ok, results} = Gemini.Tools.execute_calls(function_calls)
"""
alias Altar.ADM.{FunctionCall, FunctionDeclaration, ToolResult}
alias Altar.LATER.{Registry, Executor}
@registry_name Gemini.Tools.Registry
@doc """
Register a tool implementation with the LATER registry.
- `declaration` is a validated `%Altar.ADM.FunctionDeclaration{}`
- `fun` is an arity-1 function that accepts a map of arguments
Returns `:ok` on success or `{:error, reason}` if registration fails.
"""
@spec register(FunctionDeclaration.t(), (map() -> any())) :: :ok | {:error, term()}
def register(%FunctionDeclaration{} = declaration, fun) when is_function(fun, 1) do
Registry.register_tool(@registry_name, declaration, fun)
end
@doc """
Execute a list of function calls in parallel using the LATER executor.
Takes a list of `%Altar.ADM.FunctionCall{}` structs (typically from a
GenerateContentResponse) and executes them concurrently, returning a list
of `%Altar.ADM.ToolResult{}` structs.
Returns `{:ok, [ToolResult.t()]}` on success. Individual tool failures
are captured in the ToolResult's `is_error` field rather than causing
the entire operation to fail.
"""
@spec execute_calls([FunctionCall.t()]) :: {:ok, [ToolResult.t()]}
def execute_calls(function_calls) when is_list(function_calls) do
results =
function_calls
|> Task.async_stream(
fn call ->
{:ok, result} = Executor.execute_tool(@registry_name, call)
result
end,
max_concurrency: System.schedulers_online(),
timeout: 30_000
)
|> Enum.map(fn
{:ok, result} -> result
{:exit, reason} -> build_error_result("task_exit", reason)
end)
{:ok, results}
end
# Helper to build error results for task failures
defp build_error_result(call_id, reason) do
{:ok, result} =
Altar.ADM.new_tool_result(%{
call_id: call_id,
is_error: true,
content: %{error: "Task execution failed: #{inspect(reason)}"}
})
result
end
end