diff --git a/assets/css/app.scss b/assets/css/app.scss
index c6e5ce2..38c7869 100644
--- a/assets/css/app.scss
+++ b/assets/css/app.scss
@@ -78,3 +78,35 @@
);
@use '~bitstyles/scss/bitstyles/utilities/z-index';
+
+.loader {
+ width: 1rem;
+ height: 1rem;
+ border: 5px solid #9485ff;
+ border-bottom-color: transparent;
+ border-radius: 50%;
+ display: inline-block;
+ box-sizing: border-box;
+ animation: rotation 1s linear infinite;
+ }
+
+ @keyframes rotation {
+ 0% {
+ transform: rotate(0deg);
+ }
+ 100% {
+ transform: rotate(360deg);
+ }
+}
+
+.a-card ol {
+ padding-inline-start: var(--bs-size-l2);
+}
+
+.a-card ul {
+ padding-inline-start: var(--bs-size-l2);
+}
+
+.a-card code {
+ text-wrap: wrap;
+}
diff --git a/config/config.exs b/config/config.exs
index d88837a..03a84ef 100644
--- a/config/config.exs
+++ b/config/config.exs
@@ -6,6 +6,9 @@
import Config
+config :chatbot, Chatbot.Repo, types: Chatbot.PostgrexTypes
+config :chatbot, openai_key: "your openai API key"
+config :nx, default_backend: EXLA.Backend
import_config "config/endpoint.exs"
import_config "config/logger.exs"
import_config "config/phoenix.exs"
diff --git a/config/dev.exs b/config/dev.exs
new file mode 100644
index 0000000..becde76
--- /dev/null
+++ b/config/dev.exs
@@ -0,0 +1 @@
+import Config
diff --git a/config/prod.exs b/config/prod.exs
new file mode 100644
index 0000000..becde76
--- /dev/null
+++ b/config/prod.exs
@@ -0,0 +1 @@
+import Config
diff --git a/config/test.exs b/config/test.exs
new file mode 100644
index 0000000..becde76
--- /dev/null
+++ b/config/test.exs
@@ -0,0 +1 @@
+import Config
diff --git a/eval/rag_triad_eval.exs b/eval/rag_triad_eval.exs
new file mode 100644
index 0000000..73d8f2e
--- /dev/null
+++ b/eval/rag_triad_eval.exs
@@ -0,0 +1,63 @@
+openai_key = Application.compile_env(:chatbot, :openai_key)
+
+dataset =
+ "https://huggingface.co/datasets/explodinggradients/amnesty_qa/resolve/main/english.json"
+
+IO.puts("downloading dataset")
+
+data =
+ Req.get!(dataset).body
+ |> Jason.decode!()
+
+IO.puts("indexing")
+
+data["contexts"]
+|> Enum.map(&Enum.join(&1, " "))
+|> Enum.with_index(fn context, index -> %{document: context, source: "#{index}"} end)
+|> Chatbot.Rag.index()
+
+IO.puts("generating responses")
+
+generations =
+ for question <- data["question"] do
+ Chatbot.Rag.query(question)
+ end
+
+provider = Rag.Ai.OpenAI.new(%{text_model: "gpt-4o-mini", api_key: openai_key})
+
+IO.puts("evaluating")
+
+generations =
+ for generation <- generations do
+ Rag.Evaluation.evaluate_rag_triad(generation, provider)
+ end
+
+json =
+ generations
+ |> Enum.map(fn generation ->
+ Map.from_struct(generation)
+ |> Map.take([:query, :context, :context_sources, :response, :evaluations])
+ end)
+ |> Jason.encode!()
+
+File.write!(Path.join(__DIR__, "triad_eval.json"), json)
+
+average_rag_triad_scores =
+ Enum.map(
+ generations,
+ fn gen ->
+ %{
+ evaluations: %{
+ "context_relevance_score" => context_relevance_score,
+ "groundedness_score" => groundedness_score,
+ "answer_relevance_score" => answer_relevance_score
+ }
+ } = gen
+
+ (context_relevance_score + groundedness_score + answer_relevance_score) / 3
+ end
+ )
+
+total_average_score = Enum.sum(average_rag_triad_scores) / Enum.count(average_rag_triad_scores)
+
+IO.puts("Score: ,#{total_average_score}")
diff --git a/lib/chatbot/application.ex b/lib/chatbot/application.ex
index 93625a7..22a30cd 100644
--- a/lib/chatbot/application.ex
+++ b/lib/chatbot/application.ex
@@ -5,9 +5,18 @@ defmodule Chatbot.Application do
use Application
+ alias Chatbot.Rag.Serving
+ alias Chatbot.Rag.TelemetryHandler
+
@impl true
def start(_type, _args) do
children = [
+ {Nx.Serving,
+ [
+ serving: Serving.build_embedding_serving(),
+ name: Rag.EmbeddingServing,
+ batch_timeout: 100
+ ]},
{Task.Supervisor, name: Chatbot.TaskSupervisor},
ChatbotWeb.Telemetry,
Chatbot.Repo,
@@ -19,6 +28,14 @@ defmodule Chatbot.Application do
ChatbotWeb.Endpoint
]
+ :ok =
+ :telemetry.attach_many(
+ "rag-handler",
+ Rag.Telemetry.events(),
+ &TelemetryHandler.handle_event/4,
+ nil
+ )
+
# See https://hexdocs.pm/elixir/Supervisor.html
# for other strategies and supported options
opts = [strategy: :one_for_one, name: Chatbot.Supervisor]
diff --git a/lib/chatbot/chat.ex b/lib/chatbot/chat.ex
index d172f50..a115137 100644
--- a/lib/chatbot/chat.ex
+++ b/lib/chatbot/chat.ex
@@ -3,13 +3,12 @@ defmodule Chatbot.Chat do
Context for chat related functions.
"""
import Ecto.Query, only: [from: 2]
- alias Chatbot.{Chat.Message, LLMMock, Repo}
+ alias Chatbot.{Chat.Message, Repo}
alias LangChain.Chains.LLMChain
# There is currently a bug in the LangChain type specs:
# `add_callback/2` expects a map with all possible handler functions.
# See:
# https://hexdocs.pm/langchain/0.3.0-rc.0/LangChain.Chains.ChainCallbacks.html#t:chain_callback_handler/0
- @dialyzer {:nowarn_function, stream_assistant_message: 1}
@doc """
Creates a message.
@@ -42,16 +41,14 @@ defmodule Chatbot.Chat do
@spec request_assistant_message([Message.t()]) ::
{:ok, Message.t()} | {:error, String.t() | Ecto.Changeset.t()}
def request_assistant_message(messages) do
- maybe_mock_llm()
-
messages = Enum.map(messages, &to_langchain_message/1)
@chain
|> LLMChain.add_messages(messages)
|> LLMChain.run()
|> case do
- {:ok, _chain, response} ->
- create_message(%{role: :assistant, content: response.content})
+ {:ok, chain} ->
+ create_message(%{role: :assistant, content: chain.last_message.content})
_error ->
{:error, "I failed, I'm sorry"}
@@ -64,15 +61,13 @@ defmodule Chatbot.Chat do
Once the full message was processed, it is saved as an assistant message.
"""
- @spec stream_assistant_message(pid()) :: Message.t()
- def stream_assistant_message(receiver) do
- messages = all_messages() |> Enum.map(&to_langchain_message/1)
-
- {:ok, assistant_message} = create_message(%{role: :assistant, content: ""})
+ @spec stream_assistant_message(pid(), [Message.t()], Message.t()) :: Message.t()
+ def stream_assistant_message(receiver, messages, assistant_message) do
+ messages = Enum.map(messages, &to_langchain_message/1)
handler = %{
on_llm_new_delta: fn _model, %LangChain.MessageDelta{} = data ->
- send(receiver, {:next_message_delta, assistant_message.id, data})
+ send(receiver, {:next_message_delta, assistant_message, data})
end,
on_message_processed: fn _chain, %LangChain.Message{} = data ->
completed_message = update_message!(assistant_message, %{content: data.content})
@@ -81,29 +76,21 @@ defmodule Chatbot.Chat do
end
}
- Task.Supervisor.start_child(Chatbot.TaskSupervisor, fn ->
- maybe_mock_llm(stream: true)
-
+ {:ok, _chain} =
@chain
|> LLMChain.add_callback(handler)
- |> LLMChain.add_llm_callback(handler)
|> LLMChain.add_messages(messages)
|> LLMChain.run()
- end)
assistant_message
end
- defp to_langchain_message(%{role: :user, content: content}),
+ def to_langchain_message(%{role: :user, content: content}),
do: LangChain.Message.new_user!(content)
- defp to_langchain_message(%{role: :assistant, content: content}),
+ def to_langchain_message(%{role: :assistant, content: content}),
do: LangChain.Message.new_assistant!(content)
- defp maybe_mock_llm(opts \\ []) do
- if Application.fetch_env!(:chatbot, :mock_llm_api), do: LLMMock.mock(opts)
- end
-
@doc """
Lists all messages ordered by insertion date.
"""
diff --git a/lib/chatbot/chat/message.ex b/lib/chatbot/chat/message.ex
index 2e69242..fa10d89 100644
--- a/lib/chatbot/chat/message.ex
+++ b/lib/chatbot/chat/message.ex
@@ -13,6 +13,7 @@ defmodule Chatbot.Chat.Message do
schema "messages" do
field :role, Ecto.Enum, values: @message_types
field :content, :string
+ field :sources, {:array, :string}
timestamps()
end
@@ -24,7 +25,7 @@ defmodule Chatbot.Chat.Message do
@spec changeset(t(), map()) :: Ecto.Changeset.t()
def changeset(message \\ %__MODULE__{}, attrs) do
message
- |> cast(attrs, [:role])
+ |> cast(attrs, [:role, :sources])
|> cast(attrs, [:content], empty_values: [nil])
# we cannot require the content, as
# validate_required still considers "" as empty
diff --git a/lib/chatbot/llm_mock.ex b/lib/chatbot/llm_mock.ex
deleted file mode 100644
index 68f108a..0000000
--- a/lib/chatbot/llm_mock.ex
+++ /dev/null
@@ -1,51 +0,0 @@
-defmodule Chatbot.LLMMock do
- @moduledoc """
- This module provides functions to mock the messages from the
- LLM.
-
- Note:
- We are using `set_api_override/1` from LangChain.Utils.ApiOverride
- for this, which uses the process dictionary. So make sure to call
- the functions of this module from within the process that calls
- `LangChain.Chains.LLMChain.run/0`.
- """
- import LangChain.Utils.ApiOverride
- alias LangChain.{Message, MessageDelta}
-
- def mock(opts) do
- if Keyword.get(opts, :stream, false) do
- do_mock()
- else
- do_mock_stream()
- end
- end
-
- def do_mock do
- content = """
- Thanks for your question.
- I don't have an answer right now.
- Please try another question.
- Maybe I can help with that.
- """
-
- set_api_override({:ok, Message.new_assistant!(%{content: content}), :on_llm_new_message})
- end
-
- def do_mock_stream do
- fake_messages = [
- [MessageDelta.new!(%{role: :assistant, content: nil, status: :incomplete})],
- [MessageDelta.new!(%{content: "Thanks for your question. ", status: :incomplete})],
- [MessageDelta.new!(%{content: "Let me think about that. ", status: :incomplete})],
- [MessageDelta.new!(%{content: "... ", status: :incomplete})],
- [MessageDelta.new!(%{content: "I don't have an answer right now. ", status: :incomplete})],
- [
- MessageDelta.new!(%{
- content: "Please try another question. Maybe I can help with that.",
- status: :complete
- })
- ]
- ]
-
- set_api_override({:ok, fake_messages, :on_llm_new_delta})
- end
-end
diff --git a/lib/chatbot/rag.ex b/lib/chatbot/rag.ex
new file mode 100644
index 0000000..980b069
--- /dev/null
+++ b/lib/chatbot/rag.ex
@@ -0,0 +1,136 @@
+defmodule Chatbot.Rag do
+ @moduledoc false
+ alias Chatbot.Repo
+ alias Rag.{Ai, Embedding, Generation, Retrieval}
+
+ import Ecto.Query
+ import Pgvector.Ecto.Query
+
+ @provider Ai.Nx.new(%{embeddings_serving: Rag.EmbeddingServing})
+
+ def ingest_ecto do
+ docs_url = "https://repo.hex.pm/docs/ecto-3.12.5.tar.gz"
+
+ code_url = "https://repo.hex.pm/tarballs/ecto-3.12.5.tar"
+
+ req = Req.new(url: docs_url) |> ReqHex.attach()
+ docs_tarball = Req.get!(req).body
+
+ docs =
+ for {file, content} <- docs_tarball, text_file?(file) do
+ file = to_string(file)
+ %{source: file, document: content}
+ end
+
+ req = Req.new(url: code_url) |> ReqHex.attach()
+ code_tarball = Req.get!(req).body
+
+ code =
+ for {file, content} <- code_tarball["contents.tar.gz"] do
+ %{source: file, document: content}
+ end
+
+ index(docs ++ code)
+ end
+
+ defp text_file?(file) when is_list(file) do
+ file
+ |> to_string()
+ |> String.ends_with?([".html", ".md", ".txt"])
+ end
+
+ defp text_file?(file) when is_binary(file) do
+ file
+ |> String.ends_with?([".html", ".md", ".txt"])
+ end
+
+ def index(ingestions) do
+ chunks =
+ ingestions
+ |> Enum.flat_map(&chunk_text(&1, :document))
+ |> Embedding.generate_embeddings_batch(@provider,
+ text_key: :chunk,
+ embedding_key: :embedding
+ )
+ |> Enum.map(&to_chunk(&1))
+
+ Repo.insert_all(Chatbot.Rag.Chunk, chunks)
+ end
+
+ defp chunk_text(ingestion, text_key, opts \\ []) do
+ text = Map.fetch!(ingestion, text_key)
+ chunks = TextChunker.split(text, opts)
+
+ Enum.map(chunks, &Map.put(ingestion, :chunk, &1.text))
+ end
+
+ def build_generation(query) do
+ generation =
+ Generation.new(query)
+ |> Embedding.generate_embedding(@provider)
+ |> Retrieval.retrieve(:fulltext_results, fn generation -> query_fulltext(generation) end)
+ |> Retrieval.retrieve(:semantic_results, fn generation ->
+ query_with_pgvector(generation)
+ end)
+ |> Retrieval.reciprocal_rank_fusion(
+ %{fulltext_results: 1, semantic_results: 1},
+ :rrf_result
+ )
+ |> Retrieval.deduplicate(:rrf_result, [:source])
+
+ context =
+ Generation.get_retrieval_result(generation, :rrf_result)
+ |> Enum.map_join("\n\n", & &1.document)
+
+ context_sources =
+ Generation.get_retrieval_result(generation, :rrf_result)
+ |> Enum.map(& &1.source)
+
+ prompt = prompt(query, context)
+
+ generation
+ |> Generation.put_context(context)
+ |> Generation.put_context_sources(context_sources)
+ |> Generation.put_prompt(prompt)
+ end
+
+ defp to_chunk(ingestion) do
+ now = NaiveDateTime.utc_now() |> NaiveDateTime.truncate(:second)
+
+ ingestion
+ |> Map.put_new(:inserted_at, now)
+ |> Map.put_new(:updated_at, now)
+ end
+
+ defp query_with_pgvector(%{query_embedding: query_embedding}, limit \\ 3) do
+ {:ok,
+ Repo.all(
+ from(c in Chatbot.Rag.Chunk,
+ order_by: l2_distance(c.embedding, ^Pgvector.new(query_embedding)),
+ limit: ^limit
+ )
+ )}
+ end
+
+ defp query_fulltext(%{query: query}, limit \\ 3) do
+ {:ok,
+ Repo.all(
+ from(c in Chatbot.Rag.Chunk,
+ where: fragment("to_tsvector(?) @@ websearch_to_tsquery(?)", c.chunk, ^query),
+ limit: ^limit
+ )
+ )}
+ end
+
+ defp prompt(query, context) do
+ """
+ Context information is below.
+ ---------------------
+ #{context}
+ ---------------------
+ Given the context information and no prior knowledge, answer the query.
+ Query: #{query}
+ Answer:
+ """
+ end
+end
diff --git a/lib/chatbot/rag/chunk.ex b/lib/chatbot/rag/chunk.ex
new file mode 100644
index 0000000..32ae106
--- /dev/null
+++ b/lib/chatbot/rag/chunk.ex
@@ -0,0 +1,18 @@
+defmodule Chatbot.Rag.Chunk do
+ @moduledoc false
+
+ use Ecto.Schema
+
+ schema "chunks" do
+ field(:document, :string)
+ field(:source, :string)
+ field(:chunk, :string)
+ field(:embedding, Pgvector.Ecto.Vector)
+
+ timestamps()
+ end
+
+ def changeset(chunk \\ %__MODULE__{}, attrs) do
+ Ecto.Changeset.cast(chunk, attrs, [:document, :source, :chunk, :embedding])
+ end
+end
diff --git a/lib/chatbot/rag/serving.ex b/lib/chatbot/rag/serving.ex
new file mode 100644
index 0000000..6ceb292
--- /dev/null
+++ b/lib/chatbot/rag/serving.ex
@@ -0,0 +1,40 @@
+defmodule Chatbot.Rag.Serving do
+ @moduledoc false
+
+ alias Bumblebee.Text
+
+ def build_embedding_serving do
+ repo = {:hf, "jinaai/jina-embeddings-v2-base-code"}
+
+ {:ok, model_info} =
+ Bumblebee.load_model(repo,
+ params_filename: "model.safetensors",
+ spec_overrides: [architecture: :base]
+ )
+
+ {:ok, tokenizer} = Bumblebee.load_tokenizer(repo)
+
+ Text.TextEmbedding.text_embedding(model_info, tokenizer,
+ compile: [batch_size: 64, sequence_length: 512],
+ defn_options: [compiler: EXLA],
+ output_attribute: :hidden_state,
+ output_pool: :mean_pooling
+ )
+ end
+
+ def build_llm_serving do
+ repo = {:hf, "HuggingFaceTB/SmolLM2-135M-Instruct"}
+
+ {:ok, model_info} = Bumblebee.load_model(repo)
+ {:ok, tokenizer} = Bumblebee.load_tokenizer(repo)
+ {:ok, generation_config} = Bumblebee.load_generation_config(repo)
+
+ generation_config = Bumblebee.configure(generation_config, max_new_tokens: 100)
+
+ Bumblebee.Text.generation(model_info, tokenizer, generation_config,
+ compile: [batch_size: 1, sequence_length: 6000],
+ defn_options: [compiler: EXLA],
+ stream: false
+ )
+ end
+end
diff --git a/lib/chatbot/rag/telemetry_handler.ex b/lib/chatbot/rag/telemetry_handler.ex
new file mode 100644
index 0000000..6337532
--- /dev/null
+++ b/lib/chatbot/rag/telemetry_handler.ex
@@ -0,0 +1,9 @@
+defmodule Chatbot.Rag.TelemetryHandler do
+ @moduledoc false
+ alias Phoenix.PubSub
+
+ def handle_event(prefix, _measurement, _metadata, _config) do
+ [:rag, key, event] = prefix
+ PubSub.broadcast(Chatbot.PubSub, "rag", {key, event})
+ end
+end
diff --git a/lib/chatbot_web/live/chat_live.ex b/lib/chatbot_web/live/chat_live.ex
index 1167d4e..09db903 100644
--- a/lib/chatbot_web/live/chat_live.ex
+++ b/lib/chatbot_web/live/chat_live.ex
@@ -3,13 +3,17 @@ defmodule ChatbotWeb.ChatLive do
import ChatbotWeb.CoreComponents
import BitcrowdEcto.Random, only: [uuid: 0]
alias Chatbot.Chat
+ alias Phoenix.PubSub
@impl Phoenix.LiveView
def mount(_params, _session, socket) do
+ if connected?(socket), do: PubSub.subscribe(Chatbot.PubSub, "rag")
+
socket =
socket
|> stream(:messages, Chat.all_messages())
|> assign(:currently_streamed_response, nil)
+ |> assign(:current_activity, nil)
|> assign(:form, build_form())
{:ok, socket}
@@ -36,10 +40,15 @@ defmodule ChatbotWeb.ChatLive do
id={dom_id}
role={message.role}
content={message.content}
+ sources={message.sources}
/>
+ <.ui_card :if={@current_activity}>
+
+ <%= @current_activity %>
+
<.simple_form for={@form} phx-submit="send" class="u-justify-self-end u-width-75">
<.ui_input
type="textarea"
@@ -76,18 +85,43 @@ defmodule ChatbotWeb.ChatLive do
~H"""
<.ui_card id={@id} class={@class}>
<%= @markdown %>
+
+
+ Sources
+
+ -
+ <%= source %>
+
+
+
"""
end
@impl Phoenix.LiveView
def handle_event("send", %{"message" => %{"content" => content}}, socket) do
+ messages = Chat.all_messages()
+
+ pid = self()
+
with {:ok, user_message} <- Chat.create_message(%{role: :user, content: content}),
- assistant_message <- Chat.stream_assistant_message(self()) do
+ {:ok, assistant_message} <- Chat.create_message(%{role: :assistant, content: ""}) do
{:noreply,
socket
|> assign(:form, build_form())
- |> stream(:messages, [user_message, assistant_message])}
+ |> stream(:messages, [user_message, assistant_message])
+ |> start_async(:rag, fn ->
+ {:ok, augmented_user_message, augmentation} = augment_user_message(user_message)
+
+ assistant_message =
+ Chat.update_message!(assistant_message, %{sources: augmentation.context_sources})
+
+ Chat.stream_assistant_message(
+ pid,
+ messages ++ [augmented_user_message],
+ assistant_message
+ )
+ end)}
end
end
@@ -108,7 +142,7 @@ defmodule ChatbotWeb.ChatLive do
{:noreply, assign(socket, :currently_streamed_response, nil)}
end
- def handle_info({:next_message_delta, id, %{status: :incomplete} = message_delta}, socket) do
+ def handle_info({:next_message_delta, message, %{status: :incomplete} = message_delta}, socket) do
currently_streamed_response = socket.assigns.currently_streamed_response
merged_message_deltas =
@@ -116,11 +150,7 @@ defmodule ChatbotWeb.ChatLive do
{:noreply,
socket
- |> stream_insert(:messages, %{
- id: id,
- role: :assistant,
- content: merged_message_deltas.content
- })
+ |> stream_insert(:messages, %{message | content: merged_message_deltas.content})
|> assign(:currently_streamed_response, merged_message_deltas)}
end
@@ -128,6 +158,27 @@ defmodule ChatbotWeb.ChatLive do
{:noreply, stream_insert(socket, :messages, completed_message)}
end
+ def handle_info({:generate_embedding, :start}, socket) do
+ {:noreply, assign(socket, current_activity: "looking up information")}
+ end
+
+ def handle_info({:retrieve, :stop}, socket) do
+ {:noreply, assign(socket, current_activity: "generating response")}
+ end
+
+ def handle_info({_key, :exception}, socket) do
+ {:noreply, assign(socket, current_activity: "an error occurred :(")}
+ end
+
+ def handle_info({_key, _event}, socket) do
+ {:noreply, socket}
+ end
+
+ @impl true
+ def handle_async(:rag, _no, socket) do
+ {:noreply, assign(socket, current_activity: nil)}
+ end
+
defp build_form do
%{role: :user, content: ""}
|> Chat.Message.changeset()
@@ -136,4 +187,12 @@ defmodule ChatbotWeb.ChatLive do
# for a new message and clears the input
|> to_form(id: uuid())
end
+
+ defp augment_user_message(user_message) do
+ %{role: :user, content: query} = user_message
+
+ rag_generation = Chatbot.Rag.build_generation(query)
+
+ {:ok, %{user_message | content: rag_generation.prompt}, rag_generation}
+ end
end
diff --git a/lib/postgrex_types.ex b/lib/postgrex_types.ex
new file mode 100644
index 0000000..0850089
--- /dev/null
+++ b/lib/postgrex_types.ex
@@ -0,0 +1,5 @@
+Postgrex.Types.define(
+ Chatbot.PostgrexTypes,
+ [Pgvector.Extensions.Vector] ++ Ecto.Adapters.Postgres.extensions(),
+ []
+)
diff --git a/mix.exs b/mix.exs
index 26baa8b..308a812 100644
--- a/mix.exs
+++ b/mix.exs
@@ -33,11 +33,17 @@ defmodule Chatbot.MixProject do
# Type `mix help deps` for examples and options.
defp deps do
[
+ {:req_hex, "~> 0.2.1"},
+ {:pgvector, "~> 0.3.0"},
+ {:ecto, "~> 3.0"},
+ {:exla, "~> 0.9.1"},
+ {:bumblebee, github: "joelpaulkoch/bumblebee", branch: "jina-embeddings-v2-base-code"},
+ {:text_chunker, "~> 0.3.1"},
{:ex_machina, "~> 2.8"},
{:bitcrowd_ecto, "~> 1.0"},
{:credo, "~> 1.7", only: [:dev, :test], runtime: false},
{:dialyxir, "~> 1.4", only: [:dev, :test], runtime: false},
- {:langchain, "~> 0.3.0-rc.0"},
+ {:langchain, "~> 0.3.1"},
{:phoenix, "~> 1.7.14"},
{:earmark, "~> 1.0"},
{:phoenix_ecto, "~> 4.5"},
@@ -47,6 +53,7 @@ defmodule Chatbot.MixProject do
{:phoenix_live_reload, "~> 1.2", only: :dev},
# TODO bump on release to {:phoenix_live_view, "~> 1.0.0"},
{:phoenix_live_view, "~> 1.0.0-rc.1", override: true},
+ {:rag, "~> 0.2.1"},
{:floki, ">= 0.30.0", only: :test},
{:phoenix_live_dashboard, "~> 0.8.3"},
{:telemetry_metrics, "~> 1.0"},
diff --git a/mix.lock b/mix.lock
index a71cc19..6af8e76 100644
--- a/mix.lock
+++ b/mix.lock
@@ -1,33 +1,47 @@
%{
"abacus": {:hex, :abacus, "2.1.0", "b6db5c989ba3d9dd8c36d1cb269e2f0058f34768d47c67eb8ce06697ecb36dd4", [:mix], [], "hexpm", "255de08b02884e8383f1eed8aa31df884ce0fb5eb394db81ff888089f2a1bbff"},
+ "axon": {:hex, :axon, "0.7.0", "2e2c6d93b4afcfa812566b8922204fa022b60081e86ebd411df4db7ea30f5457", [:mix], [{:kino, "~> 0.7", [hex: :kino, repo: "hexpm", optional: true]}, {:kino_vega_lite, "~> 0.1.7", [hex: :kino_vega_lite, repo: "hexpm", optional: true]}, {:nx, "~> 0.9", [hex: :nx, repo: "hexpm", optional: false]}, {:polaris, "~> 0.1", [hex: :polaris, repo: "hexpm", optional: false]}, {:table_rex, "~> 3.1.1", [hex: :table_rex, repo: "hexpm", optional: true]}], "hexpm", "ee9857a143c9486597ceff434e6ca833dc1241be6158b01025b8217757ed1036"},
"bandit": {:hex, :bandit, "1.5.7", "6856b1e1df4f2b0cb3df1377eab7891bec2da6a7fd69dc78594ad3e152363a50", [:mix], [{:hpax, "~> 1.0.0", [hex: :hpax, repo: "hexpm", optional: false]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:thousand_island, "~> 1.0", [hex: :thousand_island, repo: "hexpm", optional: false]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "f2dd92ae87d2cbea2fa9aa1652db157b6cba6c405cb44d4f6dd87abba41371cd"},
"bitcrowd_ecto": {:hex, :bitcrowd_ecto, "1.0.0", "b255cf7b8e22bc17adeb8bbc9907ef02dcdc751fd68ab3e444b0a098dac99b65", [:mix], [{:ecto, "~> 3.6", [hex: :ecto, repo: "hexpm", optional: false]}, {:ecto_sql, "~> 3.6", [hex: :ecto_sql, repo: "hexpm", optional: false]}, {:ex_money, "~> 5.12", [hex: :ex_money, repo: "hexpm", optional: true]}, {:jason, "> 0.0.0", [hex: :jason, repo: "hexpm", optional: true]}], "hexpm", "862effc5eba425176472c2f01dd50b9994622089fa4bc2a11d75732b47dcdab9"},
"bitstyles_phoenix": {:hex, :bitstyles_phoenix, "2.5.0", "c02aae26fcf6ff752738b35aa97f3991c67533412eda381abaad3e22aa2e2215", [:mix], [{:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 3.3 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: false]}, {:phoenix_live_view, "~> 0.18.12 or ~> 0.19.0 or ~> 0.20.0", [hex: :phoenix_live_view, repo: "hexpm", optional: false]}], "hexpm", "fe018f0eeec6c84afedc1785495efe0a651ecf34656c4b52f6da09d5149a9f28"},
+ "bumblebee": {:git, "https://github.com/joelpaulkoch/bumblebee.git", "1c27550b1cbfcfff3b0fb99e84bf2b3d5ccb4369", [branch: "jina-embeddings-v2-base-code"]},
"bunt": {:hex, :bunt, "1.0.0", "081c2c665f086849e6d57900292b3a161727ab40431219529f13c4ddcf3e7a44", [:mix], [], "hexpm", "dc5f86aa08a5f6fa6b8096f0735c4e76d54ae5c9fa2c143e5a1fc7c1cd9bb6b5"},
- "castore": {:hex, :castore, "1.0.9", "5cc77474afadf02c7c017823f460a17daa7908e991b0cc917febc90e466a375c", [:mix], [], "hexpm", "5ea956504f1ba6f2b4eb707061d8e17870de2bee95fb59d512872c2ef06925e7"},
+ "castore": {:hex, :castore, "1.0.12", "053f0e32700cbec356280c0e835df425a3be4bc1e0627b714330ad9d0f05497f", [:mix], [], "hexpm", "3dca286b2186055ba0c9449b4e95b97bf1b57b47c1f2644555879e659960c224"},
+ "complex": {:hex, :complex, "0.6.0", "b0130086a7a8c33574d293b2e0e250f4685580418eac52a5658a4bd148f3ccf1", [:mix], [], "hexpm", "0a5fa95580dcaf30fcd60fe1aaf24327c0fe401e98c24d892e172e79498269f9"},
"credo": {:hex, :credo, "1.7.10", "6e64fe59be8da5e30a1b96273b247b5cf1cc9e336b5fd66302a64b25749ad44d", [:mix], [{:bunt, "~> 0.2.1 or ~> 1.0", [hex: :bunt, repo: "hexpm", optional: false]}, {:file_system, "~> 0.2 or ~> 1.0", [hex: :file_system, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "71fbc9a6b8be21d993deca85bf151df023a3097b01e09a2809d460348561d8cd"},
"db_connection": {:hex, :db_connection, "2.7.0", "b99faa9291bb09892c7da373bb82cba59aefa9b36300f6145c5f201c7adf48ec", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "dcf08f31b2701f857dfc787fbad78223d61a32204f217f15e881dd93e4bdd3ff"},
- "decimal": {:hex, :decimal, "2.1.1", "5611dca5d4b2c3dd497dec8f68751f1f1a54755e8ed2a966c2633cf885973ad6", [:mix], [], "hexpm", "53cfe5f497ed0e7771ae1a475575603d77425099ba5faef9394932b35020ffcc"},
+ "decimal": {:hex, :decimal, "2.3.0", "3ad6255aa77b4a3c4f818171b12d237500e63525c2fd056699967a3e7ea20f62", [:mix], [], "hexpm", "a4d66355cb29cb47c3cf30e71329e58361cfcb37c34235ef3bf1d7bf3773aeac"},
"dialyxir": {:hex, :dialyxir, "1.4.4", "fb3ce8741edeaea59c9ae84d5cec75da00fa89fe401c72d6e047d11a61f65f70", [:mix], [{:erlex, ">= 0.2.7", [hex: :erlex, repo: "hexpm", optional: false]}], "hexpm", "cd6111e8017ccd563e65621a4d9a4a1c5cd333df30cebc7face8029cacb4eff6"},
"dns_cluster": {:hex, :dns_cluster, "0.1.3", "0bc20a2c88ed6cc494f2964075c359f8c2d00e1bf25518a6a6c7fd277c9b0c66", [:mix], [], "hexpm", "46cb7c4a1b3e52c7ad4cbe33ca5079fbde4840dedeafca2baf77996c2da1bc33"},
"earmark": {:hex, :earmark, "1.4.47", "7e7596b84fe4ebeb8751e14cbaeaf4d7a0237708f2ce43630cfd9065551f94ca", [:mix], [], "hexpm", "3e96bebea2c2d95f3b346a7ff22285bc68a99fbabdad9b655aa9c6be06c698f8"},
- "ecto": {:hex, :ecto, "3.12.4", "267c94d9f2969e6acc4dd5e3e3af5b05cdae89a4d549925f3008b2b7eb0b93c3", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "ef04e4101688a67d061e1b10d7bc1fbf00d1d13c17eef08b71d070ff9188f747"},
+ "ecto": {:hex, :ecto, "3.12.5", "4a312960ce612e17337e7cefcf9be45b95a3be6b36b6f94dfb3d8c361d631866", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "6eb18e80bef8bb57e17f5a7f068a1719fbda384d40fc37acb8eb8aeca493b6ea"},
"ecto_sql": {:hex, :ecto_sql, "3.12.1", "c0d0d60e85d9ff4631f12bafa454bc392ce8b9ec83531a412c12a0d415a3a4d0", [:mix], [{:db_connection, "~> 2.4.1 or ~> 2.5", [hex: :db_connection, repo: "hexpm", optional: false]}, {:ecto, "~> 3.12", [hex: :ecto, repo: "hexpm", optional: false]}, {:myxql, "~> 0.7", [hex: :myxql, repo: "hexpm", optional: true]}, {:postgrex, "~> 0.19 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}, {:tds, "~> 2.1.1 or ~> 2.2", [hex: :tds, repo: "hexpm", optional: true]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "aff5b958a899762c5f09028c847569f7dfb9cc9d63bdb8133bff8a5546de6bf5"},
+ "elixir_make": {:hex, :elixir_make, "0.9.0", "6484b3cd8c0cee58f09f05ecaf1a140a8c97670671a6a0e7ab4dc326c3109726", [:mix], [], "hexpm", "db23d4fd8b757462ad02f8aa73431a426fe6671c80b200d9710caf3d1dd0ffdb"},
"erlex": {:hex, :erlex, "0.2.7", "810e8725f96ab74d17aac676e748627a07bc87eb950d2b83acd29dc047a30595", [:mix], [], "hexpm", "3ed95f79d1a844c3f6bf0cea61e0d5612a42ce56da9c03f01df538685365efb0"},
"esbuild": {:hex, :esbuild, "0.8.1", "0cbf919f0eccb136d2eeef0df49c4acf55336de864e63594adcea3814f3edf41", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}], "hexpm", "25fc876a67c13cb0a776e7b5d7974851556baeda2085296c14ab48555ea7560f"},
"ex_machina": {:hex, :ex_machina, "2.8.0", "a0e847b5712065055ec3255840e2c78ef9366634d62390839d4880483be38abe", [:mix], [{:ecto, "~> 2.2 or ~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:ecto_sql, "~> 3.0", [hex: :ecto_sql, repo: "hexpm", optional: true]}], "hexpm", "79fe1a9c64c0c1c1fab6c4fa5d871682cb90de5885320c187d117004627a7729"},
+ "exla": {:hex, :exla, "0.9.2", "2b5cb7334f79fedc301502a793ffd10bc1ec8de2c61eebabcabf213fc98ae7e6", [:make, :mix], [{:elixir_make, "~> 0.6", [hex: :elixir_make, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.0", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:nx, "~> 0.9.0", [hex: :nx, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:xla, "~> 0.8.0", [hex: :xla, repo: "hexpm", optional: false]}], "hexpm", "e51085e196b466d235e93d9f5ea2cbf7d90315d216aa02e996f99bcaaa19c593"},
"expo": {:hex, :expo, "1.1.0", "f7b9ed7fb5745ebe1eeedf3d6f29226c5dd52897ac67c0f8af62a07e661e5c75", [:mix], [], "hexpm", "fbadf93f4700fb44c331362177bdca9eeb8097e8b0ef525c9cc501cb9917c960"},
"file_system": {:hex, :file_system, "1.0.1", "79e8ceaddb0416f8b8cd02a0127bdbababe7bf4a23d2a395b983c1f8b3f73edd", [:mix], [], "hexpm", "4414d1f38863ddf9120720cd976fce5bdde8e91d8283353f0e31850fa89feb9e"},
"finch": {:hex, :finch, "0.19.0", "c644641491ea854fc5c1bbaef36bfc764e3f08e7185e1f084e35e0672241b76d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:mint, "~> 1.6.2 or ~> 1.7", [hex: :mint, repo: "hexpm", optional: false]}, {:nimble_options, "~> 0.4 or ~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}, {:nimble_pool, "~> 1.1", [hex: :nimble_pool, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "fc5324ce209125d1e2fa0fcd2634601c52a787aff1cd33ee833664a5af4ea2b6"},
"floki": {:hex, :floki, "0.36.2", "a7da0193538c93f937714a6704369711998a51a6164a222d710ebd54020aa7a3", [:mix], [], "hexpm", "a8766c0bc92f074e5cb36c4f9961982eda84c5d2b8e979ca67f5c268ec8ed580"},
- "gettext": {:hex, :gettext, "0.26.1", "38e14ea5dcf962d1fc9f361b63ea07c0ce715a8ef1f9e82d3dfb8e67e0416715", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "01ce56f188b9dc28780a52783d6529ad2bc7124f9744e571e1ee4ea88bf08734"},
- "hpax": {:hex, :hpax, "1.0.0", "28dcf54509fe2152a3d040e4e3df5b265dcb6cb532029ecbacf4ce52caea3fd2", [:mix], [], "hexpm", "7f1314731d711e2ca5fdc7fd361296593fc2542570b3105595bb0bc6d0fad601"},
+ "gettext": {:hex, :gettext, "0.26.2", "5978aa7b21fada6deabf1f6341ddba50bc69c999e812211903b169799208f2a8", [:mix], [{:expo, "~> 0.5.1 or ~> 1.0", [hex: :expo, repo: "hexpm", optional: false]}], "hexpm", "aa978504bcf76511efdc22d580ba08e2279caab1066b76bb9aa81c4a1e0a32a5"},
+ "glob_ex": {:hex, :glob_ex, "0.1.11", "cb50d3f1ef53f6ca04d6252c7fde09fd7a1cf63387714fe96f340a1349e62c93", [:mix], [], "hexpm", "342729363056e3145e61766b416769984c329e4378f1d558b63e341020525de4"},
+ "hex_core": {:hex, :hex_core, "0.10.3", "7dc866ca8f5830566aacef141bdde4f1d61f687407c7070611c349e983f185d2", [:rebar3], [], "hexpm", "bc7c2b6ceb99e29550f74234952ad3b8153e6f2dbc38892bd0ff46043be073f5"},
+ "hpax": {:hex, :hpax, "1.0.2", "762df951b0c399ff67cc57c3995ec3cf46d696e41f0bba17da0518d94acd4aac", [:mix], [], "hexpm", "2f09b4c1074e0abd846747329eaa26d535be0eb3d189fa69d812bfb8bfefd32f"},
+ "igniter": {:hex, :igniter, "0.5.33", "799a49a8eb7e2fbebd6af2b770a856fa8ede9acad73a74269a04b8e775ead199", [:mix], [{:glob_ex, "~> 0.1.7", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:inflex, "~> 2.0", [hex: :inflex, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:owl, "~> 0.11", [hex: :owl, repo: "hexpm", optional: false]}, {:phx_new, "~> 1.7", [hex: :phx_new, repo: "hexpm", optional: true]}, {:req, "~> 0.5", [hex: :req, repo: "hexpm", optional: false]}, {:rewrite, ">= 1.1.1 and < 2.0.0-0", [hex: :rewrite, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.4", [hex: :sourceror, repo: "hexpm", optional: false]}, {:spitfire, ">= 0.1.3 and < 1.0.0-0", [hex: :spitfire, repo: "hexpm", optional: false]}], "hexpm", "ac962445d426dd3815e6b5568daa86586487293d126a946a67d9cf17d0665005"},
+ "inflex": {:hex, :inflex, "2.1.0", "a365cf0821a9dacb65067abd95008ca1b0bb7dcdd85ae59965deef2aa062924c", [:mix], [], "hexpm", "14c17d05db4ee9b6d319b0bff1bdf22aa389a25398d1952c7a0b5f3d93162dd8"},
"jason": {:hex, :jason, "1.4.4", "b9226785a9aa77b6857ca22832cffa5d5011a667207eb2a0ad56adb5db443b8a", [:mix], [{:decimal, "~> 1.0 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: true]}], "hexpm", "c5eb0cab91f094599f94d55bc63409236a8ec69a21a67814529e8d5f6cc90b3b"},
- "langchain": {:hex, :langchain, "0.3.0-rc.0", "930d22170fff2c599e8a63a664e437f896555b3cebe3055276bca37e7ae17d1b", [:mix], [{:abacus, "~> 2.1.0", [hex: :abacus, repo: "hexpm", optional: false]}, {:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:nx, ">= 0.7.0", [hex: :nx, repo: "hexpm", optional: true]}, {:req, ">= 0.5.0", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "c1f4f563cfddc502d3cfa5180fef154b8e194ef0f6f7bf0fe540761d2439b7ab"},
+ "langchain": {:hex, :langchain, "0.3.1", "df63af8f928438bd98253e569099109041ce985092227cbe8e1216612b0d4f23", [:mix], [{:abacus, "~> 2.1.0", [hex: :abacus, repo: "hexpm", optional: true]}, {:ecto, "~> 3.10", [hex: :ecto, repo: "hexpm", optional: false]}, {:gettext, "~> 0.20", [hex: :gettext, repo: "hexpm", optional: false]}, {:nimble_parsec, "~> 1.4", [hex: :nimble_parsec, repo: "hexpm", optional: true]}, {:nx, ">= 0.7.0", [hex: :nx, repo: "hexpm", optional: true]}, {:req, ">= 0.5.2", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "0cc40659e9a76e4e6599727d71fc841122f0aefc036a09a145caeaca8e45558b"},
"mime": {:hex, :mime, "2.0.6", "8f18486773d9b15f95f4f4f1e39b710045fa1de891fada4516559967276e4dc2", [:mix], [], "hexpm", "c9945363a6b26d747389aac3643f8e0e09d30499a138ad64fe8fd1d13d9b153e"},
- "mint": {:hex, :mint, "1.6.2", "af6d97a4051eee4f05b5500671d47c3a67dac7386045d87a904126fd4bbcea2e", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "5ee441dffc1892f1ae59127f74afe8fd82fda6587794278d924e4d90ea3d63f9"},
+ "mint": {:hex, :mint, "1.7.1", "113fdb2b2f3b59e47c7955971854641c61f378549d73e829e1768de90fc1abf1", [:mix], [{:castore, "~> 0.1.0 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: true]}, {:hpax, "~> 0.1.1 or ~> 0.2.0 or ~> 1.0", [hex: :hpax, repo: "hexpm", optional: false]}], "hexpm", "fceba0a4d0f24301ddee3024ae116df1c3f4bb7a563a731f45fdfeb9d39a231b"},
"nimble_options": {:hex, :nimble_options, "1.1.1", "e3a492d54d85fc3fd7c5baf411d9d2852922f66e69476317787a7b2bb000a61b", [:mix], [], "hexpm", "821b2470ca9442c4b6984882fe9bb0389371b8ddec4d45a9504f00a66f650b44"},
"nimble_pool": {:hex, :nimble_pool, "1.1.0", "bf9c29fbdcba3564a8b800d1eeb5a3c58f36e1e11d7b7fb2e084a643f645f06b", [:mix], [], "hexpm", "af2e4e6b34197db81f7aad230c1118eac993acc0dae6bc83bac0126d4ae0813a"},
+ "nx": {:hex, :nx, "0.9.2", "17563029c01bf749aad3c31234326d7665abd0acc33ee2acbe531a4759f29a8a", [:mix], [{:complex, "~> 0.5", [hex: :complex, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.0 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "914d74741617d8103de8ab1f8c880353e555263e1c397b8a1109f79a3716557f"},
+ "nx_image": {:hex, :nx_image, "0.1.2", "0c6e3453c1dc30fc80c723a54861204304cebc8a89ed3b806b972c73ee5d119d", [:mix], [{:nx, "~> 0.4", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "9161863c42405ddccb6dbbbeae078ad23e30201509cc804b3b3a7c9e98764b81"},
+ "nx_signal": {:hex, :nx_signal, "0.2.0", "e1ca0318877b17c81ce8906329f5125f1e2361e4c4235a5baac8a95ee88ea98e", [:mix], [{:nx, "~> 0.6", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "7247e5e18a177a59c4cb5355952900c62fdeadeb2bad02a9a34237b68744e2bb"},
+ "owl": {:hex, :owl, "0.12.2", "65906b525e5c3ef51bab6cba7687152be017aebe1da077bb719a5ee9f7e60762", [:mix], [{:ucwidth, "~> 0.2", [hex: :ucwidth, repo: "hexpm", optional: true]}], "hexpm", "6398efa9e1fea70a04d24231e10dcd66c1ac1aa2da418d20ef5357ec61de2880"},
+ "pgvector": {:hex, :pgvector, "0.3.0", "c55c7c0f6224b06105fc3214965c6217e4cfe907d7524cd8c27ba7612b7f8582", [:mix], [{:ecto, "~> 3.0", [hex: :ecto, repo: "hexpm", optional: true]}, {:nx, "~> 0.5", [hex: :nx, repo: "hexpm", optional: true]}, {:postgrex, ">= 0.0.0", [hex: :postgrex, repo: "hexpm", optional: false]}], "hexpm", "aeb7c36c5851881fd1d8a39e213472fa0b07bd72cdb0acabc693055aa14693ab"},
"phoenix": {:hex, :phoenix, "1.7.14", "a7d0b3f1bc95987044ddada111e77bd7f75646a08518942c72a8440278ae7825", [:mix], [{:castore, ">= 0.0.0", [hex: :castore, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:phoenix_pubsub, "~> 2.1", [hex: :phoenix_pubsub, repo: "hexpm", optional: false]}, {:phoenix_template, "~> 1.0", [hex: :phoenix_template, repo: "hexpm", optional: false]}, {:phoenix_view, "~> 2.0", [hex: :phoenix_view, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.7", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:plug_crypto, "~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}, {:websock_adapter, "~> 0.5.3", [hex: :websock_adapter, repo: "hexpm", optional: false]}], "hexpm", "c7859bc56cc5dfef19ecfc240775dae358cbaa530231118a9e014df392ace61a"},
"phoenix_ecto": {:hex, :phoenix_ecto, "4.6.2", "3b83b24ab5a2eb071a20372f740d7118767c272db386831b2e77638c4dcc606d", [:mix], [{:ecto, "~> 3.5", [hex: :ecto, repo: "hexpm", optional: false]}, {:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.1", [hex: :phoenix_html, repo: "hexpm", optional: true]}, {:plug, "~> 1.9", [hex: :plug, repo: "hexpm", optional: false]}, {:postgrex, "~> 0.16 or ~> 1.0", [hex: :postgrex, repo: "hexpm", optional: true]}], "hexpm", "3f94d025f59de86be00f5f8c5dd7b5965a3298458d21ab1c328488be3b5fcd59"},
"phoenix_html": {:hex, :phoenix_html, "4.1.1", "4c064fd3873d12ebb1388425a8f2a19348cef56e7289e1998e2d2fa758aa982e", [:mix], [], "hexpm", "f2f2df5a72bc9a2f510b21497fd7d2b86d932ec0598f0210fed4114adc546c6f"},
@@ -38,12 +52,27 @@
"phoenix_template": {:hex, :phoenix_template, "1.0.4", "e2092c132f3b5e5b2d49c96695342eb36d0ed514c5b252a77048d5969330d639", [:mix], [{:phoenix_html, "~> 2.14.2 or ~> 3.0 or ~> 4.0", [hex: :phoenix_html, repo: "hexpm", optional: true]}], "hexpm", "2c0c81f0e5c6753faf5cca2f229c9709919aba34fab866d3bc05060c9c444206"},
"plug": {:hex, :plug, "1.16.1", "40c74619c12f82736d2214557dedec2e9762029b2438d6d175c5074c933edc9d", [:mix], [{:mime, "~> 1.0 or ~> 2.0", [hex: :mime, repo: "hexpm", optional: false]}, {:plug_crypto, "~> 1.1.1 or ~> 1.2 or ~> 2.0", [hex: :plug_crypto, repo: "hexpm", optional: false]}, {:telemetry, "~> 0.4.3 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "a13ff6b9006b03d7e33874945b2755253841b238c34071ed85b0e86057f8cddc"},
"plug_crypto": {:hex, :plug_crypto, "2.1.0", "f44309c2b06d249c27c8d3f65cfe08158ade08418cf540fd4f72d4d6863abb7b", [:mix], [], "hexpm", "131216a4b030b8f8ce0f26038bc4421ae60e4bb95c5cf5395e1421437824c4fa"},
+ "polaris": {:hex, :polaris, "0.1.0", "dca61b18e3e801ecdae6ac9f0eca5f19792b44a5cb4b8d63db50fc40fc038d22", [:mix], [{:nx, "~> 0.5", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "13ef2b166650e533cb24b10e2f3b8ab4f2f449ba4d63156e8c569527f206e2c2"},
"postgrex": {:hex, :postgrex, "0.19.1", "73b498508b69aded53907fe48a1fee811be34cc720e69ef4ccd568c8715495ea", [:mix], [{:db_connection, "~> 2.1", [hex: :db_connection, repo: "hexpm", optional: false]}, {:decimal, "~> 1.5 or ~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: true]}, {:table, "~> 0.1.0", [hex: :table, repo: "hexpm", optional: true]}], "hexpm", "8bac7885a18f381e091ec6caf41bda7bb8c77912bb0e9285212829afe5d8a8f8"},
- "req": {:hex, :req, "0.5.6", "8fe1eead4a085510fe3d51ad854ca8f20a622aae46e97b302f499dfb84f726ac", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "cfaa8e720945d46654853de39d368f40362c2641c4b2153c886418914b372185"},
+ "progress_bar": {:hex, :progress_bar, "3.0.0", "f54ff038c2ac540cfbb4c2bfe97c75e7116ead044f3c2b10c9f212452194b5cd", [:mix], [{:decimal, "~> 2.0", [hex: :decimal, repo: "hexpm", optional: false]}], "hexpm", "6981c2b25ab24aecc91a2dc46623658e1399c21a2ae24db986b90d678530f2b7"},
+ "rag": {:hex, :rag, "0.2.1", "5c507b065c52ea3a48c82b8c45fb864f635acc2109917aa6068a7f403d65c6b1", [:mix], [{:igniter, "~> 0.5.7", [hex: :igniter, repo: "hexpm", optional: false]}, {:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nx, "~> 0.9.0", [hex: :nx, repo: "hexpm", optional: false]}, {:req, "~> 0.5.0", [hex: :req, repo: "hexpm", optional: false]}, {:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2e9870bd3f12b8234128048aeb5ced2b8c48975e33d3ef16b1ed679b53ab2468"},
+ "req": {:hex, :req, "0.5.8", "50d8d65279d6e343a5e46980ac2a70e97136182950833a1968b371e753f6a662", [:mix], [{:brotli, "~> 0.3.1", [hex: :brotli, repo: "hexpm", optional: true]}, {:ezstd, "~> 1.0", [hex: :ezstd, repo: "hexpm", optional: true]}, {:finch, "~> 0.17", [hex: :finch, repo: "hexpm", optional: false]}, {:jason, "~> 1.0", [hex: :jason, repo: "hexpm", optional: false]}, {:mime, "~> 2.0.6 or ~> 2.1", [hex: :mime, repo: "hexpm", optional: false]}, {:nimble_csv, "~> 1.0", [hex: :nimble_csv, repo: "hexpm", optional: true]}, {:plug, "~> 1.0", [hex: :plug, repo: "hexpm", optional: true]}], "hexpm", "d7fc5898a566477e174f26887821a3c5082b243885520ee4b45555f5d53f40ef"},
+ "req_hex": {:hex, :req_hex, "0.2.1", "f7566a61afde1f3f31977fdb230c2b9e801742da5de0b66a7b1e14febb304be7", [:mix], [{:hex_core, "~> 0.10.0", [hex: :hex_core, repo: "hexpm", optional: false]}, {:req, "~> 0.4.0 or ~> 0.5.0", [hex: :req, repo: "hexpm", optional: false]}], "hexpm", "f132012071714cfc277ff52ea5a80b793a4bebd79675d75820e02b425d897461"},
+ "rewrite": {:hex, :rewrite, "1.1.2", "f5a5d10f5fed1491a6ff48e078d4585882695962ccc9e6c779bae025d1f92eda", [:mix], [{:glob_ex, "~> 0.1", [hex: :glob_ex, repo: "hexpm", optional: false]}, {:sourceror, "~> 1.0", [hex: :sourceror, repo: "hexpm", optional: false]}, {:text_diff, "~> 0.1", [hex: :text_diff, repo: "hexpm", optional: false]}], "hexpm", "7f8b94b1e3528d0a47b3e8b7bfeca559d2948a65fa7418a9ad7d7712703d39d4"},
+ "rustler_precompiled": {:hex, :rustler_precompiled, "0.8.2", "5f25cbe220a8fac3e7ad62e6f950fcdca5a5a5f8501835d2823e8c74bf4268d5", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, "~> 0.23", [hex: :rustler, repo: "hexpm", optional: true]}], "hexpm", "63d1bd5f8e23096d1ff851839923162096364bac8656a4a3c00d1fff8e83ee0a"},
+ "safetensors": {:hex, :safetensors, "0.1.3", "7ff3c22391e213289c713898481d492c9c28a49ab1d0705b72630fb8360426b2", [:mix], [{:jason, "~> 1.4", [hex: :jason, repo: "hexpm", optional: false]}, {:nx, "~> 0.5", [hex: :nx, repo: "hexpm", optional: false]}], "hexpm", "fe50b53ea59fde4e723dd1a2e31cfdc6013e69343afac84c6be86d6d7c562c14"},
+ "sourceror": {:hex, :sourceror, "1.7.1", "599d78f4cc2be7d55c9c4fd0a8d772fd0478e3a50e726697c20d13d02aa056d4", [:mix], [], "hexpm", "cd6f268fe29fa00afbc535e215158680a0662b357dc784646d7dff28ac65a0fc"},
+ "spitfire": {:hex, :spitfire, "0.2.0", "0de1f519a23f65bde40d316adad53c07a9563f25cc68915d639d8a509a0aad8a", [:mix], [], "hexpm", "743daaee2d81a0d8095431729f478ce49b47ea8943c7d770de86704975cb7775"},
"telemetry": {:hex, :telemetry, "1.3.0", "fedebbae410d715cf8e7062c96a1ef32ec22e764197f70cda73d82778d61e7a2", [:rebar3], [], "hexpm", "7015fc8919dbe63764f4b4b87a95b7c0996bd539e0d499be6ec9d7f3875b79e6"},
"telemetry_metrics": {:hex, :telemetry_metrics, "1.0.0", "29f5f84991ca98b8eb02fc208b2e6de7c95f8bb2294ef244a176675adc7775df", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "f23713b3847286a534e005126d4c959ebcca68ae9582118ce436b521d1d47d5d"},
"telemetry_poller": {:hex, :telemetry_poller, "1.1.0", "58fa7c216257291caaf8d05678c8d01bd45f4bdbc1286838a28c4bb62ef32999", [:rebar3], [{:telemetry, "~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "9eb9d9cbfd81cbd7cdd24682f8711b6e2b691289a0de6826e58452f28c103c8f"},
+ "text_chunker": {:hex, :text_chunker, "0.3.2", "bf587de84dcd405215095201ac9e2cff0d0a2c49d0fe1d8f16461944fdb124ae", [:mix], [{:nimble_options, "~> 1.0", [hex: :nimble_options, repo: "hexpm", optional: false]}], "hexpm", "84061ef0a861065ca3be66c5e3a8e0725cb0b31210f614b8b7eb135c9e6cb6b2"},
+ "text_diff": {:hex, :text_diff, "0.1.0", "1caf3175e11a53a9a139bc9339bd607c47b9e376b073d4571c031913317fecaa", [:mix], [], "hexpm", "d1ffaaecab338e49357b6daa82e435f877e0649041ace7755583a0ea3362dbd7"},
"thousand_island": {:hex, :thousand_island, "1.3.5", "6022b6338f1635b3d32406ff98d68b843ba73b3aa95cfc27154223244f3a6ca5", [:mix], [{:telemetry, "~> 0.4 or ~> 1.0", [hex: :telemetry, repo: "hexpm", optional: false]}], "hexpm", "2be6954916fdfe4756af3239fb6b6d75d0b8063b5df03ba76fd8a4c87849e180"},
+ "tokenizers": {:hex, :tokenizers, "0.5.1", "b0975d92b4ee5b18e8f47b5d65b9d5f1e583d9130189b1a2620401af4e7d4b35", [:mix], [{:castore, "~> 0.1 or ~> 1.0", [hex: :castore, repo: "hexpm", optional: false]}, {:rustler, ">= 0.0.0", [hex: :rustler, repo: "hexpm", optional: true]}, {:rustler_precompiled, "~> 0.6", [hex: :rustler_precompiled, repo: "hexpm", optional: false]}], "hexpm", "5f08d97cc7f2ed3d71d370d68120da6d3de010948ccf676c9c0eb591ba4bacc9"},
+ "unpickler": {:hex, :unpickler, "0.1.0", "c2262c0819e6985b761e7107546cef96a485f401816be5304a65fdd200d5bd6a", [:mix], [], "hexpm", "e2b3f61e62406187ac52afead8a63bfb4e49394028993f3c4c42712743cab79e"},
+ "unzip": {:hex, :unzip, "0.12.0", "beed92238724732418b41eba77dcb7f51e235b707406c05b1732a3052d1c0f36", [:mix], [], "hexpm", "95655b72db368e5a84951f0bed586ac053b55ee3815fd96062fce10ce4fc998d"},
"websock": {:hex, :websock, "0.5.3", "2f69a6ebe810328555b6fe5c831a851f485e303a7c8ce6c5f675abeb20ebdadc", [:mix], [], "hexpm", "6105453d7fac22c712ad66fab1d45abdf049868f253cf719b625151460b8b453"},
"websock_adapter": {:hex, :websock_adapter, "0.5.7", "65fa74042530064ef0570b75b43f5c49bb8b235d6515671b3d250022cb8a1f9e", [:mix], [{:bandit, ">= 0.6.0", [hex: :bandit, repo: "hexpm", optional: true]}, {:plug, "~> 1.14", [hex: :plug, repo: "hexpm", optional: false]}, {:plug_cowboy, "~> 2.6", [hex: :plug_cowboy, repo: "hexpm", optional: true]}, {:websock, "~> 0.5", [hex: :websock, repo: "hexpm", optional: false]}], "hexpm", "d0f478ee64deddfec64b800673fd6e0c8888b079d9f3444dd96d2a98383bdbd1"},
+ "xla": {:hex, :xla, "0.8.0", "fef314d085dd3ee16a0816c095239938f80769150e15db16dfaa435553d7cb16", [:make, :mix], [{:elixir_make, "~> 0.4", [hex: :elixir_make, repo: "hexpm", optional: false]}], "hexpm", "739c61c8d93b97e12ba0369d10e76130224c208f1a76ad293e3581f056833e57"},
}
diff --git a/priv/repo/migrations/20250106193459_add_sources_to_messages.exs b/priv/repo/migrations/20250106193459_add_sources_to_messages.exs
new file mode 100644
index 0000000..d7fddc4
--- /dev/null
+++ b/priv/repo/migrations/20250106193459_add_sources_to_messages.exs
@@ -0,0 +1,9 @@
+defmodule Chatbot.Repo.Migrations.AddSourcesToMessages do
+ use Ecto.Migration
+
+ def change do
+ alter table(:messages) do
+ add(:sources, {:array, :string})
+ end
+ end
+end
diff --git a/priv/repo/migrations/20250311215959_create_chunks_table.exs b/priv/repo/migrations/20250311215959_create_chunks_table.exs
new file mode 100644
index 0000000..3c48071
--- /dev/null
+++ b/priv/repo/migrations/20250311215959_create_chunks_table.exs
@@ -0,0 +1,23 @@
+defmodule Chatbot.Repo.Migrations.CreateChunksTable do
+ use Ecto.Migration
+
+ def up() do
+ execute("CREATE EXTENSION IF NOT EXISTS vector")
+
+ flush()
+
+ create table(:chunks) do
+ add(:document, :text)
+ add(:source, :text)
+ add(:chunk, :text)
+ add(:embedding, :vector, size: 768)
+
+ timestamps()
+ end
+ end
+
+ def down() do
+ drop(table(:chunks))
+ execute("DROP EXTENSION vector")
+ end
+end