quantumbit commited on
Commit
fdf8b43
Β·
verified Β·
1 Parent(s): dd5974e

Upload folder using huggingface_hub

Browse files
.gitattributes CHANGED
@@ -33,3 +33,7 @@ saved_model/**/* filter=lfs diff=lfs merge=lfs -text
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
 
 
 
 
 
33
  *.zip filter=lfs diff=lfs merge=lfs -text
34
  *.zst filter=lfs diff=lfs merge=lfs -text
35
  *tfevents* filter=lfs diff=lfs merge=lfs -text
36
+ rag_system/Try[[:space:]]Docs/Constitution[[:space:]]Of[[:space:]]india.pdf filter=lfs diff=lfs merge=lfs -text
37
+ rag_system/Try[[:space:]]Docs/Contract[[:space:]]of[[:space:]]Insurance.pdf filter=lfs diff=lfs merge=lfs -text
38
+ rag_system/Try[[:space:]]Docs/Indian[[:space:]]Penal[[:space:]]Code.pdf filter=lfs diff=lfs merge=lfs -text
39
+ rag_system/Try[[:space:]]Docs/Origin_of_Species.pdf filter=lfs diff=lfs merge=lfs -text
rag_system/Try Docs/Constitution Of india.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:819ba7adc5e5ae4063f26c82472283ba682ddb662e5a7f864cf26984d99b26ed
3
+ size 2413611
rag_system/Try Docs/Contract of Insurance.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:1e9bd70c489bb3e31d73f1df82644c7e0295b736e6177723a13a8e70d434ac3f
3
+ size 452255
rag_system/Try Docs/Indian Penal Code.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:038c736730c09d5b72b1642ab8056607ca546c0b87631811da1a30accd08f81d
3
+ size 1529218
rag_system/Try Docs/Origin_of_Species.pdf ADDED
@@ -0,0 +1,3 @@
 
 
 
 
1
+ version https://git-lfs.github.com/spec/v1
2
+ oid sha256:07baf1c96315e54f042fbb152ac3a4449d70899dc7c2165e03ccf39126a74e20
3
+ size 613465
rag_system/api.py CHANGED
@@ -21,6 +21,7 @@ from .vector_store import (
21
  add_documents, load_or_create_store, is_loaded,
22
  list_collections, get_collection_stats, delete_collection,
23
  cleanup_stale_collections, get_collection_embedding_mode,
 
24
  )
25
  from .query_engine import query as run_query, stream_query, pipeline_stream_query
26
  from .eval import evaluate
@@ -46,6 +47,9 @@ _ingest_jobs: dict[str, dict] = {}
46
  # Raw file bytes for document preview: collection_name -> (bytes, content_type)
47
  _doc_files: dict[str, tuple[bytes, str]] = {}
48
 
 
 
 
49
  _FILE_CONTENT_TYPES: dict[str, str] = {
50
  '.pdf': 'application/pdf',
51
  '.txt': 'text/plain; charset=utf-8',
@@ -115,6 +119,41 @@ def _safe_coll_name(filename: str) -> str:
115
  return safe or 'doc'
116
 
117
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
118
  # Lifespan (startup / shutdown)
119
  @asynccontextmanager
120
  async def lifespan(app: FastAPI):
@@ -135,6 +174,8 @@ async def lifespan(app: FastAPI):
135
  if d.is_dir():
136
  load_or_create_store(d.name)
137
 
 
 
138
  logger.info("RAG API ready!")
139
 
140
  # Background session-cleanup loop: remove collections idle > 30 min
@@ -207,6 +248,43 @@ async def embeddings_info():
207
  return get_embeddings_runtime_info()
208
 
209
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
210
  # ── Ingest ────────────────────────────────────────────────────────────────────
211
 
212
  @app.post("/ingest", response_model=IngestResponse, tags=["ingest"])
@@ -503,6 +581,14 @@ async def get_document_raw(collection_name: str):
503
  """Serve raw document bytes for in-browser preview."""
504
  from fastapi.responses import Response
505
  entry = _doc_files.get(collection_name)
 
 
 
 
 
 
 
 
506
  if not entry:
507
  raise HTTPException(status_code=404, detail=f"Document '{collection_name}' not available for preview")
508
  data, media_type = entry
 
21
  add_documents, load_or_create_store, is_loaded,
22
  list_collections, get_collection_stats, delete_collection,
23
  cleanup_stale_collections, get_collection_embedding_mode,
24
+ pin_collection,
25
  )
