Krishna172912 commited on
Commit
8725d0d
·
unverified ·
1 Parent(s): 6017881

Create loader.py

Browse files
Files changed (1) hide show
  1. back_end/core/loader.py +197 -0
back_end/core/loader.py ADDED
@@ -0,0 +1,197 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ from config import AUTO_GEN_SCAN_EXTENSIONS,AUTO_GENERATED_MARKERS,SUPPORTED_TYPES,EXCLUDE_PATTERNS
2
+ from pathlib import Path
3
+ import pathspec
4
+ import json
5
+ import os
6
+ import pathspec
7
+
8
+ from langchain_core.documents import Document
9
+ from langchain_core.document_loaders.base import BaseLoader
10
+ from langchain_community.document_loaders import DirectoryLoader, TextLoader, PyPDFLoader
11
+
12
+ def is_valid(file_path):
13
+ path = Path(file_path)
14
+
15
+ if not path.is_file():
16
+ return False
17
+
18
+ name_lower = path.name.lower()
19
+ extension = path.suffix.lower()
20
+
21
+ if path.name in {"Dockerfile", "Makefile", "LICENSE", "Procfile", "Rakefile"}:
22
+ return True
23
+
24
+ if ".min." in name_lower or ".pb." in name_lower or ".g." in name_lower:
25
+ return False
26
+
27
+ if path.name in {".env"} or extension in {".pem", ".key"}:
28
+ return False
29
+
30
+ if extension == ".lock":
31
+ return False
32
+
33
+ # Reject auto-generated files before touching the size check
34
+ if extension in AUTO_GEN_SCAN_EXTENSIONS:
35
+ try:
36
+ with open(file_path, "r", errors="ignore") as f:
37
+ header = f.read(512).lower() # 512 bytes is fast; covers any header
38
+ if any(marker.lower() in header for marker in AUTO_GENERATED_MARKERS):
39
+ return False
40
+ except Exception:
41
+ pass # If we can't read the header, fall through to normal checks
42
+
43
+ size_kb = path.stat().st_size >> 10
44
+
45
+ if extension in SUPPORTED_TYPES["no_limit"]:
46
+ return True
47
+ if extension in SUPPORTED_TYPES["limit_2048kb"]:
48
+ return size_kb <= 2048
49
+ if extension in SUPPORTED_TYPES["limit_50kb"]:
50
+ return size_kb <= 50
51
+ if extension in SUPPORTED_TYPES["limit_30kb"]:
52
+ return size_kb <= 30
53
+ if extension in SUPPORTED_TYPES["limit_20kb"]:
54
+ return size_kb <= 20
55
+
56
+ if extension != "":
57
+ return False
58
+
59
+ try:
60
+ with open(file_path, "rb") as f:
61
+ chunk = f.read(2048)
62
+ if b"\x00" in chunk:
63
+ return False
64
+ chunk.decode("utf-8")
65
+ return True
66
+ except Exception:
67
+ return False
68
+
69
+
70
+ def count_valid_supported_files(directory_path: Path) -> int:
71
+ import os
72
+ import pathspec
73
+ from concurrent.futures import ThreadPoolExecutor
74
+
75
+ spec = pathspec.PathSpec.from_lines('gitwildmatch', EXCLUDE_PATTERNS)
76
+ root = str(directory_path)
77
+
78
+ # 1. FAST TRAVERSAL: Gather all file paths first
79
+ candidates = []
80
+ stack = [root]
81
+
82
+ # We define this locally since we can't edit globals.
83
+ # Checking a set is O(1) and bypasses slow pathspec regex for massive junk folders.
84
+ fast_ignore_dirs = {
85
+ ".git", ".svn", ".hg", "node_modules", "venv", ".venv", "env", "python_env",
86
+ "__pycache__", "dist", "build", "out", "target", "bin", "obj", ".next",
87
+ ".nuxt", ".vscode", ".idea", "coverage", "tmp", "temp"
88
+ }
89
+
90
+ while stack:
91
+ current_dir = stack.pop()
92
+
93
+ try:
94
+ with os.scandir(current_dir) as it:
95
+ for entry in it:
96
+ # Instantly skip giant junk directories before running slow regex
97
+ if entry.is_dir(follow_symlinks=False) and entry.name in fast_ignore_dirs:
98
+ continue
99
+
100
+ rel_path = os.path.relpath(entry.path, root)
101
+
102
+ if spec.match_file(rel_path):
103
+ continue
104
+
105
+ if entry.is_dir(follow_symlinks=False):
106
+ stack.append(entry.path)
107
+ elif entry.is_file(follow_symlinks=False):
108
+ # Do NOT validate here. Just collect the path.
109
+ candidates.append(entry.path)
110
+
111
+ except PermissionError:
112
+ continue
113
+
114
+ # At this line, len(candidates) gives you the instant total of 34,645 files!
115
+
116
+ # 2. MULTITHREADED VALIDATION: Run `is_valid` in parallel
117
+ valid_count = 0
118
+
119
+ # Using 32 workers is generally a sweet spot for I/O bound disk operations
120
+ with ThreadPoolExecutor(max_workers=32) as executor:
121
+ # executor.map feeds our candidates list into your existing `is_valid` function
122
+ results = executor.map(is_valid, candidates)
123
+
124
+ # Count how many returned True
125
+ valid_count = sum(1 for is_file_valid in results if is_file_valid)
126
+
127
+ return valid_count
128
+
129
+
130
+
131
+
132
+ def _Custom_ipynbLoader(file_path):
133
+ try:
134
+ with open(file_path, 'r', encoding="utf-8") as f:
135
+ notebook = json.load(f)
136
+
137
+ cells = []
138
+ for i, cell in enumerate(notebook.get("cells", [])):
139
+ if cell.get("cell_type") in ["code", "markdown"]:
140
+ source = cell.get("source", "")
141
+ content = "".join(source) if isinstance(source, list) else source
142
+ cells.append(f"[{cell['cell_type'].upper()} CELL {i}]\n{content}")
143
+
144
+ extraction = "\n\n".join(cells)
145
+ return [Document(page_content=extraction, metadata={"source": str(file_path)})]
146
+ except Exception:
147
+ return []
148
+
149
+
150
+ class _CustomLoader(BaseLoader):
151
+ def __init__(self, file_path: str):
152
+ self.file_path = file_path
153
+
154
+ def load(self):
155
+ if not is_valid(self.file_path):
156
+ return []
157
+
158
+ ext = Path(self.file_path).suffix.lower()
159
+
160
+ try:
161
+ if ext == ".pdf":
162
+ return PyPDFLoader(self.file_path).load()
163
+ elif ext == ".ipynb":
164
+ return _Custom_ipynbLoader(self.file_path)
165
+ else:
166
+ try:
167
+ return TextLoader(self.file_path, encoding="utf-8").load()
168
+ except UnicodeDecodeError:
169
+ # SAFETY: If the file has weird characters, open it manually and ignore errors
170
+ with open(self.file_path, "r", encoding="utf-8", errors="ignore") as f:
171
+ text = f.read()
172
+ return [Document(page_content=text, metadata={"source": self.file_path})]
173
+
174
+ except Exception as e:
175
+ print(f"Failed to load {self.file_path}: {e}")
176
+ return []
177
+
178
+ def load_repository_as_documents(repo_storage_path: Path) -> list[Document]:
179
+ loader = DirectoryLoader(
180
+ repo_storage_path,
181
+ glob="**/*.*",
182
+ exclude=EXCLUDE_PATTERNS,
183
+ loader_cls=_CustomLoader,
184
+ recursive=True,
185
+ silent_errors=True,
186
+ show_progress=True,
187
+ use_multithreading=True,
188
+ )
189
+
190
+ docs = loader.load()
191
+ print(f"Successfully loaded {len(docs)} documents.")
192
+ return docs
193
+
194
+
195
+
196
+
197
+