Skip to content

Rag/errata #27

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Open
wants to merge 25 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
25 commits
Select commit Hold shift + click to select a range
dc948c6
[#INTERNAL-498] Remove repeated sentence in seeds
xhr15 Mar 9, 2025
777d85a
[#INTERNAL-499] Standard system prompt
xhr15 Mar 9, 2025
ec2d2cd
[#INTERNAL-500] Add Markdown to chatbot rendering
xhr15 Mar 9, 2025
50749cc
[#STEP-1-Generator]
xhr15 Mar 11, 2025
e3f360e
added rag
xhr15 Mar 11, 2025
c847dc5
changes generated by mix rag.install pgvector
xhr15 Mar 11, 2025
abee4c7
[#STEP-1-Generator]
xhr15 Mar 11, 2025
496d866
[#STEP-2-Custoisations]
xhr15 Mar 12, 2025
64937d0
took out nx serving for text generation
xhr15 Mar 12, 2025
f8ff490
[#STEP-3-Ingest-Ecto]
joelpaulkoch Mar 12, 2025
fc74aa8
Use jina-embeddings-v2-base-code model
joelpaulkoch Mar 12, 2025
4ec1e81
Update pipeline to ingest ecto docs and code
joelpaulkoch Mar 12, 2025
d15853a
[#STEP-4-Langchain-Integration]
joelpaulkoch Mar 12, 2025
af4b1a8
Langchain integration
joelpaulkoch Mar 12, 2025
a683d44
[#STEP-5-Show-Current-Activity]
joelpaulkoch Mar 12, 2025
a0095d2
Show current activity with telemetry
joelpaulkoch Mar 12, 2025
5d4cae9
Remove unused aliases
joelpaulkoch Mar 12, 2025
a1969a9
Make fulltext search more robust
joelpaulkoch Mar 12, 2025
cf4b4cd
Fix padding of lists
joelpaulkoch Mar 12, 2025
532513a
Update langchain to 0.3.1
joelpaulkoch Mar 13, 2025
e203b59
Update rag to latest from github
joelpaulkoch Mar 13, 2025
5c1727f
Format
joelpaulkoch Mar 13, 2025
785aff7
Fix code readability credo issues
joelpaulkoch Mar 13, 2025
f81d81d
Fix software design credo issues
joelpaulkoch Mar 13, 2025
e42d2ad
Bump rag to 0.2.1
joelpaulkoch Mar 13, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
32 changes: 32 additions & 0 deletions assets/css/app.scss
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
3 changes: 3 additions & 0 deletions config/config.exs
Original file line number Diff line number Diff line change
Expand Up @@ -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"
Expand Down
1 change: 1 addition & 0 deletions config/dev.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import Config
1 change: 1 addition & 0 deletions config/prod.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import Config
1 change: 1 addition & 0 deletions config/test.exs
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
import Config
63 changes: 63 additions & 0 deletions eval/rag_triad_eval.exs
Original file line number Diff line number Diff line change
@@ -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}")
17 changes: 17 additions & 0 deletions lib/chatbot/application.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand All @@ -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]
Expand Down
33 changes: 10 additions & 23 deletions lib/chatbot/chat.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down Expand Up @@ -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"}
Expand All @@ -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})
Expand All @@ -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.
"""
Expand Down
3 changes: 2 additions & 1 deletion lib/chatbot/chat/message.ex
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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
Expand Down
51 changes: 0 additions & 51 deletions lib/chatbot/llm_mock.ex

This file was deleted.

Loading