Packages
fnord
0.6.7
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/cmd/tool.ex
defmodule Cmd.Tool do
@behaviour Cmd
@impl Cmd
def spec() do
[
tool: [
name: "tool",
about: "Performs a tool call",
allow_unknown_args: true,
subcommands: get_subcommands(),
options: [
project: [
value_name: "PROJECT",
long: "--project",
short: "-p",
help: "Project name"
],
workers: [
value_name: "WORKERS",
long: "--workers",
short: "-w",
help: "Limits the number of concurrent OpenAI requests",
parser: :integer,
default: Cmd.default_workers()
],
tool: [
value_name: "TOOL",
long: "--tool",
short: "-t",
help: "The name of the tool to call; use --help to list options."
]
]
]
]
end
@impl Cmd
def run(opts, unknown) do
with {:ok, tool} <- Map.fetch(opts, :tool),
{:ok, tool_args} <- parse_tool_args(tool, unknown),
_ <- Store.get_project() do
state = %{ai: AI.new()}
AI.Tools.perform_tool_call(state, tool, tool_args)
|> case do
{:ok, response} -> IO.puts(response)
{:error, error} -> IO.puts(:stderr, "Error: #{error}")
end
end
end
defp parse_tool_args(tool, args) do
with {:ok, spec} <- AI.Tools.tool_spec(tool) do
build_optimus(tool, spec)
|> Optimus.parse!(args)
|> case do
%{options: args} ->
args
|> Enum.map(fn {k, v} -> {Atom.to_string(k), v} end)
|> Map.new()
|> then(&{:ok, &1})
other ->
other
end
end
end
defp build_optimus(tool, spec) do
Optimus.new!(
name: tool,
description: spec.function.description,
options: tool_spec_to_optimus_spec(spec)
)
end
defp tool_spec_to_optimus_spec(spec) do
%{required: required, properties: args} =
spec
|> Map.get(:function)
|> Map.get(:parameters)
is_required? =
required
|> Enum.map(fn arg -> String.to_atom(arg) end)
|> Enum.map(fn arg -> {arg, true} end)
|> Map.new()
args
|> Enum.reduce([], fn {arg, arg_spec}, acc ->
long = "--#{arg}"
help = arg_spec.description
required = Map.get(is_required?, arg, false)
Keyword.put(acc, arg,
long: long,
help: help,
required: required
)
end)
end
defp get_subcommands() do
AI.Tools.tools()
|> Enum.map(fn {tool, _module} ->
spec = AI.Tools.tool_spec!(tool)
desc =
spec.function.description
|> String.split("\n")
|> Enum.map(&String.trim/1)
|> Enum.join(" ")
{String.to_atom(tool),
[
name: tool,
about: desc,
options: tool_spec_to_optimus_spec(spec)
]}
end)
end
end