2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

LiveViewでLLMを使う 〜非同期処理を対応する〜

Posted at

このコラムはLiveViewでLLMを使います
そして非同期処理を対応します

実行イメージ

チャッピーの動き方で文字が流れるイメージしてもらえると良いかも

前提知識

ElixirでOllama

LiveViewの非同期処理

今回はOllamaも非同期にします(streamの部分)

プログラムを書く

lib/llm_async_web/live/index.ex
defmodule LlmAsyncWeb.Index do
  use LlmAsyncWeb, :live_view

  def mount(_params, _session, socket) do
    socket =
      assign(socket, text: "実行ボタンを押してください")
      |> assign(input_text: "Elixirについて教えてください")
      |> assign(btn: true)

    {:ok, socket}
  end

  def handle_event("start", _, socket) do
    pid = self()
    input_text = socket.assigns.input_text

    socket =
      assign(socket, btn: false)
      |> assign(text: "")
      |> assign_async(:ret, fn -> run(pid, input_text) end)

    {:noreply, socket}
  end

  def handle_event("update_text", %{"text" => new_text}, socket) do
    {:noreply, assign(socket, input_text: new_text)}
  end

  def handle_info(%{"done" => false, "response" => response}, socket) do
    text = socket.assigns.text <> response
    {:noreply, assign(socket, text: text)}
  end

  def handle_info(%{"done" => true}, socket) do
    socket =
      assign(socket, btn: true)

    {:noreply, socket}
  end

  def run(pid, text) do
    client = Ollama.init()

    {:ok, stream} =
      Ollama.completion(client,
        model: "gemma3:27b",
        prompt: text,
        stream: true
      )

    stream
    |> Stream.each(&Process.send(pid, &1, []))
    |> Stream.run()

    {:ok, %{ret: :ok}}
  end

  def render(assigns) do
    ~H"""
    <Layouts.app flash={@flash}>
      <div class="p-5">
        <form>
          <textarea id="text_input" name="text" phx-change="update_text" class="input w-[400px]">{@input_text}</textarea>
        </form>
        <button disabled={!@btn} class="btn" phx-click="start">実行</button>
        <p class="m-2">{@text}</p>
      </div>
    </Layouts.app>
    """
  end
end

解説

今回このコラムのポイントのみ書きます

  • 実行ボタンクリック def handle_event("start", _, socket) do

    • 実行ボタンを無効化
    • assign_async(:ret, fn -> run(pid, input_text) end)
      • Ollamaのプロセスを実行 ここでassign_asyncによってプロセスが独立します
      • つまり、GUIをブロックしません
  • Ollamaのプロセス def run(pid, text) do

    • Ollama.completionでstream: trueを指定
    • Stream.each(&Process.send(pid, &1, [])を書く
      • Ollamaが結果をリアルタイムで返します
  • Ollamaのプロセスの実行中に受け取る def handle_info(%{"done" => false, "response" => response}, socket) do

    • done" => false は実行中の意味
    • "response" => responseは実行した瞬間の結果を取得
    • text = socket.assigns.text <> responseで過去の結果と連携
  • Ollamaのプロセスの実行終了を受け取る def handle_info(%{"done" => true}, socket) do

    • "done" => trueは終了の意味
    • 実行ボタンを有効化

ソース

2
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
2
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?