tiny-rag-ai 0.1.0__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- tiny_rag_ai-0.1.0/PKG-INFO +12 -0
- tiny_rag_ai-0.1.0/README.md +15 -0
- tiny_rag_ai-0.1.0/pyproject.toml +28 -0
- tiny_rag_ai-0.1.0/setup.cfg +4 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai/__init__.py +34 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai/engine.py +90 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai/indexer.py +141 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai.egg-info/PKG-INFO +12 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai.egg-info/SOURCES.txt +12 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai.egg-info/dependency_links.txt +1 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai.egg-info/requires.txt +6 -0
- tiny_rag_ai-0.1.0/src/tiny_rag_ai.egg-info/top_level.txt +1 -0
- tiny_rag_ai-0.1.0/tests/test_engine.py +1 -0
- tiny_rag_ai-0.1.0/tests/test_indexer.py +1 -0
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tiny-rag-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Complete In-Device RAG chatbot library, no API keys needed.With 2 lines of implementation in applications.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Madhan-1000/tiny-ai
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Requires-Dist: faiss-cpu
|
|
8
|
+
Requires-Dist: sentence-transformers
|
|
9
|
+
Requires-Dist: llama-cpp-python
|
|
10
|
+
Requires-Dist: huggingface-hub
|
|
11
|
+
Requires-Dist: pymupdf
|
|
12
|
+
Requires-Dist: numpy
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
# Tiny AI
|
|
2
|
+
|
|
3
|
+
A lightweight Python library for embedding a chatbot into your web app with a simple function call.
|
|
4
|
+
|
|
5
|
+
## What it is
|
|
6
|
+
- Wraps the Qwen2.5-0.5B-Instruct-GGUF model.
|
|
7
|
+
- Runs inference in about 330 MB of memory on-device.
|
|
8
|
+
- Avoids heavy RAG pipelines by accepting documents directly through its parameters.
|
|
9
|
+
|
|
10
|
+
## Why use it
|
|
11
|
+
- Minimal setup and small footprint.
|
|
12
|
+
- Focus on your app logic instead of infrastructure.
|
|
13
|
+
|
|
14
|
+
## Status
|
|
15
|
+
- Code coming soon.
|
|
@@ -0,0 +1,28 @@
|
|
|
1
|
+
[build-system]
|
|
2
|
+
requires = ["setuptools>=64"]
|
|
3
|
+
build-backend = "setuptools.build_meta" # ← change this line
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
[project]
|
|
8
|
+
name = "tiny-rag-ai"
|
|
9
|
+
version = "0.1.0"
|
|
10
|
+
description = "Complete In-Device RAG chatbot library, no API keys needed.With 2 lines of implementation in applications."
|
|
11
|
+
requires-python = ">=3.9"
|
|
12
|
+
|
|
13
|
+
dependencies = [
|
|
14
|
+
"faiss-cpu",
|
|
15
|
+
"sentence-transformers",
|
|
16
|
+
"llama-cpp-python",
|
|
17
|
+
"huggingface-hub",
|
|
18
|
+
"pymupdf",
|
|
19
|
+
"numpy",
|
|
20
|
+
]
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/Madhan-1000/tiny-ai"
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[tool.setuptools.packages.find]
|
|
28
|
+
where = ["src"]
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
from . import engine,indexer
|
|
2
|
+
import faiss
|
|
3
|
+
import numpy as np
|
|
4
|
+
|
|
5
|
+
|
|
6
|
+
def index(folder_path, save_path="./tiny_ai_data"):
|
|
7
|
+
"""
|
|
8
|
+
This fucntion does many things to initialize the setup.
|
|
9
|
+
-> First loads all the documents
|
|
10
|
+
-> Second the seperates them into chunks
|
|
11
|
+
-> Third Loads all the AI models
|
|
12
|
+
-> Then Embeddes the chunks into vectors
|
|
13
|
+
-> Then it will store them in a faiss index
|
|
14
|
+
-> After that the path for saving the indexes is done
|
|
15
|
+
"""
|
|
16
|
+
|
|
17
|
+
documents=indexer.load_documenmts(folder_path=folder_path)
|
|
18
|
+
chunks=[]
|
|
19
|
+
for doc in documents:
|
|
20
|
+
chunks.extend(indexer.chunk_text(text=doc,chunk_size=500,overlap=50 ))
|
|
21
|
+
engine._load_models()
|
|
22
|
+
embeddings=indexer.embed_chunks(chunks=chunks, embed_model=engine._embed_model)
|
|
23
|
+
dimensions=embeddings.shape[1]
|
|
24
|
+
faiss_index=faiss.IndexFlatL2(dimensions)
|
|
25
|
+
engine.set_save_path(save_path)
|
|
26
|
+
faiss_index.add(embeddings)
|
|
27
|
+
indexer.save_index(faiss_index,chunks=chunks,save_path=save_path)
|
|
28
|
+
|
|
29
|
+
|
|
30
|
+
def chat(query, use_case):
|
|
31
|
+
"""
|
|
32
|
+
This fucntion does exactly what is in its name it just calls the ai fucntion.
|
|
33
|
+
"""
|
|
34
|
+
return engine.chat(user_query=query,usecase=use_case)
|
|
@@ -0,0 +1,90 @@
|
|
|
1
|
+
import os
|
|
2
|
+
import faiss
|
|
3
|
+
import pickle
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
_cache_dir = os.environ.get("TINY_AI_CACHE_DIR", os.path.expanduser("~/.cache/tiny_ai"))
|
|
8
|
+
_embed_model = None
|
|
9
|
+
_llm = None
|
|
10
|
+
_index = None
|
|
11
|
+
_chunks = None
|
|
12
|
+
_save_path = None
|
|
13
|
+
|
|
14
|
+
def set_save_path(path: str):
|
|
15
|
+
global _save_path
|
|
16
|
+
_save_path = path
|
|
17
|
+
|
|
18
|
+
|
|
19
|
+
def _load_models(cache_dir=_cache_dir):
|
|
20
|
+
global _embed_model, _llm, _index, _chunks
|
|
21
|
+
if _embed_model is None:
|
|
22
|
+
from sentence_transformers import SentenceTransformer
|
|
23
|
+
_embed_model = SentenceTransformer("sentence-transformers/all-MiniLM-L6-v2" ,cache_folder=_cache_dir)
|
|
24
|
+
if _llm is None:
|
|
25
|
+
|
|
26
|
+
from huggingface_hub import hf_hub_download
|
|
27
|
+
from llama_cpp import Llama
|
|
28
|
+
|
|
29
|
+
model_path = hf_hub_download(
|
|
30
|
+
repo_id="Qwen/Qwen2.5-0.5B-Instruct-GGUF",
|
|
31
|
+
filename="qwen2.5-0.5b-instruct-q4_k_m.gguf",
|
|
32
|
+
cache_dir=_cache_dir
|
|
33
|
+
)
|
|
34
|
+
_llm = Llama(model_path=model_path, n_ctx=4096, n_threads=16)
|
|
35
|
+
|
|
36
|
+
|
|
37
|
+
def _load_index():
|
|
38
|
+
global _index, _chunks ,_save_path
|
|
39
|
+
if _save_path is None:
|
|
40
|
+
default = Path("./tiny_ai_data/config.txt")
|
|
41
|
+
if default.exists():
|
|
42
|
+
_save_path = default.read_text().strip()
|
|
43
|
+
else:
|
|
44
|
+
print("Error: run index() first")
|
|
45
|
+
return
|
|
46
|
+
if _index is None:
|
|
47
|
+
_index = faiss.read_index(str(Path(_save_path) / "faiss.index"))
|
|
48
|
+
|
|
49
|
+
if _chunks is None:
|
|
50
|
+
try:
|
|
51
|
+
with open(Path(_save_path) / "chunks.pkl", "rb") as f:
|
|
52
|
+
_chunks = pickle.load(f)
|
|
53
|
+
except FileNotFoundError:
|
|
54
|
+
print("Error: index files not found, run index() first")
|
|
55
|
+
except Exception as e:
|
|
56
|
+
print(f"An error occurred: {e}")
|
|
57
|
+
|
|
58
|
+
def search(query: str, k=6):
|
|
59
|
+
_load_models()
|
|
60
|
+
_load_index()
|
|
61
|
+
query_vector = _embed_model.encode([query]).astype("float32").reshape(1, -1)
|
|
62
|
+
distances, indices = _index.search(query_vector, k)
|
|
63
|
+
results = [_chunks[i] for i in indices[0]]
|
|
64
|
+
return "\n".join(results)
|
|
65
|
+
|
|
66
|
+
|
|
67
|
+
def build_prompt(query: str, context, use_case: str):
|
|
68
|
+
prompt = f"""<|im_start|>system
|
|
69
|
+
You are a helpful chatbot assistant for {use_case} and you must say I don't know or I can't answer it with a reason why if anything outside of this context.
|
|
70
|
+
Use ONLY the context below to answer.
|
|
71
|
+
CONTEXT:
|
|
72
|
+
{context}<|im_end|>
|
|
73
|
+
<|im_start|>user
|
|
74
|
+
{query}<|im_end|>
|
|
75
|
+
<|im_start|>assistant
|
|
76
|
+
"""
|
|
77
|
+
return prompt
|
|
78
|
+
|
|
79
|
+
def generate(prompt:str,max_tokens:int=650):
|
|
80
|
+
_load_models()
|
|
81
|
+
answer_output=_llm(prompt,max_tokens=max_tokens,echo=False,stop=["<|im_end|>"])
|
|
82
|
+
return answer_output["choices"][0]["text"].strip()
|
|
83
|
+
|
|
84
|
+
def chat(user_query:str,usecase:str):
|
|
85
|
+
context=search(query=user_query)
|
|
86
|
+
|
|
87
|
+
prompt=build_prompt(user_query,context=context,use_case=usecase)
|
|
88
|
+
|
|
89
|
+
answer=generate(prompt=prompt)
|
|
90
|
+
return answer
|
|
@@ -0,0 +1,141 @@
|
|
|
1
|
+
import faiss
|
|
2
|
+
import pickle
|
|
3
|
+
import fitz
|
|
4
|
+
from pathlib import Path
|
|
5
|
+
|
|
6
|
+
def load_documenmts(folder_path: str) -> list:
|
|
7
|
+
"""
|
|
8
|
+
Returns all the loaded documents in the path.
|
|
9
|
+
|
|
10
|
+
Parameters
|
|
11
|
+
----------
|
|
12
|
+
path : str
|
|
13
|
+
path for the folder of the web application or any application detailed documents either in .txt files or .pdf files.
|
|
14
|
+
|
|
15
|
+
Returns
|
|
16
|
+
-------
|
|
17
|
+
returns all the text in the documents in form of array of each document in a string.
|
|
18
|
+
"""
|
|
19
|
+
docs=[]
|
|
20
|
+
folder=Path(folder_path)
|
|
21
|
+
for pdf_file in folder.glob("**/*.pdf"):
|
|
22
|
+
|
|
23
|
+
pdf_content=load_pdf(pdf_file_path=pdf_file)
|
|
24
|
+
docs.append(pdf_content)
|
|
25
|
+
|
|
26
|
+
for txt_file in folder.glob("**/*.txt"):
|
|
27
|
+
|
|
28
|
+
txt_file_content=load_txt(txt_file_path=txt_file)
|
|
29
|
+
docs.append(txt_file_content)
|
|
30
|
+
|
|
31
|
+
return docs
|
|
32
|
+
|
|
33
|
+
|
|
34
|
+
def load_txt(txt_file_path: str) -> str:
|
|
35
|
+
"""
|
|
36
|
+
Returns all the content in the txt file.
|
|
37
|
+
|
|
38
|
+
Parameters
|
|
39
|
+
----------
|
|
40
|
+
txt_file_path : str
|
|
41
|
+
path for the txt file.
|
|
42
|
+
|
|
43
|
+
Returns
|
|
44
|
+
-------
|
|
45
|
+
returns the text file content
|
|
46
|
+
"""
|
|
47
|
+
text=""
|
|
48
|
+
with open(txt_file_path,"r") as file:
|
|
49
|
+
text=file.read()
|
|
50
|
+
|
|
51
|
+
return text
|
|
52
|
+
|
|
53
|
+
|
|
54
|
+
def load_pdf(pdf_file_path: str) -> str:
|
|
55
|
+
"""
|
|
56
|
+
Returns all the content in the pdf file.
|
|
57
|
+
|
|
58
|
+
Parameters
|
|
59
|
+
----------
|
|
60
|
+
pdf_file_path : str
|
|
61
|
+
path for the pdf file.
|
|
62
|
+
|
|
63
|
+
Returns
|
|
64
|
+
-------
|
|
65
|
+
returns the pdf file content
|
|
66
|
+
"""
|
|
67
|
+
try:
|
|
68
|
+
|
|
69
|
+
doc=fitz.open(pdf_file_path)
|
|
70
|
+
text=""
|
|
71
|
+
|
|
72
|
+
for page in doc:
|
|
73
|
+
text+=page.get_text()
|
|
74
|
+
doc.close()
|
|
75
|
+
|
|
76
|
+
except Exception as e:
|
|
77
|
+
print(f"Error processing file {pdf_file_path.name}: {e}")
|
|
78
|
+
return text
|
|
79
|
+
|
|
80
|
+
|
|
81
|
+
|
|
82
|
+
def chunk_text(text :str , chunk_size:int , overlap:int)-> list :
|
|
83
|
+
|
|
84
|
+
"""
|
|
85
|
+
Returns the chunked text with size of 'chunk_size' and with overlap of 'overlap'.
|
|
86
|
+
|
|
87
|
+
Parameters
|
|
88
|
+
----------
|
|
89
|
+
text : str
|
|
90
|
+
the text of all the documents
|
|
91
|
+
chunk_size : int
|
|
92
|
+
the variable for chunking the data
|
|
93
|
+
overlap : int
|
|
94
|
+
the variable for overlapping the chunked data
|
|
95
|
+
|
|
96
|
+
Returns
|
|
97
|
+
-------
|
|
98
|
+
returns all the text in the documents in form of array of each document in a string.
|
|
99
|
+
"""
|
|
100
|
+
chunks = []
|
|
101
|
+
start = 0
|
|
102
|
+
while start < len(text):
|
|
103
|
+
end = start + chunk_size
|
|
104
|
+
chunks.append(text[start:end])
|
|
105
|
+
start += chunk_size - overlap
|
|
106
|
+
return chunks
|
|
107
|
+
|
|
108
|
+
def embed_chunks(chunks:list,embed_model):
|
|
109
|
+
"""
|
|
110
|
+
Returns the embedded chunks of data.
|
|
111
|
+
|
|
112
|
+
Parameters
|
|
113
|
+
----------
|
|
114
|
+
chunks : list
|
|
115
|
+
the text of all the documents
|
|
116
|
+
|
|
117
|
+
embed_model
|
|
118
|
+
the model with transformer
|
|
119
|
+
Returns
|
|
120
|
+
-------
|
|
121
|
+
array of float32 values which are embedded using the model.
|
|
122
|
+
"""
|
|
123
|
+
embeddings_array=embed_model.encode(chunks).astype("float32")
|
|
124
|
+
if len(embeddings_array.shape) == 1:
|
|
125
|
+
embeddings_array = embeddings_array.reshape(1, -1)
|
|
126
|
+
return embeddings_array
|
|
127
|
+
|
|
128
|
+
|
|
129
|
+
|
|
130
|
+
def save_index(index, chunks: list, save_path: str):
|
|
131
|
+
"""
|
|
132
|
+
Saves the FAISS index and chunks to disk.
|
|
133
|
+
"""
|
|
134
|
+
path = Path(save_path)
|
|
135
|
+
|
|
136
|
+
path.mkdir(parents=True, exist_ok=True)
|
|
137
|
+
faiss.write_index(index, str(path / "faiss.index"))
|
|
138
|
+
|
|
139
|
+
|
|
140
|
+
with open(path / "chunks.pkl", "wb") as f:
|
|
141
|
+
pickle.dump(chunks,f)
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: tiny-rag-ai
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: Complete In-Device RAG chatbot library, no API keys needed.With 2 lines of implementation in applications.
|
|
5
|
+
Project-URL: Homepage, https://github.com/Madhan-1000/tiny-ai
|
|
6
|
+
Requires-Python: >=3.9
|
|
7
|
+
Requires-Dist: faiss-cpu
|
|
8
|
+
Requires-Dist: sentence-transformers
|
|
9
|
+
Requires-Dist: llama-cpp-python
|
|
10
|
+
Requires-Dist: huggingface-hub
|
|
11
|
+
Requires-Dist: pymupdf
|
|
12
|
+
Requires-Dist: numpy
|
|
@@ -0,0 +1,12 @@
|
|
|
1
|
+
README.md
|
|
2
|
+
pyproject.toml
|
|
3
|
+
src/tiny_rag_ai/__init__.py
|
|
4
|
+
src/tiny_rag_ai/engine.py
|
|
5
|
+
src/tiny_rag_ai/indexer.py
|
|
6
|
+
src/tiny_rag_ai.egg-info/PKG-INFO
|
|
7
|
+
src/tiny_rag_ai.egg-info/SOURCES.txt
|
|
8
|
+
src/tiny_rag_ai.egg-info/dependency_links.txt
|
|
9
|
+
src/tiny_rag_ai.egg-info/requires.txt
|
|
10
|
+
src/tiny_rag_ai.egg-info/top_level.txt
|
|
11
|
+
tests/test_engine.py
|
|
12
|
+
tests/test_indexer.py
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
tiny_rag_ai
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#python script to test engine.py seperatly.
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
#python script to test indexer.py seperatly.
|