Packages
fnord
0.7.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/cmd/ask.ex
defmodule Cmd.Ask do
@project_not_found_error "Project not found; verify that the project has been indexed."
@template_not_found_error "Template file not found; verify that the output template file exists."
@default_rounds 3
@behaviour Cmd
@impl Cmd
def spec() do
[
ask: [
name: "ask",
about: "Ask the AI a question about the project",
options: [
project: [
value_name: "PROJECT",
long: "--project",
short: "-p",
help: "Project name",
required: true
],
question: [
value_name: "QUESTION",
long: "--question",
short: "-q",
help: "The prompt to ask the AI",
required: true
],
rounds: [
value_name: "ROUNDS",
long: "--rounds",
short: "-R",
help: "The number of research rounds to perform",
parser: :integer,
default: @default_rounds,
required: false
],
workers: [
value_name: "WORKERS",
long: "--workers",
short: "-w",
help: "Limits the number of concurrent OpenAI requests",
parser: :integer,
default: Cmd.default_workers()
],
follow: [
long: "--follow",
short: "-f",
help: "Follow up the conversation with another question/prompt"
],
template: [
value_name: "TEMPLATE",
long: "--template",
short: "-t",
help: """
The path to a file containing an output template for the AI to
follow when generating a response. Include '$$MOTD$$' in the
template to include the message of the day in the response.
"""
]
],
flags: [
replay: [
long: "--replay",
short: "-r",
help: "Replay a conversation (with --follow is set)"
]
]
]
]
end
@impl Cmd
def run(opts, _subcommands, _unknown) do
ai = AI.new()
start_time = System.monotonic_time(:second)
with :ok <- validate(opts),
{:ok, template} <- read_template(opts),
{:ok, msgs, conversation} <- restore_conversation(opts),
opts <- opts |> Map.put(:template, template) |> Map.put(:msgs, msgs),
%{msgs: msgs} <- AI.Agent.Reason.get_response(ai, opts),
{:ok, conversation_id} <- save_conversation(conversation, msgs) do
end_time = System.monotonic_time(:second)
time_taken = end_time - start_time
UI.flush()
IO.puts("""
-----
- Response generated in #{time_taken} seconds
- Conversation saved with ID #{conversation_id}
""")
else
{:error, :project_not_found} ->
UI.error(@project_not_found_error)
{:error, :template_not_found} ->
UI.error(@template_not_found_error)
{:error, :invalid_rounds} ->
UI.error("--rounds expects a positive integer")
{:ok, :testing} ->
:ok
end
end
defp validate(opts) do
with :ok <- validate_project(opts),
:ok <- validate_template(opts),
:ok <- validate_rounds(opts) do
:ok
end
end
defp validate_rounds(%{rounds: rounds}) when rounds > 0, do: :ok
defp validate_rounds(_opts), do: {:error, :invalid_rounds}
defp validate_project(_opts) do
Store.get_project()
|> Store.Project.exists_in_store?()
|> case do
true -> :ok
false -> {:error, :project_not_found}
end
end
defp validate_template(%{template: nil}), do: :ok
defp validate_template(%{template: template}) do
if File.exists?(template) do
:ok
else
{:error, :template_not_found}
end
end
defp read_template(%{template: nil}), do: {:ok, nil}
defp read_template(%{template: template}), do: File.read(template)
defp get_conversation(%{follow: conversation_id}) when is_binary(conversation_id) do
Store.Project.Conversation.new(conversation_id)
end
defp get_conversation(_opts) do
Store.Project.Conversation.new()
end
defp restore_conversation(opts) do
conversation = get_conversation(opts)
messages =
if Store.Project.Conversation.exists?(conversation) do
{:ok, _ts, messages} = Store.Project.Conversation.read(conversation)
messages
else
[]
end
{:ok, messages, conversation}
end
defp save_conversation(conversation, messages) do
Store.Project.Conversation.write(conversation, messages)
UI.debug("Conversation saved to file", conversation.store_path)
UI.report_step("Conversation saved", conversation.id)
{:ok, conversation.id}
end
end