Packages
fnord
0.4.33
0.9.40
0.9.39
0.9.38
0.9.37
0.9.36
0.9.35
0.9.34
0.9.33
0.9.32
0.9.31
0.9.30
0.9.29
0.9.28
0.9.27
0.9.26
0.9.25
0.9.24
0.9.23
0.9.22
0.9.21
0.9.20
0.9.19
0.9.18
0.9.17
0.9.16
0.9.15
0.9.14
0.9.13
0.9.12
0.9.11
0.9.10
0.9.9
0.9.8
0.9.7
0.9.6
0.9.5
0.9.4
0.9.3
0.9.2
0.9.1
0.9.0
0.8.99
0.8.98
0.8.97
0.8.96
0.8.95
0.8.94
0.8.93
0.8.92
0.8.91
0.8.90
0.8.89
0.8.88
0.8.87
0.8.86
0.8.85
0.8.84
0.8.83
0.8.82
0.8.81
0.8.80
0.8.79
0.8.78
0.8.77
0.8.76
0.8.75
0.8.74
0.8.73
0.8.72
0.8.71
0.8.70
0.8.69
0.8.68
0.8.67
0.8.66
0.8.65
0.8.64
0.8.63
0.8.62
0.8.61
0.8.60
0.8.59
0.8.58
0.8.57
0.8.56
0.8.55
0.8.54
0.8.53
0.8.52
0.8.51
0.8.50
0.8.49
0.8.48
0.8.47
0.8.46
0.8.45
0.8.44
0.8.43
0.8.42
0.8.41
0.8.40
0.8.39
0.8.38
0.8.37
0.8.36
0.8.35
0.8.34
0.8.33
0.8.32
0.8.31
0.8.30
0.8.29
0.8.27
0.8.26
0.8.25
0.8.24
0.8.23
0.8.22
0.8.21
0.8.20
0.8.19
0.8.18
0.8.17
0.8.16
0.8.15
0.8.14
0.8.13
0.8.12
0.8.11
0.8.1
0.8.0
0.7.24
0.7.23
0.7.22
0.7.21
0.7.20
0.7.19
0.7.18
0.7.17
0.7.16
0.7.15
0.7.14
0.7.13
0.7.12
0.7.11
0.7.10
0.7.9
0.7.8
0.7.7
0.7.6
0.7.5
0.7.3
0.7.2
0.7.1
0.7.0
0.6.9
0.6.8
0.6.7
0.6.6
0.6.5
0.6.4
0.6.3
0.6.1
0.6.0
0.5.9
0.5.8
0.5.7
0.5.6
0.5.5
0.5.4
0.5.3
0.5.2
0.5.1
0.5.0
0.4.44
0.4.43
0.4.42
0.4.41
0.4.40
0.4.39
0.4.38
0.4.37
0.4.36
0.4.35
0.4.34
0.4.33
0.4.32
0.4.30
0.4.29
0.4.28
0.4.27
0.4.26
0.4.25
0.4.24
0.4.23
0.4.22
0.4.21
0.4.20
0.4.19
0.4.18
0.4.17
0.4.16
0.4.15
0.4.14
0.4.13
0.4.12
0.4.11
0.4.10
0.4.9
0.4.8
0.4.7
0.4.6
0.4.5
0.4.4
0.4.3
0.4.2
0.4.1
0.4.0
0.3.0
0.2.0
0.1.0
AI code archaeology
Current section
Files
Jump to
Current section
Files
lib/ai/response.ex
defmodule AI.Response do
def get(ai, opts) do
with {:ok, max_tokens} <- Keyword.fetch(opts, :max_tokens),
{:ok, model} <- Keyword.fetch(opts, :model),
{:ok, system} <- Keyword.fetch(opts, :system),
{:ok, user} <- Keyword.fetch(opts, :user) do
tools = Keyword.get(opts, :tools, nil)
on_event = Keyword.get(opts, :on_event, fn _, _ -> :ok end)
%{
ai: ai,
opts: Enum.into(opts, %{}),
max_tokens: max_tokens,
model: model,
tools: tools,
on_event: on_event,
messages: [AI.Util.system_msg(system), AI.Util.user_msg(user)],
tool_call_requests: [],
response: nil
}
|> send_request()
|> then(fn state ->
{:ok, state.response, context_window_usage(state)}
end)
end
end
defp context_window_usage(%{messages: msgs, max_tokens: max_tokens}) do
tokens = msgs |> inspect() |> Gpt3Tokenizer.encode() |> length()
pct = tokens / max_tokens * 100.0
pct_str = Number.Percentage.number_to_percentage(pct, precision: 2)
tokens_str = Number.Delimit.number_to_delimited(tokens, precision: 0)
max_tokens_str = Number.Delimit.number_to_delimited(max_tokens, precision: 0)
{"Context window usage", "#{pct_str} | #{tokens_str} / #{max_tokens_str}"}
end
# -----------------------------------------------------------------------------
# Response handling
# -----------------------------------------------------------------------------
defp send_request(state) do
AI.get_completion(state.ai, state.model, state.messages, state.tools)
|> handle_response(state)
end
defp handle_response({:ok, :msg, response}, state) do
%{state | response: response}
end
defp handle_response({:ok, :tool, tool_calls}, state) do
%{state | tool_call_requests: tool_calls}
|> handle_tool_calls()
|> send_request()
end
defp handle_response({:error, reason}, state) do
%{
state
| response: """
I encountered an error while processing your request. Please try again.
The error message was:
#{reason}
"""
}
end
# -----------------------------------------------------------------------------
# Tool calls
# -----------------------------------------------------------------------------
defp handle_tool_calls(%{tool_call_requests: tool_calls} = state) do
{:ok, queue} = Queue.start_link(&handle_tool_call(state, &1))
outputs =
tool_calls
|> Queue.map(queue)
|> Enum.flat_map(fn {:ok, msgs} -> msgs end)
Queue.shutdown(queue)
Queue.join(queue)
%{
state
| tool_call_requests: [],
messages: state.messages ++ outputs
}
end
def handle_tool_call(state, %{id: id, function: %{name: func, arguments: args_json}}) do
request = AI.Util.assistant_tool_msg(id, func, args_json)
UI.debug("TOOL CALL ID=#{id} FUNC=#{func} ARGS=#{args_json}")
with {:ok, output} <- perform_tool_call(state, func, args_json) do
response = AI.Util.tool_msg(id, func, output)
{:ok, [request, response]}
else
{:error, reason} ->
state.on_event.(:tool_call_error, {func, args_json, reason})
response = AI.Util.tool_msg(id, func, reason)
{:ok, [request, response]}
end
end
# -----------------------------------------------------------------------------
# Tool call outputs
# -----------------------------------------------------------------------------
defp perform_tool_call(state, func, args_json) when is_binary(args_json) do
with {:ok, args} <- Jason.decode(args_json) do
state.on_event.(:tool_call, {func, args})
perform_tool_call(state, func, args)
end
end
defp perform_tool_call(state, "search_tool", args) do
AI.Tools.Search.call(state, args)
end
defp perform_tool_call(state, "list_files_tool", args) do
AI.Tools.ListFiles.call(state, args)
end
defp perform_tool_call(state, "file_info_tool", args) do
AI.Tools.FileInfo.call(state, args)
end
defp perform_tool_call(state, "spelunker_tool", args) do
AI.Tools.Spelunker.call(state, args)
end
defp perform_tool_call(state, "git_pickaxe_tool", args) do
AI.Tools.GitPickaxe.call(state, args)
end
defp perform_tool_call(state, "git_show_tool", args) do
AI.Tools.GitShow.call(state, args)
end
defp perform_tool_call(state, "outline_tool", args) do
AI.Tools.Outline.call(state, args)
end
defp perform_tool_call(_state, func, _args) do
{:error, :unhandled_tool_call, func}
end
end