Last active
September 16, 2025 16:46
-
-
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} | |
| ] | |
| ) |
Author
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