Powered by AppSignal & Oban Pro
Would you like to see your link here? Contact us

Chat with Gemma 3

chat_with_gemma_3.livemd

Chat with Gemma 3

Mix.install([
  {:ollama, "~> 0.8"},
  {:kino, "~> 0.15"}
])

Prepare Ollama client

client = Ollama.init(base_url: "http://localhost:11434/api", receive_timeout: 300_000)
Ollama.pull_model(client, name: "gemma3:1b")
Ollama.preload(client, model: "gemma3:1b")

Chat with Gemma 3

messages = [
  %{role: "system", content: "あなたは親切なアシスタントです"},
  %{role: "user", content: "浦島太郎が助けたのは何ですか?"}
]

{:ok, %{"message" => message}} =
  Ollama.chat(
    client,
    model: "gemma3:1b",
    messages: messages
  )
Kino.Markdown.new(message["content"])

Stream response

{:ok, stream} =
  Ollama.chat(
    client,
    model: "gemma3:1b",
    messages: messages,
    stream: true
  )

stream
|> Stream.map(fn chunk ->
  IO.write(chunk["message"]["content"])
end)
|> Stream.run()

Create a chat interface

# 出力用フレーム
output_frame = Kino.Frame.new()

# ストリーミング用フレーム
stream_frame = Kino.Frame.new()

# 入力用フォーム
input_form =
  Kino.Control.form(
    [
      input_text: Kino.Input.textarea("メッセージ")
    ],
    submit: "送信"
  )

initial_messages = [
  %{role: "system", content: "あなたは親切なアシスタントです"}
]

# フォーム送信時の処理
Kino.listen(input_form, initial_messages, fn %{data: %{input_text: input}}, messages ->
  messages = messages ++ [%{role: "user", content: input}]

  Kino.Frame.append(output_frame, Kino.Markdown.new("ユーザー: " <> input))

  {:ok, stream} =
    Ollama.chat(
      client,
      model: "gemma3:1b",
      messages: messages,
      stream: true
    )

  full_response =
    stream
    |> Stream.transform("AI: ", fn chunk, acc ->
      response = acc <> chunk["message"]["content"]

      markdown = Kino.Markdown.new(response)
      Kino.Frame.render(stream_frame, markdown)

      {[chunk["message"]["content"]], response}
    end)
    |> Enum.join()

  Kino.Frame.render(stream_frame, Kino.Markdown.new(""))
  Kino.Frame.append(output_frame, Kino.Markdown.new("AI: " <> full_response))

  {:cont, messages ++ [%{role: "assistant", content: full_response}]}
end)

# フレームを空にしておく
Kino.Frame.render(output_frame, Kino.Markdown.new(""))
Kino.Frame.render(stream_frame, Kino.Markdown.new(""))

# 入出力を並べて表示
Kino.Layout.grid([output_frame, stream_frame, input_form], columns: 1)