Packages
fnord
0.5.6
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/tools/answers.ex
defmodule AI.Tools.Answers do
@agents_file "data/answers.yaml"
@external_resource @agents_file
@agent_defs YamlElixir.read_from_file!(@agents_file)
@agents @agent_defs |> Enum.map(& &1["name"])
@agent @agent_defs |> Enum.map(&{&1["name"], &1}) |> Map.new()
@model "gpt-4o"
@max_tokens 128_000
def agent_names(), do: @agents
def agent_description(%{"name" => name, "description" => description}) do
"- #{name}: #{description}"
end
def agent_description_list() do
@agent_defs
|> Enum.map(&agent_description/1)
|> Enum.join("\n")
end
@non_git_tools [
AI.Tools.tool_spec!("file_contents_tool"),
AI.Tools.tool_spec!("file_info_tool"),
AI.Tools.tool_spec!("file_list_tool"),
AI.Tools.tool_spec!("file_search_tool"),
AI.Tools.tool_spec!("file_spelunker_tool"),
AI.Tools.tool_spec!("notes_search_tool")
]
@git_tools [
AI.Tools.tool_spec!("git_diff_branch_tool"),
AI.Tools.tool_spec!("git_list_branches_tool"),
AI.Tools.tool_spec!("git_log_tool"),
AI.Tools.tool_spec!("git_pickaxe_tool"),
AI.Tools.tool_spec!("git_show_tool")
]
@tools @non_git_tools ++ @git_tools
# -----------------------------------------------------------------------------
# Behaviour implementation
# -----------------------------------------------------------------------------
@behaviour AI.Tools
@impl AI.Tools
def ui_note_on_request(%{"agent" => agent}) do
{"Generating a response", "#{agent}"}
end
@impl AI.Tools
def ui_note_on_result(_completion, _args), do: nil
@impl AI.Tools
def read_args(args) do
with {:ok, agent} <- read_agent(args) do
{:ok, %{"agent" => agent}}
end
end
defp read_agent(%{"agent" => agent}) when agent in @agents, do: {:ok, agent}
defp read_agent(%{"agent" => _}), do: {:error, :invalid_argument, "agent"}
defp read_agent(_), do: {:error, :missing_argument, "agent"}
@impl AI.Tools
def spec() do
%{
type: "function",
function: %{
name: "answers_tool",
description: """
Directs one of the following AI Agents to build a response for the user's query.
The Agent will receive a full transcript of all of the research performed thus far.
Each Agent is customized to provide the optimal response format for a class of user query.
""",
strict: true,
parameters: %{
additionalProperties: false,
type: "object",
required: ["agent"],
properties: %{
agent: %{
type: "string",
description: """
The name of the Agent to use to build the response. MUST be one of the following:
#{agent_description_list()}
"""
}
}
}
}
}
end
@impl AI.Tools
def call(%{messages: messages}, %{"agent" => agent}) do
with {:ok, %{response: response}} <- get_response(agent, messages) do
{:ok, response}
end
end
# ----------------------------------------------------------------------------
# Private functions
# ----------------------------------------------------------------------------
defp get_response(agent, messages) do
with {:ok, agent} <- Map.fetch(@agent, agent),
{:ok, prompt} <- Map.fetch(agent, "prompt"),
{:ok, {question, non_system_msgs}} <- get_transcript(messages),
{:ok, transcript} <- Jason.encode(non_system_msgs) do
AI.Completion.get(AI.new(),
max_tokens: @max_tokens,
model: @model,
use_planner: false,
log_msgs: false,
log_tool_calls: false,
log_tool_call_results: false,
tools: available_tools(),
messages: [
AI.Util.system_msg(prompt),
AI.Util.user_msg("""
Never refer to the "transcript".
Call it the "research" instead.
The following is a transcript of the research performed:
```json
#{transcript}
```
The user's question is:
> #{question}
""")
]
)
end
end
defp get_transcript(messages) do
with {:ok, question} <- get_user_query(messages),
non_system_msgs <- Enum.reject(messages, &is_system_msg?/1) do
{:ok, {question, non_system_msgs}}
end
end
defp is_system_msg?(%{role: "system"}), do: true
defp is_system_msg?(_), do: false
defp get_user_query(messages) do
messages
|> Enum.filter(&(&1.role == "user"))
|> List.first()
|> then(fn msg -> {:ok, msg.content} end)
end
defp available_tools() do
if Git.is_git_repo?() do
@tools
else
@non_git_tools
end
end
end