Last active
June 17, 2025 09:05
-
-
Save joelpaulkoch/9192abd23bd2e6ff76be314c24173974 to your computer and use it in GitHub Desktop.
A RAG for Elixir in Elixir
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Mix.install( | |
[ | |
{:phoenix_playground, "~> 0.1.6"}, | |
{:phoenix, "~> 1.7.14"}, | |
{:phoenix_live_view, "~> 1.0.0-rc.1"}, | |
{:chroma, "~> 0.1.3"}, | |
{:text_chunker, "~> 0.3.1"}, | |
{:nx, "~> 0.9.0"}, | |
{:exla, "~> 0.9.1"}, | |
{:axon, "~> 0.7.0"}, | |
{:bumblebee, github: "joelpaulkoch/bumblebee", branch: "jina-embeddings-v2-base-code"} | |
], | |
config: [ | |
chroma: [host: "http://localhost:8000", api_base: "api", api_version: "v1"], | |
nx: [default_backend: EXLA.Backend] | |
] | |
) | |
defmodule RagTime.Serving do | |
def build_embedding_serving() do | |
repo = {:hf, "jinaai/jina-embeddings-v2-base-code"} | |
{:ok, model_info} = | |
Bumblebee.load_model(repo, | |
spec_overrides: [architecture: :base], | |
params_filename: "model.safetensors" | |
) | |
{:ok, tokenizer} = Bumblebee.load_tokenizer(repo) | |
Bumblebee.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, "microsoft/phi-3.5-mini-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: 512) | |
Bumblebee.Text.generation(model_info, tokenizer, generation_config, | |
compile: [batch_size: 1, sequence_length: 6000], | |
defn_options: [compiler: EXLA], | |
stream: false | |
) | |
end | |
end | |
defmodule RagTime.Ingestion do | |
def ingest(collection, input_path) do | |
files = | |
Path.wildcard(input_path <> "/**/*.{ex, exs}") | |
|> Enum.reject(&String.contains?(&1, ["/_build/", "/deps/"])) | |
files_content = for file <- files, do: File.read!(file) | |
documents = | |
Enum.zip_with(files, files_content, fn file, content -> | |
%{content: content, source: file} | |
end) | |
chunks = chunk_with_metadata(documents, :elixir) | |
embeddings = generate_embeddings(chunks) | |
store_embeddings_and_chunks(collection, embeddings, chunks) | |
end | |
def chunk_with_metadata(documents, format) do | |
chunks = Enum.map(documents, &TextChunker.split(&1.content, format: format)) | |
sources = Enum.map(documents, & &1.source) | |
Enum.zip(sources, chunks) | |
|> Enum.flat_map(fn {source, source_chunks} -> | |
for chunk <- source_chunks do | |
%{ | |
source: source, | |
start_byte: chunk.start_byte, | |
end_byte: chunk.end_byte, | |
text: chunk.text | |
} | |
end | |
end) | |
end | |
def generate_embeddings(chunks) do | |
chunk_text_list = Enum.map(chunks, & &1.text) | |
Nx.Serving.batched_run(RagTime.EmbeddingServing, chunk_text_list) | |
|> Enum.map(fn %{embedding: embedding} -> Nx.to_list(embedding) end) | |
end | |
def store_embeddings_and_chunks(collection, embeddings, chunks) do | |
documents = Enum.map(chunks, & &1.text) | |
ids = Enum.map(chunks, &chunk_to_id(&1)) | |
Chroma.Collection.add(collection, %{documents: documents, ids: ids, embeddings: embeddings}) | |
end | |
defp chunk_to_id(%{source: path, start_byte: start_byte, end_byte: end_byte}) do | |
file_content = File.read!(path) | |
start_line = | |
file_content | |
|> String.byte_slice(0, start_byte) | |
|> String.split("\n") | |
|> Enum.count() | |
end_line = | |
file_content | |
|> String.byte_slice(0, end_byte) | |
|> String.split("\n") | |
|> Enum.count() | |
"#{path}:#{start_line}-#{end_line}" | |
end | |
end | |
defmodule RagTime.Retrieval do | |
def retrieve(collection, query) do | |
%{embedding: query_embedding} = Nx.Serving.batched_run(RagTime.EmbeddingServing, query) | |
query_embedding = Nx.to_list(query_embedding) | |
{:ok, results} = | |
Chroma.Collection.query(collection, | |
results: 10, | |
query_embeddings: [query_embedding] | |
) | |
[code_chunks] = results["documents"] | |
[sources] = results["ids"] | |
{code_chunks, sources} | |
end | |
end | |
defmodule RagTime.Generation do | |
def generate_response(query, context_documents, context_sources) do | |
context = | |
Enum.map(context_documents, fn code_chunk -> | |
""" | |
[...] | |
#{code_chunk} | |
[...] | |
""" | |
end) | |
|> Enum.join("\n\n") | |
prompt = """ | |
<|system|> | |
You are a helpful assistant.</s> | |
<|user|> | |
Context information is below. | |
--------------------- | |
#{context} | |
--------------------- | |
Given the context information and no prior knowledge, answer the query. | |
Query: #{query} | |
Answer: </s> | |
<|assistant|> | |
""" | |
%{results: [result]} = Nx.Serving.batched_run(RagTime.LLMServing, prompt) | |
%{ | |
query: query, | |
context: context, | |
context_sources: context_sources, | |
response: result.text | |
} | |
end | |
end | |
defmodule RagTime do | |
def ingest(collection, path), do: RagTime.Ingestion.ingest(collection, path) | |
def query(collection, query) do | |
{context, sources} = RagTime.Retrieval.retrieve(collection, query) | |
RagTime.Generation.generate_response(query, context, sources) | |
end | |
end | |
defmodule RagLive.Style do | |
def styles do | |
""" | |
:root { | |
--bs-transition-default-duration: 0.075s; | |
--bs-transition-default-easing: ease-in-out; | |
--bs-transition-long-duration: 0.5s; | |
--bs-transition-long-easing: ease-in-out; | |
} | |
:root { | |
--bs-color-brand-1-light-4: rgb(243.95, 242.25, 255); | |
--bs-color-brand-1-light-4-rgb: | |
244, | |
242, | |
255; | |
--bs-color-brand-1-light-3: rgb(221.85, 216.75, 255); | |
--bs-color-brand-1-light-3-rgb: | |
222, | |
217, | |
255; | |
--bs-color-brand-1-light-2: rgb(199.75, 191.25, 255); | |
--bs-color-brand-1-light-2-rgb: | |
200, | |
191, | |
255; | |
--bs-color-brand-1-light-1: rgb(177.65, 165.75, 255); | |
--bs-color-brand-1-light-1-rgb: | |
178, | |
166, | |
255; | |
--bs-color-brand-1: #9485ff; | |
--bs-color-brand-1-rgb: | |
148, | |
133, | |
255; | |
--bs-color-brand-1-dark-1: rgb(100.3, 76.5, 255); | |
--bs-color-brand-1-dark-1-rgb: | |
100, | |
77, | |
255; | |
--bs-color-brand-1-dark-2: rgb(30.6, 0, 229.5); | |
--bs-color-brand-1-dark-2-rgb: | |
31, | |
0, | |
230; | |
--bs-color-brand-1-dark-3: rgb(13.6, 0, 102); | |
--bs-color-brand-1-dark-3-rgb: | |
14, | |
0, | |
102; | |
--bs-color-brand-1-dark-4: rgb(6.8, 0, 51); | |
--bs-color-brand-1-dark-4-rgb: | |
7, | |
0, | |
51; | |
--bs-color-brand-2-light-4: rgb(229.5, 255, 253.7); | |
--bs-color-brand-2-light-4-rgb: | |
229, | |
255, | |
254; | |
--bs-color-brand-2-light-3: rgb(178.5, 255, 251.1); | |
--bs-color-brand-2-light-3-rgb: | |
179, | |
255, | |
251; | |
--bs-color-brand-2-light-2: rgb(140.25, 255, 249.15); | |
--bs-color-brand-2-light-2-rgb: | |
140, | |
255, | |
249; | |
--bs-color-brand-2-light-1: rgb(51, 255, 244.6); | |
--bs-color-brand-2-light-1-rgb: | |
51, | |
255, | |
245; | |
--bs-color-brand-2: #00e5da; | |
--bs-color-brand-2-rgb: | |
0, | |
229, | |
218; | |
--bs-color-brand-2-dark-1: rgb(0, 153, 145.2); | |
--bs-color-brand-2-dark-1-rgb: | |
0, | |
153, | |
145; | |
--bs-color-brand-2-dark-2: rgb(0, 127.5, 121); | |
--bs-color-brand-2-dark-2-rgb: | |
0, | |
128, | |
121; | |
--bs-color-brand-2-dark-3: #005752; | |
--bs-color-brand-2-dark-3-rgb: | |
0, | |
87, | |
82; | |
--bs-color-brand-2-dark-4: rgb(0, 38.25, 36.3); | |
--bs-color-brand-2-dark-4-rgb: | |
0, | |
38, | |
36; | |
--bs-color-grayscale-white: white; | |
--bs-color-grayscale-white-rgb: | |
255, | |
255, | |
255; | |
--bs-color-grayscale-light-4: rgb(248.631375, 248.401875, 250.123125); | |
--bs-color-grayscale-light-4-rgb: | |
249, | |
248, | |
250; | |
--bs-color-grayscale-light-3: rgb(235.894125, 235.205625, 240.369375); | |
--bs-color-grayscale-light-3-rgb: | |
236, | |
235, | |
240; | |
--bs-color-grayscale-light-2: rgb(216.78825, 215.41125, 225.73875); | |
--bs-color-grayscale-light-2-rgb: | |
217, | |
215, | |
226; | |
--bs-color-grayscale-light-1: rgb(191.31375, 189.01875, 206.23125); | |
--bs-color-grayscale-light-1-rgb: | |
191, | |
189, | |
206; | |
--bs-color-grayscale: rgb(140.36475, 136.23375, 167.21625); | |
--bs-color-grayscale-rgb: | |
140, | |
136, | |
167; | |
--bs-color-grayscale-dark-1: rgb(99.858, 95.37, 129.03); | |
--bs-color-grayscale-dark-1-rgb: | |
100, | |
95, | |
129; | |
--bs-color-grayscale-dark-2: rgb(81.134625, 77.488125, 104.836875); | |
--bs-color-grayscale-dark-2-rgb: | |
81, | |
77, | |
105; | |
--bs-color-grayscale-dark-3: rgb(49.929, 47.685, 64.515); | |
--bs-color-grayscale-dark-3-rgb: | |
50, | |
48, | |
65; | |
--bs-color-grayscale-dark-4: rgb(37.44675, 35.76375, 48.38625); | |
--bs-color-grayscale-dark-4-rgb: | |
37, | |
36, | |
48; | |
--bs-color-grayscale-black: black; | |
--bs-color-grayscale-black-rgb: | |
0, | |
0, | |
0; | |
--bs-color-danger-light-4: rgb(255, 242.25, 243.55); | |
--bs-color-danger-light-4-rgb: | |
255, | |
242, | |
244; | |
--bs-color-danger-light-3: rgb(255, 216.75, 220.65); | |
--bs-color-danger-light-3-rgb: | |
255, | |
217, | |
221; | |
--bs-color-danger-light-2: rgb(255, 191.25, 197.75); | |
--bs-color-danger-light-2-rgb: | |
255, | |
191, | |
198; | |
--bs-color-danger-light-1: rgb(255, 153, 163.4); | |
--bs-color-danger-light-1-rgb: | |
255, | |
153, | |
163; | |
--bs-color-danger: rgb(255, 102, 117.6); | |
--bs-color-danger-rgb: | |
255, | |
102, | |
118; | |
--bs-color-danger-dark-1: rgb(216.75, 0, 22.1); | |
--bs-color-danger-dark-1-rgb: | |
217, | |
0, | |
22; | |
--bs-color-danger-dark-2: rgb(153, 0, 15.6); | |
--bs-color-danger-dark-2-rgb: | |
153, | |
0, | |
16; | |
--bs-color-danger-dark-3: rgb(89.25, 0, 9.1); | |
--bs-color-danger-dark-3-rgb: | |
89, | |
0, | |
9; | |
--bs-color-danger-dark-4: rgb(63.75, 0, 6.5); | |
--bs-color-danger-dark-4-rgb: | |
64, | |
0, | |
7; | |
--bs-color-warning-light-4: rgb(255, 243.5, 229.5); | |
--bs-color-warning-light-4-rgb: | |
255, | |
243, | |
229; | |
--bs-color-warning-light-3: #ffe8cc; | |
--bs-color-warning-light-3-rgb: | |
255, | |
232, | |
204; | |
--bs-color-warning-light-2: rgb(255, 214.75, 165.75); | |
--bs-color-warning-light-2-rgb: | |
255, | |
215, | |
166; | |
--bs-color-warning-light-1: rgb(255, 203.25, 140.25); | |
--bs-color-warning-light-1-rgb: | |
255, | |
203, | |
140; | |
--bs-color-warning: rgb(255, 168.75, 63.75); | |
--bs-color-warning-rgb: | |
255, | |
169, | |
64; | |
--bs-color-warning-dark-1: darkorange; | |
--bs-color-warning-dark-1-rgb: | |
255, | |
140, | |
0; | |
--bs-color-warning-dark-2: rgb(165.75, 91, 0); | |
--bs-color-warning-dark-2-rgb: | |
166, | |
91, | |
0; | |
--bs-color-warning-dark-3: #613500; | |
--bs-color-warning-dark-3-rgb: | |
97, | |
53, | |
0; | |
--bs-color-warning-dark-4: #331c00; | |
--bs-color-warning-dark-4-rgb: | |
51, | |
28, | |
0; | |
--bs-color-positive-light-4: rgb(216.75, 255, 240.3); | |
--bs-color-positive-light-4-rgb: | |
217, | |
255, | |
240; | |
--bs-color-positive-light-3: rgb(178.5, 255, 225.6); | |
--bs-color-positive-light-3-rgb: | |
179, | |
255, | |
226; | |
--bs-color-positive-light-2: rgb(114.75, 255, 201.1); | |
--bs-color-positive-light-2-rgb: | |
115, | |
255, | |
201; | |
--bs-color-positive-light-1: rgb(25.5, 255, 166.8); | |
--bs-color-positive-light-1-rgb: | |
26, | |
255, | |
167; | |
--bs-color-positive: rgb(0, 216.75, 133.45); | |
--bs-color-positive-rgb: | |
0, | |
217, | |
133; | |
--bs-color-positive-dark-1: rgb(0, 153, 94.2); | |
--bs-color-positive-dark-1-rgb: | |
0, | |
153, | |
94; | |
--bs-color-positive-dark-2: rgb(0, 102, 62.8); | |
--bs-color-positive-dark-2-rgb: | |
0, | |
102, | |
63; | |
--bs-color-positive-dark-3: rgb(0, 63.75, 39.25); | |
--bs-color-positive-dark-3-rgb: | |
0, | |
64, | |
39; | |
--bs-color-positive-dark-4: rgb(0, 38.25, 23.55); | |
--bs-color-positive-dark-4-rgb: | |
0, | |
38, | |
24; | |
} | |
:root { | |
--bs-size-0: 0; | |
--bs-size-s7: 0.125rem; | |
--bs-size-s6: 0.25rem; | |
--bs-size-s5: 0.375rem; | |
--bs-size-s4: 0.5rem; | |
--bs-size-s3: 0.625rem; | |
--bs-size-s2: 0.75rem; | |
--bs-size-s1: 0.875rem; | |
--bs-size-m: 1rem; | |
--bs-size-l1: 1.25rem; | |
--bs-size-l2: 1.5rem; | |
--bs-size-l3: 2rem; | |
--bs-size-l4: 2.5rem; | |
--bs-size-l5: 3rem; | |
--bs-size-l6: 4rem; | |
--bs-size-l7: 5rem; | |
--bs-z-index-base: 0; | |
--bs-z-index-overlay: 10; | |
--bs-z-index-top: 20; | |
} | |
:root { | |
--bs-font-size-1: 0.75rem; | |
--bs-font-size-2: 0.875rem; | |
--bs-font-size-3: 1rem; | |
--bs-font-size-4: 1.125rem; | |
--bs-font-size-5: 1.25rem; | |
--bs-font-size-6: 1.5rem; | |
--bs-font-size-7: 2rem; | |
--bs-font-size-8: 3rem; | |
--bs-line-height-1: 1.1667; | |
--bs-line-height-2: 1.25; | |
--bs-line-height-3: 1.4; | |
--bs-line-height-4: 1.5555; | |
--bs-line-height-5: 1.67; | |
--bs-font-weight-thin: 100; | |
--bs-font-weight-extralight: 200; | |
--bs-font-weight-light: 300; | |
--bs-font-weight-normal: 400; | |
--bs-font-weight-medium: 500; | |
--bs-font-weight-semibold: 600; | |
--bs-font-weight-bold: 700; | |
--bs-font-weight-extrabold: 800; | |
--bs-font-weight-black: 900; | |
} | |
:root { | |
--bs-content-padding-base: var(--bs-size-s1); | |
--bs-content-padding-l1: var(--bs-size-m); | |
} | |
html { | |
box-sizing: border-box; | |
} | |
*, | |
*::before, | |
*::after { | |
box-sizing: inherit; | |
} | |
html, | |
body, | |
#root { | |
height: 100%; | |
min-height: stretch; | |
} | |
button, | |
input, | |
textarea { | |
font-family: inherit; | |
font-size: 100%; | |
line-height: inherit; | |
margin: 0; | |
} | |
button, | |
input { | |
overflow: visible; | |
} | |
button { | |
text-transform: none; | |
} | |
button, | |
[type=button], | |
[type=reset], | |
[type=submit] { | |
-webkit-appearance: button; | |
} | |
button::-moz-focus-inner, | |
[type=button]::-moz-focus-inner, | |
[type=reset]::-moz-focus-inner, | |
[type=submit]::-moz-focus-inner { | |
border-style: none; | |
padding: 0; | |
} | |
button:-moz-focusring, | |
[type=button]:-moz-focusring, | |
[type=reset]:-moz-focusring, | |
[type=submit]:-moz-focusring { | |
outline: 1px dotted ButtonText; | |
} | |
textarea { | |
overflow: auto; | |
} | |
html, | |
body, | |
p, | |
textarea, | |
button, | |
input { | |
margin: 0; | |
} | |
html { | |
font-family: | |
-apple-system, | |
BlinkMacSystemFont, | |
Roboto, | |
helvetica, | |
arial, | |
sans-serif; | |
line-height: var(--bs-line-height-4); | |
-webkit-text-size-adjust: 100%; | |
} | |
textarea, | |
input { | |
transition: | |
color var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
background-color var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
border var(--bs-transition-default-duration) var(--bs-transition-default-easing); | |
resize: vertical; | |
} | |
textarea::placeholder, | |
input::placeholder { | |
opacity: 1; | |
color: var(--bs-color-grayscale-light-1); | |
font-style: italic; | |
font-weight: var(--bs-font-weight-normal); | |
} | |
[type=text], | |
textarea { | |
display: block; | |
width: 100%; | |
padding: var(--bs-size-s5) var(--bs-size-s5); | |
border: var(--bs-size-s7) solid var(--bs-color-grayscale); | |
border-radius: var(--bs-size-s5); | |
background-color: var(--bs-color-grayscale-white); | |
box-shadow: | |
0 0 calc(var(--bs-size-s5) / 4) rgba(var(--bs-color-grayscale-light-1-rgb), 0.025), | |
0 0 calc(var(--bs-size-s5) / 3) rgba(var(--bs-color-grayscale-light-1-rgb), 0.025), | |
0 0 calc(var(--bs-size-s5) / 2) rgba(var(--bs-color-grayscale-light-1-rgb), 0.025), | |
0 0 calc(var(--bs-size-s5) / 1) rgba(var(--bs-color-grayscale-light-1-rgb), 0.025); | |
color: var(--bs-color-grayscale); | |
font: inherit; | |
} | |
[type=text]:hover, | |
textarea:hover { | |
border-color: var(--bs-color-grayscale-dark-1); | |
background-color: var(--bs-color-grayscale); | |
box-shadow: | |
0 0 calc(var(--bs-size-s2) / 4) rgba(var(--bs-color-brand-1-light-1-rgb), 0.025), | |
0 0 calc(var(--bs-size-s2) / 3) rgba(var(--bs-color-brand-1-light-1-rgb), 0.025), | |
0 0 calc(var(--bs-size-s2) / 2) rgba(var(--bs-color-brand-1-light-1-rgb), 0.025), | |
0 0 calc(var(--bs-size-s2) / 1) rgba(var(--bs-color-brand-1-light-1-rgb), 0.025); | |
color: var(--bs-color-grayscale-dark-1); | |
} | |
[type=text]:active, | |
[type=text]:focus, | |
[type=url]:active, | |
[type=url]:focus, | |
textarea:active, | |
textarea:focus { | |
border-color: var(--bs-color-grayscale-dark-1); | |
outline: none; | |
background-color: var(--bs-color-grayscale-white); | |
box-shadow: | |
0 0 calc(var(--bs-size-s6) / 4) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s6) / 3) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s6) / 2) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s6) / 1) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075); | |
color: var(--bs-color-grayscale-dark-1); | |
} | |
[type=text]:invalid, | |
[type=text][aria-invalid=true], | |
textarea:invalid, | |
textarea[aria-invalid=true] { | |
border-color: var(--bs-color-warning); | |
background-color: var(--bs-color-grayscale-white); | |
box-shadow: | |
0 0 calc(var(--bs-size-s4) / 4) rgba(var(--bs-color-warning-rgb), 0.05), | |
0 0 calc(var(--bs-size-s4) / 3) rgba(var(--bs-color-warning-rgb), 0.05), | |
0 0 calc(var(--bs-size-s4) / 2) rgba(var(--bs-color-warning-rgb), 0.05), | |
0 0 calc(var(--bs-size-s4) / 1) rgba(var(--bs-color-warning-rgb), 0.05); | |
color: var(--bs-color-warning); | |
} | |
[type=text]:disabled, | |
textarea:disabled { | |
border-color: var(--bs-color-grayscale-light-2); | |
background: var(--bs-color-grayscale-light-2); | |
box-shadow: none; | |
color: var(--bs-color-grayscale); | |
cursor: default; | |
} | |
:root, | |
[data-theme] { | |
background-color: var(--bs-theme-background); | |
color: var(--bs-theme-text); | |
} | |
:root, | |
[data-theme=default] { | |
--bs-theme-text: var(--bs-color-grayscale-dark-3); | |
--bs-theme-background: var(--bs-color-grayscale-white); | |
} | |
[data-theme=dark] { | |
--bs-theme-text: var(--bs-color-grayscale-light-3); | |
--bs-theme-background: var(--bs-color-grayscale-dark-3); | |
} | |
[data-theme=brand-1] { | |
--bs-theme-text: var(--bs-color-brand-1-dark-2); | |
--bs-theme-text-dark: var(--bs-color-brand-1-dark-2); | |
--bs-theme-background: var(--bs-color-brand-1-light-4); | |
--bs-theme-background-dark: var(--bs-color-brand-1-light-2); | |
} | |
[data-theme=brand-2] { | |
--bs-theme-text: var(--bs-color-brand-2-dark-2); | |
--bs-theme-background: var(--bs-color-brand-2-light-4); | |
} | |
[data-theme=positive] { | |
--bs-theme-text: var(--bs-color-positive-dark-2); | |
--bs-theme-text-dark: var(--bs-color-positive-dark-2); | |
--bs-theme-background: var(--bs-color-positive-light-4); | |
--bs-theme-background-dark: var(--bs-color-positive-light-2); | |
} | |
[data-theme=warning] { | |
--bs-theme-text: var(--bs-color-warning-dark-2); | |
--bs-theme-text-dark: var(--bs-color-grayscale-dark-4); | |
--bs-theme-background: var(--bs-color-warning-light-4); | |
--bs-theme-background-dark: var(--bs-color-warning-light-1); | |
} | |
[data-theme=danger] { | |
--bs-theme-text: var(--bs-color-danger-dark-1); | |
--bs-theme-text-dark: var(--bs-color-danger-dark-2); | |
--bs-theme-background: var(--bs-color-danger-light-4); | |
--bs-theme-background-dark: var(--bs-color-danger-light-2); | |
} | |
[data-theme=grayscale] { | |
--bs-theme-text: var(--bs-color-grayscale-dark-2); | |
--bs-theme-background: var(--bs-color-grayscale-light-4); | |
} | |
h1, | |
h2, | |
h3, | |
h4, | |
h5, | |
h6 { | |
margin: 0; | |
font-family: | |
-apple-system, | |
BlinkMacSystemFont, | |
Roboto, | |
helvetica, | |
arial, | |
sans-serif; | |
font-weight: var(--bs-font-weight-bold); | |
text-rendering: optimizeLegibility; | |
overflow-wrap: break-word; | |
text-wrap: balance; | |
hyphens: auto; | |
} | |
h1 { | |
font-size: var(--bs-font-size-7); | |
line-height: var(--bs-line-height-2); | |
} | |
h2 { | |
font-size: var(--bs-font-size-4); | |
line-height: var(--bs-line-height-4); | |
} | |
body { | |
font-size: var(--bs-font-size-3); | |
line-height: var(--bs-line-height-4); | |
} | |
.u-fg-brand-1 { | |
color: var(--bs-color-brand-1); | |
} | |
.u-margin-l3-y { | |
margin-top: var(--bs-size-l3); | |
margin-bottom: var(--bs-size-l3); | |
} | |
.a-button { | |
--bs-button-border-top-right-radius: var(--bs-button-border-radius); | |
--bs-button-border-bottom-right-radius: var(--bs-button-border-radius); | |
--bs-button-border-bottom-left-radius: var(--bs-button-border-radius); | |
--bs-button-border-top-left-radius: var(--bs-button-border-radius); | |
--bs-button-transition: | |
color var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
background-color var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
border var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
box-shadow var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
outline-offset var(--bs-transition-default-duration) var(--bs-transition-default-easing); | |
--bs-button-justify-content: center; | |
--bs-button-transition: | |
color var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
background-color var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
border var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
box-shadow var(--bs-transition-default-duration) var(--bs-transition-default-easing), | |
outline-offset var(--bs-transition-default-duration) var(--bs-transition-default-easing); | |
--bs-button-justify-content: center; | |
display: flex; | |
position: relative; | |
flex-shrink: 0; | |
align-items: center; | |
justify-content: var(--bs-button-justify-content); | |
min-width: var(--bs-button-min-width); | |
max-width: 100%; | |
min-height: var(--bs-button-min-height); | |
margin: 0; | |
padding: calc(var(--bs-button-padding-vertical) - var(--bs-button-border-width, 0rem)) calc(var(--bs-button-padding-horizontal) - var(--bs-button-border-width, 0rem)); | |
overflow: visible; | |
transition: var(--bs-button-transition); | |
border-width: var(--bs-button-border-width); | |
border-style: solid; | |
border-radius: var(--bs-button-border-top-left-radius) var(--bs-button-border-top-right-radius) var(--bs-button-border-bottom-right-radius) var(--bs-button-border-bottom-left-radius); | |
outline-offset: 0; | |
font-family: inherit; | |
font-size: var(--bs-button-font-size); | |
font-weight: var(--bs-button-font-weight); | |
text-align: center; | |
text-decoration: none; | |
white-space: nowrap; | |
cursor: pointer; | |
user-select: none; | |
-webkit-appearance: none; | |
touch-action: manipulation; | |
} | |
.a-button:hover, | |
.a-button:focus { | |
z-index: 1; | |
outline-width: 0; | |
text-decoration: none; | |
} | |
.a-button:focus-visible { | |
z-index: 3; | |
outline: var(--bs-color-brand-2) solid calc(var(--bs-size-s7) + var(--bs-size-s7) / 2); | |
outline-offset: var(--bs-size-s7); | |
} | |
.a-button[aria-expanded=true], | |
.a-button[aria-pressed=true], | |
.a-button[aria-selected=true], | |
.a-button[aria-current] { | |
z-index: 2; | |
} | |
.a-button:disabled, | |
.a-button[aria-disabled=true], | |
.a-button:disabled:hover, | |
.a-button[aria-disabled=true]:hover, | |
.a-button:disabled:focus, | |
.a-button[aria-disabled=true]:focus { | |
cursor: not-allowed; | |
} | |
.a-button__avatar, | |
.a-button__icon { | |
flex-shrink: 0; | |
margin-right: var(--bs-size-s2); | |
margin-left: calc(var(--bs-size-s2) * -1 / 4); | |
} | |
.a-button__label + .a-button__icon { | |
margin-right: calc(var(--bs-size-s2) * -1 / 4); | |
margin-left: var(--bs-size-s2); | |
} | |
.a-button, | |
.a-button:visited { | |
color: var(--bs-button-color); | |
background-color: var(--bs-button-background-color); | |
border-color: var(--bs-button-border-color); | |
box-shadow: var(--bs-button-box-shadow); | |
} | |
.a-button:hover, | |
.a-button:focus { | |
color: var(--bs-button-hover-color); | |
background-color: var(--bs-button-hover-background-color); | |
border-color: var(--bs-button-hover-border-color); | |
box-shadow: var(--bs-button-hover-box-shadow); | |
} | |
.a-button:active { | |
color: var(--bs-button-active-color); | |
background-color: var(--bs-button-active-background-color); | |
border-color: var(--bs-button-active-border-color); | |
box-shadow: var(--bs-button-active-box-shadow); | |
} | |
.a-button[aria-expanded=true], | |
.a-button[aria-pressed=true], | |
.a-button[aria-selected=true], | |
.a-button[aria-current] { | |
color: var(--bs-button-pressed-color); | |
background-color: var(--bs-button-pressed-background-color); | |
border-color: var(--bs-button-pressed-border-color); | |
box-shadow: var(--bs-button-pressed-box-shadow); | |
} | |
.a-button:disabled, | |
.a-button[aria-disabled=true], | |
.a-button:disabled:hover, | |
.a-button[aria-disabled=true]:hover, | |
.a-button:disabled:focus, | |
.a-button[aria-disabled=true]:focus { | |
color: var(--bs-button-disabled-color); | |
background-color: var(--bs-button-disabled-background-color); | |
border-color: var(--bs-button-disabled-border-color); | |
box-shadow: var(--bs-button-disabled-box-shadow); | |
} | |
:root, | |
[data-theme=default] { | |
--bs-button-color: var(--bs-color-grayscale-white); | |
--bs-button-background-color: var(--bs-color-brand-1-dark-1); | |
--bs-button-box-shadow: none; | |
--bs-button-hover-color: var(--bs-color-brand-1-dark-2); | |
--bs-button-hover-background-color: var(--bs-color-brand-1-light-2); | |
--bs-button-hover-box-shadow: none; | |
--bs-button-active-color: var(--bs-color-grayscale-white); | |
--bs-button-active-background-color: var(--bs-color-brand-1-dark-1); | |
--bs-button-active-box-shadow: none; | |
--bs-button-pressed-color: var(--bs-color-grayscale-white); | |
--bs-button-pressed-background-color: var(--bs-color-brand-1-dark-2); | |
--bs-button-pressed-box-shadow: none; | |
--bs-button-disabled-color: var(--bs-color-grayscale); | |
--bs-button-disabled-background-color: var(--bs-color-grayscale-light-3); | |
--bs-button-disabled-box-shadow: none; | |
} | |
.a-button--secondary, | |
.a-button--secondary:visited { | |
color: var(--bs-button--secondary-color); | |
background-color: var(--bs-button--secondary-background-color); | |
border-color: var(--bs-button--secondary-border-color); | |
box-shadow: var(--bs-button--secondary-box-shadow); | |
} | |
.a-button--secondary:hover, | |
.a-button--secondary:focus { | |
color: var(--bs-button--secondary-hover-color); | |
background-color: var(--bs-button--secondary-hover-background-color); | |
border-color: var(--bs-button--secondary-hover-border-color); | |
box-shadow: var(--bs-button--secondary-hover-box-shadow); | |
} | |
.a-button--secondary:active { | |
color: var(--bs-button--secondary-active-color); | |
background-color: var(--bs-button--secondary-active-background-color); | |
border-color: var(--bs-button--secondary-active-border-color); | |
box-shadow: var(--bs-button--secondary-active-box-shadow); | |
} | |
.a-button--secondary[aria-expanded=true], | |
.a-button--secondary[aria-pressed=true], | |
.a-button--secondary[aria-selected=true], | |
.a-button--secondary[aria-current] { | |
color: var(--bs-button--secondary-pressed-color); | |
background-color: var(--bs-button--secondary-pressed-background-color); | |
border-color: var(--bs-button--secondary-pressed-border-color); | |
box-shadow: var(--bs-button--secondary-pressed-box-shadow); | |
} | |
.a-button--secondary:disabled, | |
.a-button--secondary[aria-disabled=true], | |
.a-button--secondary:disabled:hover, | |
.a-button--secondary[aria-disabled=true]:hover, | |
.a-button--secondary:disabled:focus, | |
.a-button--secondary[aria-disabled=true]:focus { | |
color: var(--bs-button--secondary-disabled-color); | |
background-color: var(--bs-button--secondary-disabled-background-color); | |
border-color: var(--bs-button--secondary-disabled-border-color); | |
box-shadow: var(--bs-button--secondary-disabled-box-shadow); | |
} | |
:root, | |
[data-theme=default] { | |
--bs-button--secondary-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--secondary-background-color: var(--bs-color-grayscale-light-3); | |
--bs-button--secondary-box-shadow: none; | |
--bs-button--secondary-hover-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--secondary-hover-background-color: var(--bs-color-brand-1-light-2); | |
--bs-button--secondary-hover-box-shadow: none; | |
--bs-button--secondary-active-color: var(--bs-color-brand-1-dark-2); | |
--bs-button--secondary-active-background-color: var(--bs-color-brand-1-light-2); | |
--bs-button--secondary-active-box-shadow: none; | |
--bs-button--secondary-pressed-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--secondary-pressed-background-color: var(--bs-color-brand-1-light-2); | |
--bs-button--secondary-pressed-box-shadow: | |
0 0 calc(var(--bs-size-s4) / 4) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s4) / 3) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s4) / 2) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s4) / 1) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075); | |
--bs-button--secondary-disabled-color: var(--bs-color-grayscale); | |
--bs-button--secondary-disabled-background-color: var(--bs-color-grayscale-light-3); | |
--bs-button--secondary-disabled-box-shadow: none; | |
} | |
[data-theme=dark] { | |
--bs-button--secondary-color: var(--bs-color-grayscale-light-2); | |
--bs-button--secondary-background-color: var(--bs-color-grayscale-dark-1); | |
--bs-button--secondary-box-shadow: none; | |
--bs-button--secondary-hover-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--secondary-hover-background-color: var(--bs-color-brand-1-light-1); | |
--bs-button--secondary-hover-box-shadow: none; | |
--bs-button--secondary-active-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--secondary-active-background-color: var(--bs-color-brand-1-light-1); | |
--bs-button--secondary-active-box-shadow: none; | |
--bs-button--secondary-pressed-color: var(--bs-color-brand-1); | |
--bs-button--secondary-pressed-background-color: var(--bs-color-brand-1-light-1); | |
--bs-button--secondary-pressed-box-shadow: | |
0 0 calc(var(--bs-size-s4) / 4) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s4) / 3) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s4) / 2) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075), | |
0 0 calc(var(--bs-size-s4) / 1) rgba(var(--bs-color-brand-1-light-1-rgb), 0.075); | |
--bs-button--secondary-disabled-color: var(--bs-color-grayscale-light-1); | |
--bs-button--secondary-disabled-background-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--secondary-disabled-box-shadow: none; | |
} | |
.a-button--outline, | |
.a-button--outline:visited { | |
color: var(--bs-button--outline-color); | |
background-color: var(--bs-button--outline-background-color); | |
border-color: var(--bs-button--outline-border-color); | |
box-shadow: var(--bs-button--outline-box-shadow); | |
} | |
.a-button--outline:hover, | |
.a-button--outline:focus { | |
color: var(--bs-button--outline-hover-color); | |
background-color: var(--bs-button--outline-hover-background-color); | |
border-color: var(--bs-button--outline-hover-border-color); | |
box-shadow: var(--bs-button--outline-hover-box-shadow); | |
} | |
.a-button--outline:active { | |
color: var(--bs-button--outline-active-color); | |
background-color: var(--bs-button--outline-active-background-color); | |
border-color: var(--bs-button--outline-active-border-color); | |
box-shadow: var(--bs-button--outline-active-box-shadow); | |
} | |
.a-button--outline[aria-expanded=true], | |
.a-button--outline[aria-pressed=true], | |
.a-button--outline[aria-selected=true], | |
.a-button--outline[aria-current] { | |
color: var(--bs-button--outline-pressed-color); | |
background-color: var(--bs-button--outline-pressed-background-color); | |
border-color: var(--bs-button--outline-pressed-border-color); | |
box-shadow: var(--bs-button--outline-pressed-box-shadow); | |
} | |
.a-button--outline:disabled, | |
.a-button--outline[aria-disabled=true], | |
.a-button--outline:disabled:hover, | |
.a-button--outline[aria-disabled=true]:hover, | |
.a-button--outline:disabled:focus, | |
.a-button--outline[aria-disabled=true]:focus { | |
color: var(--bs-button--outline-disabled-color); | |
background-color: var(--bs-button--outline-disabled-background-color); | |
border-color: var(--bs-button--outline-disabled-border-color); | |
box-shadow: var(--bs-button--outline-disabled-box-shadow); | |
} | |
:root, | |
[data-theme=default] { | |
--bs-button--outline-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--outline-background-color: var(--bs-color-grayscale-white); | |
--bs-button--outline-border-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--outline-box-shadow: none; | |
--bs-button--outline-hover-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--outline-hover-background-color: var(--bs-color-brand-1-light-4); | |
--bs-button--outline-hover-border-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--outline-hover-box-shadow: none; | |
--bs-button--outline-active-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--outline-active-background-color: var(--bs-color-brand-1-light-4); | |
--bs-button--outline-active-border-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--outline-active-box-shadow: none; | |
--bs-button--outline-pressed-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--outline-pressed-background-color: var(--bs-color-grayscale-white); | |
--bs-button--outline-pressed-border-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--outline-pressed-box-shadow: | |
0 0 calc(var(--bs-size-m) / 4) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 3) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 2) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 1) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125); | |
--bs-button--outline-disabled-color: var(--bs-color-grayscale); | |
--bs-button--outline-disabled-background-color: transparent; | |
--bs-button--outline-disabled-border-color: var(--bs-color-grayscale); | |
--bs-button--outline-disabled-box-shadow: none; | |
} | |
[data-theme=dark] { | |
--bs-button--outline-color: var(--bs-color-grayscale-light-2); | |
--bs-button--outline-background-color: var(--bs-color-grayscale-dark-1); | |
--bs-button--outline-border-color: var(--bs-color-grayscale-dark-1); | |
--bs-button--outline-box-shadow: none; | |
--bs-button--outline-hover-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--outline-hover-background-color: var(--bs-color-brand-1-light-1); | |
--bs-button--outline-hover-border-color: var(--bs-color-brand-1-light-1); | |
--bs-button--outline-hover-box-shadow: none; | |
--bs-button--outline-active-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--outline-active-background-color: var(--bs-color-brand-1-light-1); | |
--bs-button--outline-active-border-color: var(--bs-color-brand-1-light-1); | |
--bs-button--outline-active-box-shadow: none; | |
--bs-button--outline-pressed-color: var(--bs-color-brand-1); | |
--bs-button--outline-pressed-background-color: var(--bs-color-brand-1-light-1); | |
--bs-button--outline-pressed-border-color: var(--bs-color-brand-1-light-1); | |
--bs-button--outline-pressed-box-shadow: | |
0 0 calc(var(--bs-size-m) / 4) rgba(var(--bs-color-brand-1-light-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 3) rgba(var(--bs-color-brand-1-light-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 2) rgba(var(--bs-color-brand-1-light-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 1) rgba(var(--bs-color-brand-1-light-1-rgb), 0.125); | |
--bs-button--outline-disabled-color: var(--bs-color-grayscale-light-1); | |
--bs-button--outline-disabled-background-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--outline-disabled-border-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--outline-disabled-box-shadow: none; | |
} | |
.a-button--transparent, | |
.a-button--transparent:visited { | |
color: var(--bs-button--transparent-color); | |
background-color: var(--bs-button--transparent-background-color); | |
border-color: var(--bs-button--transparent-border-color); | |
box-shadow: var(--bs-button--transparent-box-shadow); | |
} | |
.a-button--transparent:hover, | |
.a-button--transparent:focus { | |
color: var(--bs-button--transparent-hover-color); | |
background-color: var(--bs-button--transparent-hover-background-color); | |
border-color: var(--bs-button--transparent-hover-border-color); | |
box-shadow: var(--bs-button--transparent-hover-box-shadow); | |
} | |
.a-button--transparent:active { | |
color: var(--bs-button--transparent-active-color); | |
background-color: var(--bs-button--transparent-active-background-color); | |
border-color: var(--bs-button--transparent-active-border-color); | |
box-shadow: var(--bs-button--transparent-active-box-shadow); | |
} | |
.a-button--transparent[aria-expanded=true], | |
.a-button--transparent[aria-pressed=true], | |
.a-button--transparent[aria-selected=true], | |
.a-button--transparent[aria-current] { | |
color: var(--bs-button--transparent-pressed-color); | |
background-color: var(--bs-button--transparent-pressed-background-color); | |
border-color: var(--bs-button--transparent-pressed-border-color); | |
box-shadow: var(--bs-button--transparent-pressed-box-shadow); | |
} | |
.a-button--transparent:disabled, | |
.a-button--transparent[aria-disabled=true], | |
.a-button--transparent:disabled:hover, | |
.a-button--transparent[aria-disabled=true]:hover, | |
.a-button--transparent:disabled:focus, | |
.a-button--transparent[aria-disabled=true]:focus { | |
color: var(--bs-button--transparent-disabled-color); | |
background-color: var(--bs-button--transparent-disabled-background-color); | |
border-color: var(--bs-button--transparent-disabled-border-color); | |
box-shadow: var(--bs-button--transparent-disabled-box-shadow); | |
} | |
:root, | |
[data-theme=default] { | |
--bs-button--transparent-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-background-color: transparent; | |
--bs-button--transparent-border-color: transparent; | |
--bs-button--transparent-box-shadow: none; | |
--bs-button--transparent-hover-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-hover-background-color: var(--bs-color-brand-1-light-4); | |
--bs-button--transparent-hover-border-color: transparent; | |
--bs-button--transparent-hover-box-shadow: none; | |
--bs-button--transparent-active-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--transparent-active-background-color: var(--bs-color-brand-1-light-4); | |
--bs-button--transparent-active-border-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--transparent-active-box-shadow: none; | |
--bs-button--transparent-pressed-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--transparent-pressed-background-color: var(--bs-color-grayscale-white); | |
--bs-button--transparent-pressed-border-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--transparent-pressed-box-shadow: | |
0 0 calc(var(--bs-size-m) / 4) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 3) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 2) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 1) rgba(var(--bs-color-brand-1-dark-1-rgb), 0.125); | |
--bs-button--transparent-disabled-color: var(--bs-color-grayscale); | |
--bs-button--transparent-disabled-background-color: var(--bs-color-grayscale-light-3); | |
--bs-button--transparent-disabled-border-color: var(--bs-color-grayscale-light-3); | |
--bs-button--transparent-disabled-box-shadow: none; | |
} | |
[data-theme=dark] { | |
--bs-button--transparent-color: var(--bs-color-grayscale-light-1); | |
--bs-button--transparent-background-color: transparent; | |
--bs-button--transparent-border-color: transparent; | |
--bs-button--transparent-box-shadow: none; | |
--bs-button--transparent-hover-color: var(--bs-color-brand-1-light-1); | |
--bs-button--transparent-hover-background-color: transparent; | |
--bs-button--transparent-hover-border-color: transparent; | |
--bs-button--transparent-hover-box-shadow: none; | |
--bs-button--transparent-active-color: var(--bs-color-brand-1-light-1); | |
--bs-button--transparent-active-background-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-active-border-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-active-box-shadow: none; | |
--bs-button--transparent-pressed-color: var(--bs-color-grayscale-white); | |
--bs-button--transparent-pressed-background-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-pressed-border-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-pressed-box-shadow: | |
0 0 calc(var(--bs-size-m) / 4) rgba(var(--bs-color-grayscale-dark-2-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 3) rgba(var(--bs-color-grayscale-dark-2-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 2) rgba(var(--bs-color-grayscale-dark-2-rgb), 0.125), | |
0 0 calc(var(--bs-size-m) / 1) rgba(var(--bs-color-grayscale-dark-2-rgb), 0.125); | |
--bs-button--transparent-disabled-color: var(--bs-color-grayscale-light-1); | |
--bs-button--transparent-disabled-background-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-disabled-border-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--transparent-disabled-box-shadow: none; | |
} | |
.a-button--tab, | |
.a-button--tab:visited { | |
color: var(--bs-button--tab-color); | |
background-color: var(--bs-button--tab-background-color); | |
border-color: var(--bs-button--tab-border-color); | |
box-shadow: var(--bs-button--tab-box-shadow); | |
} | |
.a-button--tab:hover, | |
.a-button--tab:focus { | |
color: var(--bs-button--tab-hover-color); | |
background-color: var(--bs-button--tab-hover-background-color); | |
border-color: var(--bs-button--tab-hover-border-color); | |
box-shadow: var(--bs-button--tab-hover-box-shadow); | |
} | |
.a-button--tab:active { | |
color: var(--bs-button--tab-active-color); | |
background-color: var(--bs-button--tab-active-background-color); | |
border-color: var(--bs-button--tab-active-border-color); | |
box-shadow: var(--bs-button--tab-active-box-shadow); | |
} | |
.a-button--tab[aria-expanded=true], | |
.a-button--tab[aria-pressed=true], | |
.a-button--tab[aria-selected=true], | |
.a-button--tab[aria-current] { | |
color: var(--bs-button--tab-pressed-color); | |
background-color: var(--bs-button--tab-pressed-background-color); | |
border-color: var(--bs-button--tab-pressed-border-color); | |
box-shadow: var(--bs-button--tab-pressed-box-shadow); | |
} | |
.a-button--tab:disabled, | |
.a-button--tab[aria-disabled=true], | |
.a-button--tab:disabled:hover, | |
.a-button--tab[aria-disabled=true]:hover, | |
.a-button--tab:disabled:focus, | |
.a-button--tab[aria-disabled=true]:focus { | |
color: var(--bs-button--tab-disabled-color); | |
background-color: var(--bs-button--tab-disabled-background-color); | |
border-color: var(--bs-button--tab-disabled-border-color); | |
box-shadow: var(--bs-button--tab-disabled-box-shadow); | |
} | |
:root, | |
[data-theme=default] { | |
--bs-button--tab-color: var(--bs-color-grayscale-dark-1); | |
--bs-button--tab-background-color: transparent; | |
--bs-button--tab-box-shadow: none; | |
--bs-button--tab-icon-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--tab-hover-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--tab-hover-background-color: transparent; | |
--bs-button--tab-hover-box-shadow: none; | |
--bs-button--tab-active-color: var(--bs-color-brand-1-dark-1); | |
--bs-button--tab-active-background-color: transparent; | |
--bs-button--tab-active-box-shadow: none; | |
--bs-button--tab-pressed-color: var(--bs-color-brand-1-dark-2); | |
--bs-button--tab-pressed-background-color: transparent; | |
--bs-button--tab-pressed-box-shadow: none; | |
--bs-button--tab-disabled-color: var(--bs-color-grayscale); | |
--bs-button--tab-disabled-background-color: transparent; | |
--bs-button--tab-disabled-box-shadow: none; | |
} | |
[data-theme=dark] { | |
--bs-button--tab-color: var(--bs-color-grayscale-light-2); | |
--bs-button--tab-background-color: transparent; | |
--bs-button--tab-box-shadow: none; | |
--bs-button--tab-hover-color: var(--bs-color-brand-1-light-2); | |
--bs-button--tab-hover-background-color: transparent; | |
--bs-button--tab-hover-box-shadow: none; | |
--bs-button--tab-active-color: var(--bs-color-brand-1-light-3); | |
--bs-button--tab-active-background-color: transparent; | |
--bs-button--tab-active-box-shadow: none; | |
--bs-button--tab-pressed-color: var(--bs-color-grayscale-light-1); | |
--bs-button--tab-pressed-background-color: transparent; | |
--bs-button--tab-pressed-box-shadow: none; | |
--bs-button--tab-disabled-color: var(--bs-color-grayscale-dark-2); | |
--bs-button--tab-disabled-background-color: transparent; | |
--bs-button--tab-disabled-box-shadow: none; | |
} | |
.a-button--danger:hover, | |
.a-button--danger:focus { | |
color: var(--bs-button--danger-hover-color); | |
background-color: var(--bs-button--danger-hover-background-color); | |
border-color: var(--bs-button--danger-hover-border-color); | |
box-shadow: var(--bs-button--danger-hover-box-shadow); | |
} | |
.a-button--danger:active { | |
color: var(--bs-button--danger-active-color); | |
background-color: var(--bs-button--danger-active-background-color); | |
border-color: var(--bs-button--danger-active-border-color); | |
box-shadow: var(--bs-button--danger-active-box-shadow); | |
} | |
.a-button--danger[aria-expanded=true], | |
.a-button--danger[aria-pressed=true], | |
.a-button--danger[aria-selected=true], | |
.a-button--danger[aria-current] { | |
color: var(--bs-button--danger-pressed-color); | |
background-color: var(--bs-button--danger-pressed-background-color); | |
border-color: var(--bs-button--danger-pressed-border-color); | |
box-shadow: var(--bs-button--danger-pressed-box-shadow); | |
} | |
:root, | |
[data-theme=default] { | |
--bs-button--danger-hover-color: var(--bs-color-grayscale-white); | |
--bs-button--danger-hover-background-color: var(--bs-color-danger-dark-1); | |
--bs-button--danger-hover-border-color: var(--bs-color-danger-dark-1); | |
--bs-button--danger-hover-box-shadow: none; | |
--bs-button--danger-active-color: var(--bs-color-danger-dark-1); | |
--bs-button--danger-active-background-color: var(--bs-color-danger-light-2); | |
--bs-button--danger-active-border-color: var(--bs-color-danger-light-2); | |
--bs-button--danger-active-box-shadow: none; | |
--bs-button--danger-pressed-color: var(--bs-color-danger-dark-2); | |
--bs-button--danger-pressed-background-color: var(--bs-color-danger-light-2); | |
--bs-button--danger-pressed-border-color: var(--bs-color-danger-light-2); | |
--bs-button--danger-pressed-box-shadow: none; | |
} | |
.a-button { | |
--bs-button-padding-vertical: var(--bs-size-s2); | |
--bs-button-padding-horizontal: var(--bs-size-l1); | |
--bs-button-border-width: 0rem; | |
--bs-button-border-radius: var(--bs-size-s4); | |
--bs-button-min-height: 1em; | |
--bs-button-min-width: 0; | |
--bs-button-font-size: var(--bs-font-size-2); | |
--bs-button-font-weight: var(--bs-font-weight-semibold); | |
--bs-button-padding-vertical: var(--bs-size-s2); | |
--bs-button-padding-horizontal: var(--bs-size-l1); | |
--bs-button-border-width: 0rem; | |
--bs-button-border-radius: var(--bs-size-s4); | |
--bs-button-min-height: 1em; | |
--bs-button-min-width: 0; | |
--bs-button-font-size: var(--bs-font-size-2); | |
--bs-button-font-weight: var(--bs-font-weight-semibold); | |
} | |
.a-button.a-button--square { | |
--bs-button-min-width: calc(1em * var(--bs-line-height-5) + 2 * var(--bs-size-s2)); | |
--bs-button-min-height: calc(1em * var(--bs-line-height-5) + 2 * var(--bs-size-s2)); | |
--bs-button-padding-horizontal: var(--bs-size-s2); | |
--bs-button-padding-vertical: var(--bs-size-s2); | |
} | |
.a-button--small { | |
--bs-button-padding-vertical: var(--bs-size-s4); | |
--bs-button-padding-horizontal: var(--bs-size-m); | |
--bs-button-min-height: var(--bs-size-l1); | |
--bs-button-min-width: var(--bs-size-l2); | |
--bs-button-font-weight: var(--bs-font-weight-medium); | |
--bs-button-padding-vertical: var(--bs-size-s4); | |
--bs-button-padding-horizontal: var(--bs-size-m); | |
--bs-button-min-height: var(--bs-size-l1); | |
--bs-button-min-width: var(--bs-size-l2); | |
--bs-button-font-weight: var(--bs-font-weight-medium); | |
} | |
.a-button--small.a-button--square { | |
--bs-button-min-width: calc(1em * var(--bs-line-height-5) + 2 * var(--bs-size-s4)); | |
--bs-button-min-height: calc(1em * var(--bs-line-height-5) + 2 * var(--bs-size-s4)); | |
--bs-button-padding-horizontal: var(--bs-size-s4); | |
--bs-button-padding-vertical: var(--bs-size-s4); | |
} | |
.a-button--x-small { | |
--bs-button-padding-vertical: var(--bs-size-s6); | |
--bs-button-padding-horizontal: var(--bs-size-s2); | |
--bs-button-font-weight: var(--bs-font-weight-medium); | |
--bs-button-padding-vertical: var(--bs-size-s6); | |
--bs-button-padding-horizontal: var(--bs-size-s2); | |
--bs-button-font-weight: var(--bs-font-weight-medium); | |
} | |
.a-button--x-small.a-button--square { | |
--bs-button-min-width: calc(1em * var(--bs-line-height-5) + 2 * var(--bs-size-s6)); | |
--bs-button-min-height: calc(1em * var(--bs-line-height-5) + 2 * var(--bs-size-s6)); | |
--bs-button-padding-horizontal: var(--bs-size-s6); | |
--bs-button-padding-vertical: var(--bs-size-s6); | |
} | |
.a-button--transparent { | |
--bs-button-border-width: 0.1875rem; | |
--bs-button-border-width: 0.1875rem; | |
} | |
.a-button--outline { | |
--bs-button-border-width: 0.1875rem; | |
--bs-button-border-width: 0.1875rem; | |
} | |
.a-button--menu { | |
--bs-button-border-radius: 0; | |
--bs-button-justify-content: flex-start; | |
--bs-button-border-radius: 0; | |
--bs-button-justify-content: flex-start; | |
} | |
.a-button--tab { | |
--bs-button-border-radius: 0; | |
--bs-button-border-radius: 0; | |
} | |
.a-button--square { | |
--bs-button-padding-vertical: calc(var(--bs-size-s2) + var(--bs-size-s7)); | |
--bs-button-padding-horizontal: calc(var(--bs-size-s2) + var(--bs-size-s7)); | |
--bs-button-padding-vertical: calc(var(--bs-size-s2) + var(--bs-size-s7)); | |
--bs-button-padding-horizontal: calc(var(--bs-size-s2) + var(--bs-size-s7)); | |
} | |
.a-button--round { | |
--bs-button-border-radius: 9999rem; | |
--bs-button-border-radius: 9999rem; | |
} | |
.a-button--tab-container > *:first-child { | |
margin-left: calc(var(--bs-button-padding-horizontal) * -1); | |
} | |
.a-button--tab { | |
position: relative; | |
} | |
.a-button--tab::after, | |
.a-button--tab:visited::after { | |
content: ""; | |
position: absolute; | |
right: 0; | |
bottom: 0; | |
left: 0; | |
height: var(--bs-size-s6); | |
transition: background-color var(--bs-transition-default-duration) var(--bs-transition-default-easing); | |
border-radius: 0; | |
background-color: var(--bs-color-brand-1-light-4); | |
} | |
.a-button--tab:hover::after, | |
.a-button--tab:focus::after { | |
background-color: var(--bs-color-brand-1-light-4); | |
} | |
.a-button--tab:active::after { | |
background-color: var(--bs-color-brand-1-light-2); | |
} | |
.a-button--tab[aria-selected=true]::after, | |
.a-button--tab[aria-current]::after { | |
background-color: var(--bs-color-brand-1-dark-1); | |
} | |
.a-button--tab:disabled::after, | |
.a-button--tab[aria-disabled=true]::after, | |
.a-button--tab:disabled:hover::after, | |
.a-button--tab[aria-disabled=true]:hover::after, | |
.a-button--tab:disabled:focus::after, | |
.a-button--tab[aria-disabled=true]:focus::after { | |
background-color: var(--bs-color-grayscale); | |
} | |
.a-button--tab .a-icon { | |
color: var(--bs-button--tab-icon-color, currentcolor); | |
} | |
.min-h-screen { | |
min-height: 100vh; | |
} | |
.center { | |
max-width: 100ch; | |
margin-inline: auto; | |
} | |
.flex-column { | |
display: flex; | |
flex-direction: column; | |
gap: 1rem; | |
} | |
.flex-row { | |
display: flex; | |
flex-direction: row; | |
gap: 1rem; | |
align-items: flex-end; | |
} | |
.flex-grow { | |
flex-grow: 1; | |
} | |
""" | |
end | |
end | |
defmodule RagLive do | |
use Phoenix.LiveView | |
@chroma_collection_name "rag-time" | |
def mount(_params, _session, socket) do | |
{:ok, collection} = | |
Chroma.Collection.get_or_create(@chroma_collection_name, %{"hnsw:space" => "l2"}) | |
socket = | |
socket | |
|> assign(:ingest_form, to_form(%{"path" => ""})) | |
|> assign_async( | |
:chunks, | |
fn -> | |
{:ok, %{chunks: Chroma.Collection.count(collection)}} | |
end, | |
reset: true | |
) | |
|> assign(:query_form, to_form(%{"query" => ""})) | |
|> assign_async(:response, fn -> {:ok, %{response: %{}}} end) | |
{:ok, socket} | |
end | |
def render(assigns) do | |
~H""" | |
<style> | |
<%= RagLive.Style.styles() %> | |
</style> | |
<div class="flex-column center min-h-screen"> | |
<h1 class="u-fg-brand-1 u-margin-l3-y">A RAG for Elixir</h1> | |
<div class="flex-row"> | |
<div class="flex-grow"> | |
<.async_result :let={chunks} assign={@chunks}> | |
<:loading>Ingesting...</:loading> | |
<:failed>Something went wrong...</:failed> | |
Code chunks in database: <%= chunks %> | |
</.async_result> | |
</div> | |
<button phx-click="reset" class="a-button a-button--secondary a-button--danger">Reset</button> | |
</div> | |
<.form for={@ingest_form} phx-submit="ingest" class="flex-row" > | |
<div class="flex-grow"> | |
<.input type="text" field={@ingest_form[:path]} label="Ingestion Path" /> | |
</div> | |
<button class="a-button a-button--secondary">Ingest</button> | |
</.form> | |
<div class="u-bg-white flex-grow" > | |
<.async_result :let={response} assign={@response}> | |
<:loading>Waiting for response...</:loading> | |
<:failed>Something went wrong...</:failed> | |
<div class="flow"> | |
<h2 :if={response[:query]}>Query</h2> | |
<p :if={response[:query]}> <%= response.query %></p> | |
<h2 :if={response[:response]}>Response</h2> | |
<p :if={response[:response]}> <%= response.response %></p> | |
<div :if={response[:context_sources]}> | |
<h2>Sources</h2> | |
<ol> | |
<li :for={source <- response[:context_sources] || []}><%= source %></li> | |
</ol> | |
</div> | |
</div> | |
</.async_result> | |
</div> | |
<.form for={@query_form} phx-submit="query" class="flex-row u-margin-l3-y" > | |
<div class="flex-grow"> | |
<.input type="textarea" field={@query_form[:query]} label="Query" /> | |
</div> | |
<button class="a-button a-button--primary">Send</button> | |
</.form> | |
</div> | |
""" | |
end | |
def input(%{field: %Phoenix.HTML.FormField{} = field} = assigns) do | |
assigns | |
|> assign(field: nil, id: assigns[:id] || field.id) | |
|> assign_new(:name, fn -> field.name end) | |
|> assign_new(:value, fn -> field.value end) | |
|> input() | |
end | |
def input(%{type: "textarea"} = assigns) do | |
~H""" | |
<div> | |
<label for={@id}><%= @label %></label> | |
<textarea id={@id} name={@name}><%= Phoenix.HTML.Form.normalize_value("textarea", @value) %></textarea> | |
</div> | |
""" | |
end | |
def input(assigns) do | |
~H""" | |
<div> | |
<label for={@id}><%= @label %></label> | |
<input | |
type={@type} | |
name={@name} | |
id={@id} | |
value={Phoenix.HTML.Form.normalize_value(@type, @value)} | |
/> | |
</div> | |
""" | |
end | |
def handle_event("ingest", %{"path" => path}, socket) do | |
{:ok, collection} = | |
Chroma.Collection.get_or_create(@chroma_collection_name, %{"hnsw:space" => "l2"}) | |
{:noreply, | |
assign_async( | |
socket, | |
:chunks, | |
fn -> | |
RagTime.ingest(collection, path) | |
{:ok, %{chunks: Chroma.Collection.count(collection)}} | |
end, | |
reset: true | |
)} | |
end | |
def handle_event("reset", _params, socket) do | |
{:noreply, | |
assign_async( | |
socket, | |
:chunks, | |
fn -> | |
{:ok, collection} = | |
Chroma.Collection.get_or_create(@chroma_collection_name, %{"hnsw:space" => "l2"}) | |
Chroma.Collection.delete(collection) | |
{:ok, %{chunks: 0}} | |
end, | |
reset: true | |
)} | |
end | |
def handle_event("query", %{"query" => query}, socket) do | |
{:ok, collection} = | |
Chroma.Collection.get_or_create(@chroma_collection_name, %{"hnsw:space" => "l2"}) | |
{:noreply, | |
assign_async( | |
socket, | |
:response, | |
fn -> {:ok, %{response: RagTime.query(collection, query)}} end, | |
reset: true | |
)} | |
end | |
end | |
PhoenixPlayground.start( | |
live: RagLive, | |
child_specs: [ | |
{Nx.Serving, | |
serving: RagTime.Serving.build_embedding_serving(), | |
name: RagTime.EmbeddingServing, | |
batch_timeout: 100}, | |
{Nx.Serving, | |
serving: RagTime.Serving.build_llm_serving(), name: RagTime.LLMServing, batch_timeout: 100} | |
] | |
) |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Here is the accompanying blog post: https://bitcrowd.dev/a-rag-for-elixir-in-elixir