lvvignesh2122 commited on
Commit
775a7d0
·
1 Parent(s): bbd443b

Initial FastAPI RAG backend

Browse files
Files changed (4) hide show
  1. .gitignore +14 -201
  2. main.py +75 -0
  3. rag_store.py +67 -0
  4. requirements.txt +8 -0
.gitignore CHANGED
@@ -1,207 +1,20 @@
1
- # Byte-compiled / optimized / DLL files
2
  __pycache__/
3
- *.py[codz]
4
- *$py.class
 
5
 
6
- # C extensions
7
- *.so
8
-
9
- # Distribution / packaging
10
- .Python
11
- build/
12
- develop-eggs/
13
- dist/
14
- downloads/
15
- eggs/
16
- .eggs/
17
- lib/
18
- lib64/
19
- parts/
20
- sdist/
21
- var/
22
- wheels/
23
- share/python-wheels/
24
- *.egg-info/
25
- .installed.cfg
26
- *.egg
27
- MANIFEST
28
-
29
- # PyInstaller
30
- # Usually these files are written by a python script from a template
31
- # before PyInstaller builds the exe, so as to inject date/other infos into it.
32
- *.manifest
33
- *.spec
34
-
35
- # Installer logs
36
- pip-log.txt
37
- pip-delete-this-directory.txt
38
-
39
- # Unit test / coverage reports
40
- htmlcov/
41
- .tox/
42
- .nox/
43
- .coverage
44
- .coverage.*
45
- .cache
46
- nosetests.xml
47
- coverage.xml
48
- *.cover
49
- *.py.cover
50
- .hypothesis/
51
- .pytest_cache/
52
- cover/
53
-
54
- # Translations
55
- *.mo
56
- *.pot
57
-
58
- # Django stuff:
59
- *.log
60
- local_settings.py
61
- db.sqlite3
62
- db.sqlite3-journal
63
-
64
- # Flask stuff:
65
- instance/
66
- .webassets-cache
67
-
68
- # Scrapy stuff:
69
- .scrapy
70
-
71
- # Sphinx documentation
72
- docs/_build/
73
-
74
- # PyBuilder
75
- .pybuilder/
76
- target/
77
-
78
- # Jupyter Notebook
79
- .ipynb_checkpoints
80
-
81
- # IPython
82
- profile_default/
83
- ipython_config.py
84
-
85
- # pyenv
86
- # For a library or package, you might want to ignore these files since the code is
87
- # intended to run in multiple environments; otherwise, check them in:
88
- # .python-version
89
-
90
- # pipenv
91
- # According to pypa/pipenv#598, it is recommended to include Pipfile.lock in version control.
92
- # However, in case of collaboration, if having platform-specific dependencies or dependencies
93
- # having no cross-platform support, pipenv may install dependencies that don't work, or not
94
- # install all needed dependencies.
95
- #Pipfile.lock
96
-
97
- # UV
98
- # Similar to Pipfile.lock, it is generally recommended to include uv.lock in version control.
99
- # This is especially recommended for binary packages to ensure reproducibility, and is more
100
- # commonly ignored for libraries.
101
- #uv.lock
102
-
103
- # poetry
104
- # Similar to Pipfile.lock, it is generally recommended to include poetry.lock in version control.
105
- # This is especially recommended for binary packages to ensure reproducibility, and is more
106
- # commonly ignored for libraries.
107
- # https://python-poetry.org/docs/basic-usage/#commit-your-poetrylock-file-to-version-control
108
- #poetry.lock
109
- #poetry.toml
110
-
111
- # pdm
112
- # Similar to Pipfile.lock, it is generally recommended to include pdm.lock in version control.
113
- # pdm recommends including project-wide configuration in pdm.toml, but excluding .pdm-python.
114
- # https://pdm-project.org/en/latest/usage/project/#working-with-version-control
115
- #pdm.lock
116
- #pdm.toml
117
- .pdm-python
118
- .pdm-build/
119
-
120
- # pixi
121
- # Similar to Pipfile.lock, it is generally recommended to include pixi.lock in version control.
122
- #pixi.lock
123
- # Pixi creates a virtual environment in the .pixi directory, just like venv module creates one
124
- # in the .venv directory. It is recommended not to include this directory in version control.
125
- .pixi
126
-
127
- # PEP 582; used by e.g. github.com/David-OConnor/pyflow and github.com/pdm-project/pdm
128
- __pypackages__/
129
-
130
- # Celery stuff
131
- celerybeat-schedule
132
- celerybeat.pid
133
-
134
- # SageMath parsed files
135
- *.sage.py
136
-
137
- # Environments
138
- .env
139
- .envrc
140
- .venv
141
- env/
142
  venv/
