Skip to content

Latest commit

 

History

History
154 lines (126 loc) · 3.34 KB

BOOK.md

File metadata and controls

154 lines (126 loc) · 3.34 KB

GenAI

Mix.install([
  {:genai, "~> 0.0.1"},
  {:kino, "~> 0.12.0"}
])

Application.put_env(:genai, :mistral, api_key: System.get_env("MISTRAL_API_KEY"))
Application.put_env(:genai, :gemini, api_key: System.get_env("GEMINI_API_KEY"))
Application.put_env(:genai, :openai, api_key: System.get_env("OPENAI_API_KEY"))
Application.put_env(:genai, :anthropic, api_key: System.get_env("ANTHROPIC_API_KEY"))

Chat Form

defmodule ChatTree do
  def start_link do
    :ets.new(:conversation_history, [:set, :public, :named_table])
    :ets.insert(:conversation_history, {{[], :next}, nil})
    {:ok, :started}
  end

  def reset() do
    :ets.delete_all_objects(:conversation_history)
  end

  def next_node do
    next_node([])
  end

  defp next_node(acc) do
    case :ets.lookup(:conversation_history, {acc, :next}) do
      [{_, x}] when is_integer(x) -> next_node(acc ++ [x])
      # Default path if not found
      _ -> acc ++ [0]
    end
  end

  def next_child([]) do
    next_child([0])
  end

  def next_child([p]) do
    if :ets.lookup(:conversation_history, {[p + 1], :node}) != [] do
      next_child([p + 1])
    else
      [p + 1]
    end
  end

  def next_child(path) do
    [t | p] = Enum.reverse(path)
    p = Enum.reverse(p)
    n = p ++ [t + 1]

    if :ets.lookup(:conversation_history, {n, :node}) != [] do
      next_child(n)
    else
      n
    end
  end

  def insert_node(path, node) do
    p = next_child(path)
    :ets.insert(:conversation_history, {{p, :node}, node})
    select_node(p)
  end

  def append_node(node) do
    p = next_node()
    :ets.insert(:conversation_history, {{p, :node}, node})
    select_node(p)
  end

  defp select_node(path) do
    :ets.lookup(:conversation_history, {path, :node})

    if :ets.lookup(:conversation_history, {path, :node}) != [] do
      [t | p] = Enum.reverse(path)
      p = Enum.reverse(p)
      :ets.insert(:conversation_history, {{p, :next}, t})
    end
  end

  def node_list do
    p =
      next_node()
      |> Enum.slice(0..-2//1)

    Enum.map_reduce(p, [], fn n, acc ->
      acc = acc ++ [n]

      case :ets.lookup(:conversation_history, {acc, :node}) do
        [{_, value}] -> {value, acc}
        # In case the node is not found
        _ -> {:error, acc}
      end
    end)
    |> elem(0)
  end
end
frame = Kino.Frame.new()

inputs = [
  message: Kino.Input.text("Message")
]

form = Kino.Control.form(inputs, submit: "Send", reset_on_submit: [:message])

ChatTree.node_list()
|> Enum.map(fn msg ->
  content = Kino.Markdown.new("**Msg:**\n#{inspect(msg, pretty: true)}")
  Kino.Frame.append(frame, content)
end)
Kino.listen(form, fn %{data: %{message: message}, origin: _origin} ->
  msg = %GenAI.Message{role: :user, content: message}
  content = Kino.Markdown.new("**Msg:**\n#{inspect(msg, pretty: true)}")
  Kino.Frame.append(frame, content)
  ChatTree.append_node(msg)
  convo = ChatTree.node_list()

  {:ok, x} =
    GenAI.chat()
    |> GenAI.with_model(GenAI.Provider.Mistral.Models.mistral_small())
    |> GenAI.with_setting(:temperature, 0.7)
    |> GenAI.with_messages(convo)
    |> GenAI.run()

  with %{choices: [%{message: msg} | _]} <- x do
    IO.puts("MATCH)")
    ChatTree.append_node(msg)
    content = Kino.Markdown.new("**Msg:**\n#{inspect(msg, pretty: true)}")
    Kino.Frame.append(frame, content)
  end
end)

:ok
frame
form