26
  from .query_engine import query as run_query, stream_query, pipeline_stream_query
27
  from .eval import evaluate
 
47
  # Raw file bytes for document preview: collection_name -> (bytes, content_type)
48
  _doc_files: dict[str, tuple[bytes, str]] = {}
49
 
50
+ # Try Docs file paths: collection_name -> path
51
+ _try_doc_paths: dict[str, "Path"] = {}
52
+
53
  _FILE_CONTENT_TYPES: dict[str, str] = {
54
  '.pdf': 'application/pdf',
55
  '.txt': 'text/plain; charset=utf-8',
 
119
  return safe or 'doc'
120
 
121
 
122
+ def _try_doc_collection_name(filename: str) -> str:
123
+ return f"{settings.try_docs_prefix}{_safe_coll_name(filename)}"
124
+
125
+
126
+ def _load_try_docs() -> None:
127
+ """Load pre-indexed Try Docs into memory and cache raw bytes for preview."""
128
+ from pathlib import Path
129
+
130
+ try_dir = Path(settings.try_docs_path)
131
+ if not try_dir.exists():
132
+ logger.info("Try Docs folder not found at %s", try_dir)
133
+ return
134
+
135
+ for path in sorted(try_dir.iterdir()):
136
+ if not path.is_file():
137
+ continue
138
+ suffix = path.suffix.lower()
139
+ if suffix not in _FILE_CONTENT_TYPES:
140
+ continue
141
+
142
+ collection = _try_doc_collection_name(path.name)
143
+ _try_doc_paths[collection] = path
144
+
145
+ store = load_or_create_store(collection)
146
+ if store is not None:
147
+ pin_collection(collection)
148
+ else:
149
+ logger.warning("Try Doc index missing for '%s' (%s)", path.name, collection)
150
+
151
+ try:
152
+ _doc_files[collection] = (path.read_bytes(), _FILE_CONTENT_TYPES[suffix])
153
+ except Exception:
154
+ logger.warning("Failed to cache Try Doc file bytes for '%s'", path.name)
155
+
156
+
157
  # Lifespan (startup / shutdown)
158
  @asynccontextmanager
159
  async def lifespan(app: FastAPI):
 
174
  if d.is_dir():
175
  load_or_create_store(d.name)
176
 
177
+ _load_try_docs()
178
+
179
  logger.info("RAG API ready!")
180
 
181
  # Background session-cleanup loop: remove collections idle > 30 min
 
248
  return get_embeddings_runtime_info()
249
 
250
 
251
+ # ── Try Docs ─────────────────────────────────────────────────────────────────
252
+
253
+ @app.get("/try_docs", tags=["try_docs"])
254
+ async def list_try_docs():
255
+ """List pre-indexed Try Docs available to add to a session."""
256
+ from pathlib import Path
257
+
258
+ try_dir = Path(settings.try_docs_path)
259
+ if not try_dir.exists():
260
+ return {"docs": []}
261
+
262
+ docs = []
263
+ for path in sorted(try_dir.iterdir()):
264
+ if not path.is_file():
265
+ continue
266
+ suffix = path.suffix.lower()
267
+ if suffix not in _FILE_CONTENT_TYPES:
268
+ continue
269
+
270
+ collection = _try_doc_collection_name(path.name)
271
+ _try_doc_paths.setdefault(collection, path)
272
+
273
+ index_path = Path(settings.faiss_index_path) / collection
274
+ stats = get_collection_stats(collection) if index_path.exists() else None
275
+
276
+ docs.append({
277
+ "filename": path.name,
278
+ "collection": collection,
279
+ "chunks": stats["chunk_count"] if stats else 0,
280
+ "embedding_mode": stats["embedding_mode"] if stats else None,
281
+ "size_mb": stats["size_mb"] if stats else 0.0,
282
+ "ready": stats is not None,
283
+ })
284
+
285
+ return {"docs": docs}
286
+
287
+
288
  # ── Ingest ────────────────────────────────────────────────────────────────────
289
 
290
  @app.post("/ingest", response_model=IngestResponse, tags=["ingest"])
 
581
  """Serve raw document bytes for in-browser preview."""
582
  from fastapi.responses import Response
583
  entry = _doc_files.get(collection_name)
584
+ if not entry and collection_name in _try_doc_paths:
585
+ path = _try_doc_paths[collection_name]
586
+ try:
587
+ suffix = path.suffix.lower()
588
+ _doc_files[collection_name] = (path.read_bytes(), _FILE_CONTENT_TYPES.get(suffix, 'application/octet-stream'))
589
+ entry = _doc_files.get(collection_name)
590
+ except Exception:
591
+ entry = None
592
  if not entry:
593
  raise HTTPException(status_code=404, detail=f"Document '{collection_name}' not available for preview")
594
  data, media_type = entry
rag_system/config.py CHANGED
@@ -20,6 +20,10 @@ class Settings(BaseSettings):
20
  embedding_batch_size: int = 32
21
  embedding_normalize: bool = True
22
 
 
 
 
 
23
  #FAISS
24
  faiss_index_path: str = "./faiss_indexes"
25
  faiss_index_name: str = "prod_rag"
 
20
  embedding_batch_size: int = 32
21
  embedding_normalize: bool = True
22
 
23
+ # Try Docs (pre-indexed demo docs)
24
+ try_docs_path: str = os.path.join(os.path.dirname(__file__), "Try Docs")
25
+ try_docs_prefix: str = "try__"
26
+
27
  #FAISS
28
  faiss_index_path: str = "./faiss_indexes"
29
  faiss_index_name: str = "prod_rag"
rag_system/vector_store.py CHANGED
@@ -26,6 +26,7 @@ _stores: dict[str, FAISS] = {}
26
  # Tracks last-used timestamp per collection (epoch seconds) for TTL-based cleanup
27
  _last_used: dict[str, float] = {}
28
  _collection_embeddings: dict[str, str] = {}
 
29
 
30
  _EMBEDDING_META_FILE = "embedding.json"
31
 
@@ -64,6 +65,14 @@ def get_collection_embedding_mode(collection: str) -> Optional[str]:
64
  return None
65
 
66
 
 
 
 
 
 
 
 
 
67
  def resolve_embedding_mode_for_collections(
68
  collections: list[str],
69
  requested_mode: Optional[str] = None,
@@ -274,7 +283,10 @@ def cleanup_stale_collections(ttl_seconds: int = 1800) -> list[str]:
274
  Returns the list of collection names that were removed.
275
  """
276
  cutoff = time.time() - ttl_seconds
277
- stale = [name for name, ts in list(_last_used.items()) if ts < cutoff]
 
 
 
278
  for name in stale:
279
  logger.info(f"Cleaning up stale collection '{name}' (idle > {ttl_seconds}s)")
280
  delete_collection(name)
@@ -290,6 +302,8 @@ def delete_collection(collection: str) -> bool:
290
  del _stores[collection]
291
  if collection in _collection_embeddings:
292
  del _collection_embeddings[collection]
 
 
293
 
294
  # Local import to avoid circular dependency with retriever
295
  from .retriever import _bm25_cache
 
26
  # Tracks last-used timestamp per collection (epoch seconds) for TTL-based cleanup
27
  _last_used: dict[str, float] = {}
28
  _collection_embeddings: dict[str, str] = {}
29
+ _pinned: set[str] = set()
30
 
31
  _EMBEDDING_META_FILE = "embedding.json"
32
 
 
65
  return None
66
 
67
 
68
+ def pin_collection(collection: str) -> None:
69
+ _pinned.add(collection)
70
+
71
+
72
+ def is_pinned_collection(collection: str) -> bool:
73
+ return collection in _pinned
74
+
75
+
76
  def resolve_embedding_mode_for_collections(
77
  collections: list[str],
78
  requested_mode: Optional[str] = None,
 
283
  Returns the list of collection names that were removed.
284
  """
285
  cutoff = time.time() - ttl_seconds
286
+ stale = [
287
+ name for name, ts in list(_last_used.items())
288
+ if ts < cutoff and name not in _pinned
289
+ ]
290
  for name in stale:
291
  logger.info(f"Cleaning up stale collection '{name}' (idle > {ttl_seconds}s)")
292
  delete_collection(name)
 
302
  del _stores[collection]
303
  if collection in _collection_embeddings:
304
  del _collection_embeddings[collection]
305
+ if collection in _pinned:
306
+ _pinned.discard(collection)
307
 
308
  # Local import to avoid circular dependency with retriever
309
  from .retriever import _bm25_cache