143
- ENV/
144
- env.bak/
145
- venv.bak/
146
-
147
- # Spyder project settings
148
- .spyderproject
149
- .spyproject
150
-
151
- # Rope project settings
152
- .ropeproject
153
-
154
- # mkdocs documentation
155
- /site
156
-
157
- # mypy
158
- .mypy_cache/
159
- .dmypy.json
160
- dmypy.json
161
 
162
- # Pyre type checker
163
- .pyre/
164
-
165
- # pytype static type analyzer
166
- .pytype/
167
-
168
- # Cython debug symbols
169
- cython_debug/
170
-
171
- # PyCharm
172
- # JetBrains specific template is maintained in a separate JetBrains.gitignore that can
173
- # be found at https://github.com/github/gitignore/blob/main/Global/JetBrains.gitignore
174
- # and can be added to the global gitignore or merged into this file. For a more nuclear
175
- # option (not recommended) you can uncomment the following to ignore the entire idea folder.
176
- #.idea/
177
-
178
- # Abstra
179
- # Abstra is an AI-powered process automation framework.
180
- # Ignore directories containing user credentials, local state, and settings.
181
- # Learn more at https://abstra.io/docs
182
- .abstra/
183
-
184
- # Visual Studio Code
185
- # Visual Studio Code specific template is maintained in a separate VisualStudioCode.gitignore
186
- # that can be found at https://github.com/github/gitignore/blob/main/Global/VisualStudioCode.gitignore
187
- # and can be added to the global gitignore or merged into this file. However, if you prefer,
188
- # you could uncomment the following to ignore the entire vscode folder
189
- # .vscode/
190
-
191
- # Ruff stuff:
192
- .ruff_cache/
193
-
194
- # PyPI configuration file
195
- .pypirc
196
 
197
- # Cursor
198
- # Cursor is an AI-powered code editor. `.cursorignore` specifies files/directories to
199
- # exclude from AI features like autocomplete and code analysis. Recommended for sensitive data
200
- # refer to https://docs.cursor.com/context/ignore-files
201
- .cursorignore
202
- .cursorindexingignore
203
 
204
- # Marimo
205
- marimo/_static/
206
- marimo/_lsp/
207
- __marimo__/
 
1
+ # Python
2
  __pycache__/
3
+ *.pyc
4
+ *.pyo
5
+ *.pyd
6
 
7
+ # Virtual environment
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
8
  venv/
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
9
 
10
+ # Environment variables
11
+ .env
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
12
 
13
+ # Vector store & runtime data
14
+ data/
15
+ *.npy
16
+ *.index
 
 
17
 
18
+ # OS / editor
19
+ .vscode/
20
+ .DS_Store
 
