Gemini.Tools.AutomaticFunctionCalling (GeminiEx v0.8.4)

View Source

Implements the Automatic Function Calling (AFC) loop for Gemini.

AFC automatically executes function calls from Gemini responses and continues the conversation until no more function calls are needed or limits are reached.

How It Works

  1. Send initial request to Gemini with tools defined
  2. Check if response contains function calls
  3. If yes, execute function calls against the registry
  4. Build function response content
  5. Send new request with function results
  6. Repeat until no more function calls or max_calls reached

Configuration

Use config/1 to create an AFC configuration:

config = AFC.config(
  max_calls: 10,           # Maximum function calls before stopping
  ignore_call_history: false,  # Whether to track call history
  enabled: true            # Enable/disable AFC
)

Usage with Coordinator

AFC is typically used through the high-level API:

# Define tools
tools = [
  %FunctionDeclaration{
    name: "get_weather",
    description: "Get current weather",
    parameters: %{type: "object", properties: %{"location" => %{type: "string"}}}
  }
]

# Define registry
registry = %{
  "get_weather" => fn args -> WeatherService.get(args["location"]) end
}

# Generate with AFC
{:ok, response} = Gemini.generate(
  "What's the weather in NYC?",
  tools: tools,
  auto_execute_tools: true,
  tool_registry: registry
)

Manual AFC Loop

For more control, you can use the AFC functions directly:

response = initial_response
history = []
call_count = 0
config = AFC.config(max_calls: 5)

{final_response, call_count, history} =
  AFC.loop(response, contents, registry, config, call_count, history, generate_fn)

Summary

Functions

Build content containing function responses for the API.

Create an AFC configuration.

Extract function calls from a Gemini API response.

Extract model content from a response in API-compatible format.

Check if a response contains function calls.

Determine if the AFC loop should continue.

Track function call history.

Types

call_history()

@type call_history() :: [Altar.ADM.FunctionCall.t()]

config()

generate_fn()

@type generate_fn() :: (list(), keyword() -> {:ok, map()} | {:error, term()})

Functions

build_function_response_content(calls, results)

@spec build_function_response_content([Altar.ADM.FunctionCall.t()], [
  Gemini.Tools.Executor.execution_result()
]) :: map()

Build content containing function responses for the API.

Parameters

  • calls: List of executed FunctionCall structs
  • results: List of execution results from Executor

Returns

A content map with role "function" and function response parts.

config(opts \\ [])

Create an AFC configuration.

Options

  • :max_calls - Maximum number of function calls to execute (default: 10)
  • :ignore_call_history - If true, don't track call history (default: false)
  • :enabled - Enable or disable AFC (default: true)
  • :parallel_execution - Execute multiple calls in parallel (default: false)

Examples

# Default configuration
config = AFC.config()

# Custom configuration
config = AFC.config(max_calls: 5, parallel_execution: true)

# Disable AFC
config = AFC.config(enabled: false)

extract_function_calls(response)

@spec extract_function_calls(map()) :: [Altar.ADM.FunctionCall.t()]

Extract function calls from a Gemini API response.

Parameters

  • response: Raw API response map or GenerateContentResponse struct

Returns

List of FunctionCall structs.

Examples

calls = AFC.extract_function_calls(response)
[%FunctionCall{name: "get_weather", args: %{"location" => "NYC"}}] = calls

extract_model_content_for_api(response)

@spec extract_model_content_for_api(map()) :: map()

Extract model content from a response in API-compatible format.

This is useful for multi-turn conversations where you need to include the model's response (including function calls) in the conversation history.

Parameters

  • response: A GenerateContentResponse struct or raw API response map

Returns

A map with role: "model" and parts in API format (camelCase keys).

Examples

# Get model content for conversation history
model_content = AFC.extract_model_content_for_api(response)
contents = [user_content, model_content, function_response_content]

has_function_calls?(response)

@spec has_function_calls?(map()) :: boolean()

Check if a response contains function calls.

Examples

if AFC.has_function_calls?(response) do
  # Handle function calls
end

loop(response, contents, registry, config, call_count, history, generate_fn, opts \\ [])

Execute the AFC loop.

This is the main entry point for automatic function calling. It:

  1. Checks if response contains function calls
  2. Executes them against the registry
  3. Builds function response content
  4. Calls the generate function with updated contents
  5. Repeats until done or limits reached

Parameters

  • response: Initial Gemini response
  • contents: Current conversation contents
  • registry: Function registry map
  • config: AFC configuration
  • call_count: Current call count (usually 0)
  • history: Call history (usually [])
  • generate_fn: Function to call Gemini API

Returns

{final_response, final_call_count, final_history}

Examples

generate_fn = fn contents, opts ->
  Gemini.APIs.Coordinator.generate_content(contents, opts)
end

{response, call_count, history} =
  AFC.loop(initial_response, contents, registry, config, 0, [], generate_fn)

should_continue?(response, config, call_count)

Determine if the AFC loop should continue.

Returns true if:

  • AFC is enabled
  • Response contains function calls
  • Call count is below max_calls limit

Parameters

  • response: The current Gemini response
  • config: AFC configuration
  • call_count: Current number of executed calls

Examples

if AFC.should_continue?(response, config, call_count) do
  # Continue AFC loop
end

track_history(history, calls)

@spec track_history(call_history(), [Altar.ADM.FunctionCall.t()]) :: call_history()

Track function call history.

Parameters

  • history: Current call history
  • calls: New calls to add

Returns

Updated history with new calls appended.