Packages
llm_composer
0.18.1
0.20.2
0.20.1
0.20.0
0.19.6
0.19.5
0.19.4
0.19.3
0.19.2
0.19.1
0.19.0
0.18.2
0.18.1
0.18.0
0.17.1
0.17.0
0.16.2
0.16.1
0.16.0
0.15.0
0.14.2
0.14.1
0.14.0
0.13.1
0.13.0
0.12.3
0.12.2
0.12.0
0.11.2
0.11.1
0.11.0
0.10.0
0.8.0
0.7.0
0.6.0
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.0
0.3.6
0.3.5
0.3.4
0.3.3
0.3.2
0.3.1
0.3.0
0.2.0
0.1.0
LlmComposer is an Elixir library that facilitates chat interactions with language models, providing tools to handle user messages, generate responses, and execute functions automatically based on model outputs.
Current section
Files
Jump to
Current section
Files
lib/llm_composer/function_executor.ex
defmodule LlmComposer.FunctionExecutor do
@moduledoc """
Provides manual execution of function calls from LLM responses.
This module allows users to explicitly execute individual function calls
returned by the LLM, without automatic execution. It's designed for manual
control over function invocation and result handling.
## Usage
After receiving a response with function calls, use `execute/2` to
manually execute each function call with its arguments parsed and
validated before invocation.
"""
alias LlmComposer.Function
alias LlmComposer.FunctionCall
@json_mod if Code.ensure_loaded?(JSON), do: JSON, else: Jason
@doc """
Executes a single function call and returns the updated FunctionCall with result.
## Parameters
- `function_call`: The FunctionCall struct to execute
- `functions`: List of Function definitions available for execution
## Returns
- `{:ok, executed_call}`: FunctionCall with result populated
- `{:error, reason}`: Error tuple if execution fails
## Possible Errors
- `{:error, :function_not_found}`: Named function not in definitions
- `{:error, {:invalid_arguments, reason}}`: Failed to parse JSON arguments
- `{:error, {:execution_failed, reason}}`: Exception during function execution
"""
@spec execute(FunctionCall.t(), [Function.t()]) ::
{:ok, FunctionCall.t()} | {:error, term()}
def execute(%FunctionCall{} = function_call, functions) when is_list(functions) do
with {:ok, function} <- find_function(function_call.name, functions),
{:ok, args} <- parse_arguments(function_call.arguments),
{:ok, result} <- invoke_function(function, args) do
executed_call = %FunctionCall{function_call | result: result}
{:ok, executed_call}
end
end
@spec find_function(String.t(), [Function.t()]) ::
{:ok, Function.t()} | {:error, :function_not_found}
defp find_function(name, functions) do
case Enum.find(functions, fn f -> f.name == name end) do
nil -> {:error, :function_not_found}
function -> {:ok, function}
end
end
@spec parse_arguments(String.t() | nil) :: {:ok, map()} | {:error, term()}
defp parse_arguments(nil) do
{:ok, %{}}
end
defp parse_arguments(arguments) when is_binary(arguments) do
parsed = @json_mod.decode!(arguments)
{:ok, parsed}
rescue
e -> {:error, {:invalid_arguments, Exception.message(e)}}
end
@spec invoke_function(Function.t(), map()) :: {:ok, term()} | {:error, term()}
defp invoke_function(function, args) do
{module, function_name} = function.mf
try do
result = apply(module, function_name, [args])
{:ok, result}
rescue
e -> {:error, {:execution_failed, Exception.message(e)}}
catch
type, value -> {:error, {:execution_failed, "#{type}: #{inspect(value)}"}}
end
end
end