main.py ADDED
@@ -0,0 +1,75 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ from fastapi import FastAPI
3
+ from pydantic import BaseModel
4
+ from dotenv import load_dotenv
5
+ import google.generativeai as genai
6
+ from rag_store import search_knowledge
7
+
8
+ load_dotenv()
9
+
10
+ genai.configure(api_key=os.getenv("GEMINI_API_KEY"))
11
+
12
+ app = FastAPI(title="AI RAG Backend with Gemini")
13
+
14
+ class PromptRequest(BaseModel):
15
+ prompt: str
16
+
17
+ @app.get("/")
18
+ def home():
19
+ return {"message": "AI backend is running 🚀"}
20
+
21
+ @app.post("/ask")
22
+ async def ask(data: PromptRequest):
23
+ results = search_knowledge(data.prompt)
24
+
25
+ if not results:
26
+ return {
27
+ "answer": "I don't know based on the provided documents.",
28
+ "confidence": 0.0,
29
+ "citations": []
30
+ }
31
+
32
+ # -------- Context
33
+ context_text = "\n".join(r["text"] for r in results)
34
+
35
+ prompt = f"""
36
+ Answer the question strictly using the context.
37
+ If unsure, say "I don't know".
38
+
39
+ Question:
40
+ {data.prompt}
41
+
42
+ Context:
43
+ {context_text}
44
+ """
45
+
46
+ model = genai.GenerativeModel("gemini-2.5-flash")
47
+ response = model.generate_content(prompt)
48
+
49
+ # -------- Confidence scoring
50
+ avg_distance = sum(r["distance"] for r in results) / len(results)
51
+
52
+ if avg_distance < 0.6:
53
+ confidence = 0.9
54
+ elif avg_distance < 1.2:
55
+ confidence = 0.7
56
+ else:
57
+ confidence = 0.4
58
+
59
+ # -------- Citations
60
+ citations = []
61
+ seen = set()
62
+ for r in results:
63
+ key = (r["metadata"]["source"], r["metadata"]["page"])
64
+ if key not in seen:
65
+ seen.add(key)
66
+ citations.append({
67
+ "source": r["metadata"]["source"],
68
+ "page": r["metadata"]["page"]
69
+ })
70
+
71
+ return {
72
+ "answer": response.text,
73
+ "confidence": round(confidence, 2),
74
+ "citations": citations
75
+ }
rag_store.py ADDED
@@ -0,0 +1,67 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ import os
2
+ import faiss
3
+ import numpy as np
4
+ from sentence_transformers import SentenceTransformer
5
+ from pypdf import PdfReader
6
+
7
+ DATA_DIR = "data"
8
+ INDEX_FILE = "vector.index"
9
+ DOCS_FILE = "documents.npy"
10
+ META_FILE = "metadata.npy"
11
+
12
+ model = SentenceTransformer("all-MiniLM-L6-v2")
13
+
14
+ # -------------------------
15
+ # Load or build index
16
+ # -------------------------
17
+ if os.path.exists(INDEX_FILE):
18
+ print("🔁 Loading FAISS index from disk...")
19
+ index = faiss.read_index(INDEX_FILE)
20
+ documents = np.load(DOCS_FILE, allow_pickle=True)
21
+ metadata = np.load(META_FILE, allow_pickle=True)
22
+ else:
23
+ print("🧠 Building FAISS index...")
24
+ texts = []
25
+ meta = []
26
+
27
+ for file in os.listdir(DATA_DIR):
28
+ if file.endswith(".pdf"):
29
+ reader = PdfReader(os.path.join(DATA_DIR, file))
30
+ for i, page in enumerate(reader.pages):
31
+ text = page.extract_text()
32
+ if text:
33
+ texts.append(text)
34
+ meta.append({
35
+ "source": file,
36
+ "page": i + 1
37
+ })
38
+
39
+ embeddings = model.encode(texts)
40
+ index = faiss.IndexFlatL2(embeddings.shape[1])
41
+ index.add(np.array(embeddings))
42
+
43
+ np.save(DOCS_FILE, texts)
44
+ np.save(META_FILE, meta)
45
+ faiss.write_index(index, INDEX_FILE)
46
+
47
+ documents = texts
48
+ metadata = meta
49
+
50
+ print("✅ FAISS index saved to disk.")
51
+
52
+ # -------------------------
53
+ # Search
54
+ # -------------------------
55
+ def search_knowledge(query, top_k=5):
56
+ query_vec = model.encode([query])
57
+ distances, indices = index.search(query_vec, top_k)
58
+
59
+ results = []
60
+ for dist, idx in zip(distances[0], indices[0]):
61
+ results.append({
62
+ "text": documents[idx],
63
+ "metadata": metadata[idx],
64
+ "distance": float(dist)
65
+ })
66
+
67
+ return results
requirements.txt ADDED
@@ -0,0 +1,8 @@
 
 
 
 
 
 
 
 
 
1
+ fastapi
2
+ uvicorn
3
+ python-dotenv
4
+ google-generativeai
5
+ faiss-cpu
6
+ sentence-transformers
7
+ pypdf
8
+ numpy