]> Untitled Git - axy/ft/rag.git/commitdiff
Partial impl of inference
authorAxy <gilliardmarthey.axel@gmail.com>
Thu, 27 Aug 2026 15:59:29 +0000 (17:59 +0200)
committerAxy <gilliardmarthey.axel@gmail.com>
Thu, 27 Aug 2026 15:59:29 +0000 (17:59 +0200)
pyproject.toml
src/rag/__init__.py
src/rag/_stoopid.py [new file with mode: 0644]
src/rag/answering.py [new file with mode: 0644]
src/rag/chunking.py
src/rag/models.py
src/rag/retrieval.py
uv.lock

index cfca57488fd0e1a461147aae61c439316a0abf8f..b712de9a4be691a082ea89c301faaa9afb966c1f 100644 (file)
@@ -5,6 +5,7 @@ description = "Retrieval augmented generation"
 readme = "README.md"
 requires-python = ">=3.13"
 dependencies = [
+    "accelerate>=1.14.0",
     "fire>=0.7.1",
     "pydantic>=2.13.4",
     "stopwordsiso>=0.7.1",
index 7e66bb04393ccd4a3ccce0c1e00aa6ffa6ce8132..4157d8bdb66a60ea342ed09e348092813543ef27 100644 (file)
@@ -7,12 +7,13 @@ import pydantic
 from pydantic_core import ValidationError
 from tqdm import tqdm
 
+from rag.answering import InferenceBackend
 from rag.chunking import FileType, chunk_file
 from rag.models import (
-    MinimalSource,
     RagDataset,
     RetrievedQuestion,
     SearchResults,
+    Source,
 )
 from rag.retrieval import BM25, BagOfWords, stopwords
 
@@ -22,16 +23,15 @@ def main() -> None:
 
 
 storage_adapter = pydantic.TypeAdapter(dict[bytes, BagOfWords])
-key_adapter: pydantic.TypeAdapter[MinimalSource] = pydantic.TypeAdapter(
-    MinimalSource
-)
+key_adapter: pydantic.TypeAdapter[Source] = pydantic.TypeAdapter(Source)
 
 
 class RAG:
     """A retrieval augmented generation CLI"""
 
     def __init__(self) -> None:
-        self._bm25_store: BM25[MinimalSource] | None = None
+        self._bm25_store: BM25[Source] | None = None
+        self._inference_store: InferenceBackend | None = None
 
     def index(
         self,
@@ -75,12 +75,12 @@ class RAG:
             exit(1)
         print(f"Successfully ingested: {data} -> {index}")
 
-    def _bm25(self, index: str) -> BM25[MinimalSource]:
+    def _bm25(self, index: str) -> BM25[Source]:
         if self._bm25_store:
             return self._bm25_store
         try:
             with open(index + "/index.json") as f:
-                bm25: BM25[MinimalSource] = BM25.from_storage(
+                bm25: BM25[Source] = BM25.from_storage(
                     (key_adapter.validate_json(k), v)
                     for k, v in tqdm(
                         storage_adapter.validate_json(f.read()).items(),
@@ -93,7 +93,7 @@ class RAG:
             logging.getLogger(__name__).error(f"Failed to open index {e}")
             exit(1)
 
-    def _search(self, query: str, k: int, index: str) -> list[MinimalSource]:
+    def _search(self, query: str, k: int, index: str) -> list[Source]:
         bm25 = self._bm25(index)
         return bm25.best_k(query, k)
 
@@ -140,3 +140,28 @@ class RAG:
         except (OSError, ValidationError) as e:
             logging.getLogger(__name__).error(f"Failed to open dataset {e}")
             exit(1)
+
+    def _inference(self) -> InferenceBackend:
+        if not self._inference_store:
+            self._inference_store = InferenceBackend("Qwen/Qwen3-0.6B")
+        return self._inference_store
+
+    def answer(
+        self,
+        query: str,
+        /,
+        k: int = 5,
+        index: str = "data/processed",
+        max_tokens: int = 100,
+    ) -> None:
+        sources = self._search(query, k, index)
+        inference = self._inference()
+        print("Sources:")
+        for source in sources:
+            print(source)
+        print("")
+        inference.answer(
+            RetrievedQuestion(question=query, retrieved_sources=sources),
+            max_tokens,
+            cb=lambda s: print(s, end="", flush=True),
+        )
diff --git a/src/rag/_stoopid.py b/src/rag/_stoopid.py
new file mode 100644 (file)
index 0000000..991966e
--- /dev/null
@@ -0,0 +1,46 @@
+"""Useless module where I can shove the required but overcomplicated and
+poorly designed pydantic classes."""
+
+import uuid
+
+from pydantic import BaseModel, Field
+
+
+class MinimalSource(BaseModel):
+    file_path: str
+    first_character_index: int
+    last_character_index: int
+
+
+class UnansweredQuestion(BaseModel):
+    question_id: str = Field(default_factory=lambda: str(uuid.uuid4()))
+    question: str
+
+
+class AnsweredQuestion(UnansweredQuestion):
+    sources: list[MinimalSource]
+    answer: str
+
+
+class RagDataset(BaseModel):
+    rag_questions: list[AnsweredQuestion | UnansweredQuestion]
+
+
+class MinimalSearchResults(BaseModel):
+    question_id: str
+    question: str
+    retrieved_sources: list[MinimalSource]
+
+
+class MinimalAnswer(MinimalSearchResults):
+    answer: str
+
+
+class StudentSearchResults(BaseModel):
+    search_results: list[MinimalSearchResults]
+    k: int
+
+
+class StudentSearchResultsAndAnswer(BaseModel):
+    search_results: list[MinimalAnswer]
+    k: int
diff --git a/src/rag/answering.py b/src/rag/answering.py
new file mode 100644 (file)
index 0000000..f201793
--- /dev/null
@@ -0,0 +1,117 @@
+import logging
+from collections.abc import Callable
+from typing import Any, cast
+
+from transformers import AutoModelForCausalLM, AutoTokenizer
+from transformers.generation import BaseStreamer  # type:ignore
+
+from rag.models import AnsweredQuestion, RetrievedQuestion, Source
+
+
+class InferenceBackend:
+    SYSTEM_PROMPT: str = """You are a codebase and document search assistant.
+    You answer querries factually using the provided sources.
+    Answer directly with no reasoning, no <think> tags.
+    Answer simply, in a few short sentences, in a single line.
+    Assume the user cannot see the sources, they are your knowledge, \
+    do not mention them.
+    End your answer with a newline."""
+
+    class _Streamer(BaseStreamer):
+        def __init__(
+            self, backend: "InferenceBackend", cb: Callable[[str], None]
+        ) -> None:
+            self.backend = backend
+            self.cb = cb
+            self.skip = True
+
+        def put(self, value: Any) -> None:
+            if self.skip:
+                self.skip = False
+                return
+            s: str = cast(
+                str,
+                self.backend.tokenizer.decode(value, skip_special_tokens=True),
+            )
+            self.cb(s.rstrip("\n"))
+
+        def end(self) -> None:
+            self.cb("\n")
+
+    def __init__(self, model: str) -> None:
+        self.model: Any = AutoModelForCausalLM.from_pretrained(
+            model, device_map="auto"
+        )
+        self.tokenizer = AutoTokenizer.from_pretrained(
+            model, padding_side="left"
+        )
+
+    @staticmethod
+    def _fetch_range(source: Source) -> str | None:
+        try:
+            with open(source.file_path) as f:
+                s = f.read()
+                return s[
+                    source.first_character_index : source.last_character_index
+                    + 1
+                ]
+        except OSError as e:
+            logging.getLogger(__name__).error(
+                f"Failed to fetch source {source}: {e}"
+            )
+        return None
+
+    def _prompt(self, question: RetrievedQuestion) -> str:
+        sources = "\n\n".join(
+            s
+            for e in question.retrieved_sources
+            if (s := self._fetch_range(e))
+        )
+        sep = "\n\n\n"
+        return (
+            "# System instructions\n"
+            + self.SYSTEM_PROMPT
+            + sep
+            + "# Context\n"
+            + sources
+            + "# Question\n"
+            + question.question
+            + sep
+            + "# Answer\n"
+        )
+
+    def _answer(
+        self,
+        question: RetrievedQuestion,
+        max_tokens: int,
+        cb: Callable[[str], None],
+    ) -> str:
+        prompt = self._prompt(question)
+        model_inputs = self.tokenizer([prompt], return_tensors="pt").to(
+            self.model.device
+        )
+        generated_ids = self.model.generate(
+            **model_inputs,
+            max_new_tokens=max_tokens,
+            stop_strings="\n",
+            tokenizer=self.tokenizer,
+            streamer=self._Streamer(self, cb),
+        )
+        full_response = self.tokenizer.batch_decode(
+            generated_ids,
+            skip_special_tokens=True,
+        )[0]
+        return full_response[len(prompt) :].strip()
+
+    def answer(
+        self,
+        question: RetrievedQuestion,
+        max_tokens: int,
+        cb: Callable[[str], None] = lambda _s: None,
+    ) -> AnsweredQuestion:
+        return AnsweredQuestion(
+            question_id=question.question_id,
+            question=question.question,
+            retrieved_sources=question.retrieved_sources,
+            answer=self._answer(question, max_tokens, cb),
+        )
index 24e8fec7c9fac23ddd5ed456abf8fe4cb018eb93..aabaf7366f87d0a155cd4c546b5ec38c806951be 100644 (file)
@@ -2,7 +2,7 @@ import logging
 from enum import Enum, auto
 from itertools import count
 
-from rag.models import MinimalSource
+from rag.models import Source
 
 
 class FileType(Enum):
@@ -65,7 +65,7 @@ def chunk_by_depth(
 
 def chunk_file(
     path: str, max_chunk_size: int, by: FileType
-) -> dict[MinimalSource, str]:
+) -> dict[Source, str]:
     try:
         with open(path) as f:
             s = f.read()
@@ -79,7 +79,7 @@ def chunk_file(
     res = {}
     for chunk in chunks:
         res[
-            MinimalSource(
+            Source(
                 first_character_index=total_len,
                 last_character_index=total_len + len(chunk) - 1,
                 file_path=path,
index dada28dbd763f751532d5c0d462a4dc0af511b16..cb67b1794273f823766327a2bd10f9d55aab6708 100644 (file)
@@ -3,7 +3,7 @@ import uuid
 from pydantic import BaseModel, Field
 
 
-class MinimalSource(BaseModel, frozen=True):
+class Source(BaseModel, frozen=True):
     file_path: str
     first_character_index: int
     last_character_index: int
@@ -26,7 +26,7 @@ class RagDataset(BaseModel):
 
 
 class RetrievedQuestion(Question):
-    retrieved_sources: list[MinimalSource]
+    retrieved_sources: list[Source]
 
 
 class AnsweredQuestion(RetrievedQuestion):
index 92403240f90d847ad3eeea426683148288f29789..e62fb750da0ae7f75e5d380f4dc186621104ad43 100644 (file)
@@ -2,6 +2,8 @@ import math
 from collections.abc import Iterable
 from dataclasses import dataclass
 
+import stopwordsiso
+
 type Multiset[T] = dict[T, int]
 
 type BagOfWords = Multiset[str]
@@ -27,8 +29,14 @@ def bag_of_words(s: str, stopwords: set[str]) -> BagOfWords:
         res[word] = res.get(word, 0) + 1
     return res
 
+
 def stopwords(lang: str | Iterable[str]) -> set[str]:
-    return {word for e in stopwords(lang) for word in words_normalize(e)}
+    return {
+        word
+        for e in stopwordsiso.stopwords(lang)
+        for word in words_normalize(e)
+    }
+
 
 @dataclass
 class BM25[T]:
diff --git a/uv.lock b/uv.lock
index 46d2b45a133bfa1c90ea740f4cb9dc25685bd314..6c12a842122d9d3f97a2d4f3d1dbb566f346257c 100644 (file)
--- a/uv.lock
+++ b/uv.lock
@@ -5,17 +5,36 @@ resolution-markers = [
     "python_full_version >= '3.15' and sys_platform == 'win32'",
     "python_full_version >= '3.15' and sys_platform == 'emscripten'",
     "python_full_version >= '3.15' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+    "python_full_version >= '3.15' and sys_platform == 'darwin'",
     "python_full_version == '3.14.*' and sys_platform == 'win32'",
     "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
     "python_full_version == '3.14.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+    "python_full_version == '3.14.*' and sys_platform == 'darwin'",
     "python_full_version < '3.14' and sys_platform == 'win32'",
     "python_full_version < '3.14' and sys_platform == 'emscripten'",
     "python_full_version < '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'",
-    "python_full_version >= '3.15' and sys_platform == 'darwin'",
-    "python_full_version == '3.14.*' and sys_platform == 'darwin'",
     "python_full_version < '3.14' and sys_platform == 'darwin'",
 ]
 
+[[package]]
+name = "accelerate"
+version = "1.14.0"
+source = { registry = "https://pypi.org/simple" }
+dependencies = [
+    { name = "huggingface-hub" },
+    { name = "numpy" },
+    { name = "packaging" },
+    { name = "psutil" },
+    { name = "pyyaml" },
+    { name = "safetensors" },
+    { name = "torch", version = "2.13.0", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "python_full_version < '3.15' and sys_platform == 'darwin'" },
+    { name = "torch", version = "2.13.0+cpu", source = { registry = "https://download.pytorch.org/whl/cpu" }, marker = "python_full_version >= '3.15' or sys_platform != 'darwin'" },
+]
+sdist = { url = "https://files.pythonhosted.org/packages/8d/75/94cd5d389649578aca399e5aa822637eec18319a1dadc400ffe2f9a7493f/accelerate-1.14.0.tar.gz", hash = "sha256:41b9c4377a54e0b460a959b0defa1b736e4ca0a2373252d9a539964c2afe3c8d", size = 412167, upload-time = "2026-06-11T13:45:52.326Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/a8/db/253133d7e7cb40d3af384bb2f5c0b4a2b7fdcffbc95c688cc67a20a3c103/accelerate-1.14.0-py3-none-any.whl", hash = "sha256:e94390c2863b873be18f623f9df48a0d8fe5eff13ea7f1a00092b0a7904888c6", size = 389246, upload-time = "2026-06-11T13:45:50.477Z" },
+]
+
 [[package]]
 name = "annotated-doc"
 version = "0.0.5"
@@ -574,6 +593,34 @@ wheels = [
     { url = "https://files.pythonhosted.org/packages/f1/d9/7fb5aa316bc299258e68c73ba3bddbc499654a07f151cba08f6153988714/pathspec-1.1.1-py3-none-any.whl", hash = "sha256:a00ce642f577bf7f473932318056212bc4f8bfdf53128c78bbd5af0b9b20b189", size = 57328, upload-time = "2026-04-27T01:46:07.06Z" },
 ]
 
+[[package]]
+name = "psutil"
+version = "7.2.2"
+source = { registry = "https://pypi.org/simple" }
+sdist = { url = "https://files.pythonhosted.org/packages/aa/c6/d1ddf4abb55e93cebc4f2ed8b5d6dbad109ecb8d63748dd2b20ab5e57ebe/psutil-7.2.2.tar.gz", hash = "sha256:0746f5f8d406af344fd547f1c8daa5f5c33dbc293bb8d6a16d80b4bb88f59372", size = 493740, upload-time = "2026-01-28T18:14:54.428Z" }
+wheels = [
+    { url = "https://files.pythonhosted.org/packages/51/08/510cbdb69c25a96f4ae523f733cdc963ae654904e8db864c07585ef99875/psutil-7.2.2-cp313-cp313t-macosx_10_13_x86_64.whl", hash = "sha256:2edccc433cbfa046b980b0df0171cd25bcaeb3a68fe9022db0979e7aa74a826b", size = 130595, upload-time = "2026-01-28T18:14:57.293Z" },
+    { url = "https://files.pythonhosted.org/packages/d6/f5/97baea3fe7a5a9af7436301f85490905379b1c6f2dd51fe3ecf24b4c5fbf/psutil-7.2.2-cp313-cp313t-macosx_11_0_arm64.whl", hash = "sha256:e78c8603dcd9a04c7364f1a3e670cea95d51ee865e4efb3556a3a63adef958ea", size = 131082, upload-time = "2026-01-28T18:14:59.732Z" },
+    { url = "https://files.pythonhosted.org/packages/37/d6/246513fbf9fa174af531f28412297dd05241d97a75911ac8febefa1a53c6/psutil-7.2.2-cp313-cp313t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1a571f2330c966c62aeda00dd24620425d4b0cc86881c89861fbc04549e5dc63", size = 181476, upload-time = "2026-01-28T18:15:01.884Z" },
+    { url = "https://files.pythonhosted.org/packages/b8/b5/9182c9af3836cca61696dabe4fd1304e17bc56cb62f17439e1154f225dd3/psutil-7.2.2-cp313-cp313t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:917e891983ca3c1887b4ef36447b1e0873e70c933afc831c6b6da078ba474312", size = 184062, upload-time = "2026-01-28T18:15:04.436Z" },
+    { url = "https://files.pythonhosted.org/packages/16/ba/0756dca669f5a9300d0cbcbfae9a4c30e446dfc7440ffe43ded5724bfd93/psutil-7.2.2-cp313-cp313t-win_amd64.whl", hash = "sha256:ab486563df44c17f5173621c7b198955bd6b613fb87c71c161f827d3fb149a9b", size = 139893, upload-time = "2026-01-28T18:15:06.378Z" },
+    { url = "https://files.pythonhosted.org/packages/1c/61/8fa0e26f33623b49949346de05ec1ddaad02ed8ba64af45f40a147dbfa97/psutil-7.2.2-cp313-cp313t-win_arm64.whl", hash = "sha256:ae0aefdd8796a7737eccea863f80f81e468a1e4cf14d926bd9b6f5f2d5f90ca9", size = 135589, upload-time = "2026-01-28T18:15:08.03Z" },
+    { url = "https://files.pythonhosted.org/packages/81/69/ef179ab5ca24f32acc1dac0c247fd6a13b501fd5534dbae0e05a1c48b66d/psutil-7.2.2-cp314-cp314t-macosx_10_15_x86_64.whl", hash = "sha256:eed63d3b4d62449571547b60578c5b2c4bcccc5387148db46e0c2313dad0ee00", size = 130664, upload-time = "2026-01-28T18:15:09.469Z" },
+    { url = "https://files.pythonhosted.org/packages/7b/64/665248b557a236d3fa9efc378d60d95ef56dd0a490c2cd37dafc7660d4a9/psutil-7.2.2-cp314-cp314t-macosx_11_0_arm64.whl", hash = "sha256:7b6d09433a10592ce39b13d7be5a54fbac1d1228ed29abc880fb23df7cb694c9", size = 131087, upload-time = "2026-01-28T18:15:11.724Z" },
+    { url = "https://files.pythonhosted.org/packages/d5/2e/e6782744700d6759ebce3043dcfa661fb61e2fb752b91cdeae9af12c2178/psutil-7.2.2-cp314-cp314t-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:1fa4ecf83bcdf6e6c8f4449aff98eefb5d0604bf88cb883d7da3d8d2d909546a", size = 182383, upload-time = "2026-01-28T18:15:13.445Z" },
+    { url = "https://files.pythonhosted.org/packages/57/49/0a41cefd10cb7505cdc04dab3eacf24c0c2cb158a998b8c7b1d27ee2c1f5/psutil-7.2.2-cp314-cp314t-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:e452c464a02e7dc7822a05d25db4cde564444a67e58539a00f929c51eddda0cf", size = 185210, upload-time = "2026-01-28T18:15:16.002Z" },
+    { url = "https://files.pythonhosted.org/packages/dd/2c/ff9bfb544f283ba5f83ba725a3c5fec6d6b10b8f27ac1dc641c473dc390d/psutil-7.2.2-cp314-cp314t-win_amd64.whl", hash = "sha256:c7663d4e37f13e884d13994247449e9f8f574bc4655d509c3b95e9ec9e2b9dc1", size = 141228, upload-time = "2026-01-28T18:15:18.385Z" },
+    { url = "https://files.pythonhosted.org/packages/f2/fc/f8d9c31db14fcec13748d373e668bc3bed94d9077dbc17fb0eebc073233c/psutil-7.2.2-cp314-cp314t-win_arm64.whl", hash = "sha256:11fe5a4f613759764e79c65cf11ebdf26e33d6dd34336f8a337aa2996d71c841", size = 136284, upload-time = "2026-01-28T18:15:19.912Z" },
+    { url = "https://files.pythonhosted.org/packages/e7/36/5ee6e05c9bd427237b11b3937ad82bb8ad2752d72c6969314590dd0c2f6e/psutil-7.2.2-cp36-abi3-macosx_10_9_x86_64.whl", hash = "sha256:ed0cace939114f62738d808fdcecd4c869222507e266e574799e9c0faa17d486", size = 129090, upload-time = "2026-01-28T18:15:22.168Z" },
+    { url = "https://files.pythonhosted.org/packages/80/c4/f5af4c1ca8c1eeb2e92ccca14ce8effdeec651d5ab6053c589b074eda6e1/psutil-7.2.2-cp36-abi3-macosx_11_0_arm64.whl", hash = "sha256:1a7b04c10f32cc88ab39cbf606e117fd74721c831c98a27dc04578deb0c16979", size = 129859, upload-time = "2026-01-28T18:15:23.795Z" },
+    { url = "https://files.pythonhosted.org/packages/b5/70/5d8df3b09e25bce090399cf48e452d25c935ab72dad19406c77f4e828045/psutil-7.2.2-cp36-abi3-manylinux2010_x86_64.manylinux_2_12_x86_64.manylinux_2_28_x86_64.whl", hash = "sha256:076a2d2f923fd4821644f5ba89f059523da90dc9014e85f8e45a5774ca5bc6f9", size = 155560, upload-time = "2026-01-28T18:15:25.976Z" },
+    { url = "https://files.pythonhosted.org/packages/63/65/37648c0c158dc222aba51c089eb3bdfa238e621674dc42d48706e639204f/psutil-7.2.2-cp36-abi3-manylinux2014_aarch64.manylinux_2_17_aarch64.manylinux_2_28_aarch64.whl", hash = "sha256:b0726cecd84f9474419d67252add4ac0cd9811b04d61123054b9fb6f57df6e9e", size = 156997, upload-time = "2026-01-28T18:15:27.794Z" },
+    { url = "https://files.pythonhosted.org/packages/8e/13/125093eadae863ce03c6ffdbae9929430d116a246ef69866dad94da3bfbc/psutil-7.2.2-cp36-abi3-musllinux_1_2_aarch64.whl", hash = "sha256:fd04ef36b4a6d599bbdb225dd1d3f51e00105f6d48a28f006da7f9822f2606d8", size = 148972, upload-time = "2026-01-28T18:15:29.342Z" },
+    { url = "https://files.pythonhosted.org/packages/04/78/0acd37ca84ce3ddffaa92ef0f571e073faa6d8ff1f0559ab1272188ea2be/psutil-7.2.2-cp36-abi3-musllinux_1_2_x86_64.whl", hash = "sha256:b58fabe35e80b264a4e3bb23e6b96f9e45a3df7fb7eed419ac0e5947c61e47cc", size = 148266, upload-time = "2026-01-28T18:15:31.597Z" },
+    { url = "https://files.pythonhosted.org/packages/b4/90/e2159492b5426be0c1fef7acba807a03511f97c5f86b3caeda6ad92351a7/psutil-7.2.2-cp37-abi3-win_amd64.whl", hash = "sha256:eb7e81434c8d223ec4a219b5fc1c47d0417b12be7ea866e24fb5ad6e84b3d988", size = 137737, upload-time = "2026-01-28T18:15:33.849Z" },
+    { url = "https://files.pythonhosted.org/packages/8c/c7/7bb2e321574b10df20cbde462a94e2b71d05f9bbda251ef27d104668306a/psutil-7.2.2-cp37-abi3-win_arm64.whl", hash = "sha256:8c233660f575a5a89e6d4cb65d9f938126312bca76d8fe087b947b3a1aaac9ee", size = 134617, upload-time = "2026-01-28T18:15:36.514Z" },
+]
+
 [[package]]
 name = "pycodestyle"
 version = "2.14.0"
@@ -713,6 +760,7 @@ name = "rag"
 version = "0.1.0"
 source = { editable = "." }
 dependencies = [
+    { name = "accelerate" },
     { name = "fire" },
     { name = "pydantic" },
     { name = "stopwordsiso" },
@@ -732,6 +780,7 @@ dev = [
 
 [package.metadata]
 requires-dist = [
+    { name = "accelerate", specifier = ">=1.14.0" },
     { name = "fire", specifier = ">=0.7.1" },
     { name = "pydantic", specifier = ">=2.13.4" },
     { name = "stopwordsiso", specifier = ">=0.7.1" },
@@ -987,13 +1036,13 @@ resolution-markers = [
     "python_full_version >= '3.15' and sys_platform == 'win32'",
     "python_full_version >= '3.15' and sys_platform == 'emscripten'",
     "python_full_version >= '3.15' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'",
+    "python_full_version >= '3.15' and sys_platform == 'darwin'",
     "python_full_version == '3.14.*' and sys_platform == 'win32'",
     "python_full_version == '3.14.*' and sys_platform == 'emscripten'",
     "python_full_version == '3.14.*' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'",
     "python_full_version < '3.14' and sys_platform == 'win32'",
     "python_full_version < '3.14' and sys_platform == 'emscripten'",
     "python_full_version < '3.14' and sys_platform != 'darwin' and sys_platform != 'emscripten' and sys_platform != 'win32'",
-    "python_full_version >= '3.15' and sys_platform == 'darwin'",
 ]
 dependencies = [
     { name = "filelock" },