Current section
Files
Jump to
Current section
Files
lib/mix/tasks/ex_claw.chat.ex
defmodule Mix.Tasks.ExClaw.Chat do
@moduledoc """
Starts an interactive chat REPL with an ADK Elixir agent.
mix ex_claw.chat
Options:
--model LLM model to use (default: gemini-2.0-flash)
--name Agent name (default: ex_claw)
"""
@shortdoc "Interactive chat with an ExClaw agent"
use Mix.Task
@impl Mix.Task
def run(args) do
Mix.Task.run("app.start")
{opts, _, _} =
OptionParser.parse(args, strict: [model: :string, name: :string])
model = opts[:model] || "gemini-2.0-flash"
name = opts[:name] || "ex_claw"
IO.puts("ExClaw Chat — #{name} (#{model})")
IO.puts("Type 'exit' or Ctrl-C to quit.\n")
agent =
ADK.Agent.LlmAgent.new(
name: name,
model: model,
instruction: """
You are ExClaw, a helpful AI assistant built on ADK Elixir.
Be concise and helpful. You're running as a CLI chat agent.
"""
)
runner = ADK.Runner.new(app_name: name, agent: agent)
loop(runner, name, "user-1", "chat-#{System.unique_integer([:positive])}")
end
defp loop(runner, name, user_id, session_id) do
case IO.gets("you> ") do
:eof ->
IO.puts("\nBye!")
input when is_binary(input) ->
input = String.trim(input)
if input in ["exit", "quit"] do
IO.puts("Bye!")
else
if input == "" do
loop(runner, name, user_id, session_id)
else
case ADK.Runner.run(runner, user_id, session_id, input) do
{:ok, events} ->
events
|> Enum.filter(&match?(%ADK.Event{author: a} when a != "user", &1))
|> Enum.each(fn event ->
text = extract_text(event)
if text && text != "", do: IO.puts("\n#{name}> #{text}\n")
end)
loop(runner, name, user_id, session_id)
{:error, reason} ->
IO.puts("\n[error] #{inspect(reason)}\n")
loop(runner, name, user_id, session_id)
end
end
end
end
end
defp extract_text(%ADK.Event{content: %{parts: parts}}) when is_list(parts) do
parts
|> Enum.map(fn
%{text: t} -> t
_ -> nil
end)
|> Enum.reject(&is_nil/1)
|> Enum.join("")
end
defp extract_text(_), do: nil
end