corpus-engine-tanjha 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.
- corpus_engine_tanjha-0.1.0/LICENSE +21 -0
- corpus_engine_tanjha-0.1.0/PKG-INFO +34 -0
- corpus_engine_tanjha-0.1.0/README.md +15 -0
- corpus_engine_tanjha-0.1.0/pyproject.toml +32 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/__init__.py +0 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/__main__.py +4 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/dbManager.py +134 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/embed.py +42 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/engine.py +81 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/entryObj.py +35 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/vectorCheck.py +464 -0
- corpus_engine_tanjha-0.1.0/src/corpusEngine/worker.py +182 -0
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
MIT License
|
|
2
|
+
|
|
3
|
+
Copyright (c) 2026 Tanush Ojha
|
|
4
|
+
|
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy
|
|
6
|
+
of this software and associated documentation files (the "Software"), to deal
|
|
7
|
+
in the Software without restriction, including without limitation the rights
|
|
8
|
+
to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
|
|
9
|
+
copies of the Software, and to permit persons to whom the Software is
|
|
10
|
+
furnished to do so, subject to the following conditions:
|
|
11
|
+
|
|
12
|
+
The above copyright notice and this permission notice shall be included in all
|
|
13
|
+
copies or substantial portions of the Software.
|
|
14
|
+
|
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
|
16
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
|
|
17
|
+
FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
|
|
18
|
+
AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
|
|
19
|
+
LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
|
|
20
|
+
OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
|
|
21
|
+
SOFTWARE.
|
|
@@ -0,0 +1,34 @@
|
|
|
1
|
+
Metadata-Version: 2.4
|
|
2
|
+
Name: corpus-engine_tanjha
|
|
3
|
+
Version: 0.1.0
|
|
4
|
+
Summary: A CLI tool meant for directory text querying VIA text embedding
|
|
5
|
+
Author: Tanush Ojha
|
|
6
|
+
Author-email: Tanush Ojha <tanushojha057@gmail.com>
|
|
7
|
+
License-Expression: MIT
|
|
8
|
+
License-File: LICENSE
|
|
9
|
+
Requires-Dist: einops>=0.8.2
|
|
10
|
+
Requires-Dist: langchain-text-splitters>=1.1.2
|
|
11
|
+
Requires-Dist: numpy>=2.4.6
|
|
12
|
+
Requires-Dist: scikit-learn>=1.9.0
|
|
13
|
+
Requires-Dist: sentence-transformers>=5.5.1
|
|
14
|
+
Requires-Dist: typer>=0.26.5
|
|
15
|
+
Requires-Python: >=3.11.9
|
|
16
|
+
Project-URL: Homepage, https://github.com/tanjha/corpus-engine
|
|
17
|
+
Project-URL: Issues, https://github.com/tanjha/corpus-engine/issues
|
|
18
|
+
Description-Content-Type: text/markdown
|
|
19
|
+
|
|
20
|
+
# Corpus Engine
|
|
21
|
+
Vector Embedding Powered Corpus Search Engine
|
|
22
|
+
|
|
23
|
+
|
|
24
|
+
# Build
|
|
25
|
+
|
|
26
|
+
Build in project root using:
|
|
27
|
+
```
|
|
28
|
+
uv tool install .
|
|
29
|
+
```
|
|
30
|
+
|
|
31
|
+
Dev build using:
|
|
32
|
+
```
|
|
33
|
+
uv tool install -e .
|
|
34
|
+
```
|
|
@@ -0,0 +1,32 @@
|
|
|
1
|
+
[project]
|
|
2
|
+
name = "corpus-engine_tanjha"
|
|
3
|
+
version = "0.1.0"
|
|
4
|
+
description = "A CLI tool meant for directory text querying VIA text embedding"
|
|
5
|
+
authors = [{name="Tanush Ojha",email="tanushojha057@gmail.com"}]
|
|
6
|
+
readme = "README.md"
|
|
7
|
+
requires-python = ">=3.11.9"
|
|
8
|
+
dependencies = [
|
|
9
|
+
"einops>=0.8.2",
|
|
10
|
+
"langchain-text-splitters>=1.1.2",
|
|
11
|
+
"numpy>=2.4.6",
|
|
12
|
+
"scikit-learn>=1.9.0",
|
|
13
|
+
"sentence-transformers>=5.5.1",
|
|
14
|
+
"typer>=0.26.5",
|
|
15
|
+
]
|
|
16
|
+
license= "MIT"
|
|
17
|
+
license-files=["LICEN[CS]E*"]
|
|
18
|
+
|
|
19
|
+
[project.scripts]
|
|
20
|
+
engine = "corpusEngine.engine:app"
|
|
21
|
+
|
|
22
|
+
[project.urls]
|
|
23
|
+
Homepage = "https://github.com/tanjha/corpus-engine"
|
|
24
|
+
Issues = "https://github.com/tanjha/corpus-engine/issues"
|
|
25
|
+
|
|
26
|
+
|
|
27
|
+
[build-system]
|
|
28
|
+
requires = ["uv_build >= 0.11.23, <0.12.0"]
|
|
29
|
+
build-backend = "uv_build"
|
|
30
|
+
|
|
31
|
+
[tool.uv.build-backend]
|
|
32
|
+
module-name = "corpusEngine"
|
|
File without changes
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
import sqlite3
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
import numpy as np
|
|
4
|
+
from .entryObj import entry
|
|
5
|
+
|
|
6
|
+
|
|
7
|
+
class dbManager:
|
|
8
|
+
def __init__(self, db: sqlite3.Connection):
|
|
9
|
+
self.db = db
|
|
10
|
+
|
|
11
|
+
def initSQL(self):
|
|
12
|
+
self.db.execute("""
|
|
13
|
+
CREATE TABLE IF NOT EXISTS files (
|
|
14
|
+
id INTEGER PRIMARY KEY,
|
|
15
|
+
path TEXT UNIQUE NOT NULL,
|
|
16
|
+
time_updated TEXT NOT NULL
|
|
17
|
+
);
|
|
18
|
+
""")
|
|
19
|
+
self.db.execute("""
|
|
20
|
+
CREATE TABLE IF NOT EXISTS chunks (
|
|
21
|
+
id INTEGER PRIMARY KEY,
|
|
22
|
+
file_id INTEGER NOT NULL REFERENCES files(id) ON DELETE CASCADE,
|
|
23
|
+
start_line INTEGER,
|
|
24
|
+
end_line INTEGER,
|
|
25
|
+
chunk_num INTEGER,
|
|
26
|
+
embedding BLOB NOT NULL
|
|
27
|
+
);
|
|
28
|
+
""")
|
|
29
|
+
self.db.execute(
|
|
30
|
+
"CREATE INDEX IF NOT EXISTS idx_chunks_file_id ON chunks(file_id);"
|
|
31
|
+
)
|
|
32
|
+
self.db.commit()
|
|
33
|
+
|
|
34
|
+
def getEmbeddings(self):
|
|
35
|
+
rows = self.db.execute("""
|
|
36
|
+
SELECT c.id, c.start_line, c.end_line, f.path, c.embedding, c.chunk_num
|
|
37
|
+
FROM chunks c
|
|
38
|
+
JOIN files f ON c.file_id = f.id
|
|
39
|
+
""")
|
|
40
|
+
return [
|
|
41
|
+
{
|
|
42
|
+
"id": row[0],
|
|
43
|
+
"start_line": row[1],
|
|
44
|
+
"end_line": row[2],
|
|
45
|
+
"path": Path(row[3]),
|
|
46
|
+
"embedding": np.frombuffer(row[4], dtype=np.float32),
|
|
47
|
+
"chunk_num": int(row[5]),
|
|
48
|
+
}
|
|
49
|
+
for row in rows
|
|
50
|
+
]
|
|
51
|
+
|
|
52
|
+
def getDB(self) -> sqlite3.Connection:
|
|
53
|
+
return self.db
|
|
54
|
+
|
|
55
|
+
def insertFull(self, entries: list[entry]):
|
|
56
|
+
if entries:
|
|
57
|
+
self._insertFile(str(entries[0].path.absolute()), entries[0].time)
|
|
58
|
+
print(f"inserting {str(entries[0].path.absolute())}")
|
|
59
|
+
self.db.commit()
|
|
60
|
+
for ent in entries:
|
|
61
|
+
file_id = self._getFileId(str(ent.path.absolute()))
|
|
62
|
+
self._insertChunk(
|
|
63
|
+
file_id,
|
|
64
|
+
ent.start_line,
|
|
65
|
+
ent.end_line,
|
|
66
|
+
ent.chunk_num,
|
|
67
|
+
ent.embedding,
|
|
68
|
+
)
|
|
69
|
+
self.db.commit()
|
|
70
|
+
|
|
71
|
+
def removeAll(self, paths: list[Path]):
|
|
72
|
+
for path in paths:
|
|
73
|
+
p = str(path)
|
|
74
|
+
fileId = self._getFileId(path)
|
|
75
|
+
if fileId is not None:
|
|
76
|
+
self.db.execute("DELETE FROM files where path = ?", (p,))
|
|
77
|
+
self.db.commit()
|
|
78
|
+
|
|
79
|
+
def removeFile(self, path: Path):
|
|
80
|
+
p = str(path)
|
|
81
|
+
fileId = self._getFileId(path)
|
|
82
|
+
if fileId is not None:
|
|
83
|
+
self.db.execute("DELETE FROM files where path = ?", (p,))
|
|
84
|
+
self.db.commit()
|
|
85
|
+
|
|
86
|
+
def _insertFile(
|
|
87
|
+
self,
|
|
88
|
+
path: Path,
|
|
89
|
+
time: str,
|
|
90
|
+
):
|
|
91
|
+
self.db.execute(
|
|
92
|
+
"""INSERT INTO files (path, time_updated)
|
|
93
|
+
VALUES (?, ?)""",
|
|
94
|
+
(
|
|
95
|
+
path,
|
|
96
|
+
time,
|
|
97
|
+
),
|
|
98
|
+
)
|
|
99
|
+
|
|
100
|
+
def _getFileId(self, path: Path) -> int:
|
|
101
|
+
p = str(path)
|
|
102
|
+
row = self.db.execute("SELECT id FROM files WHERE path = ?", (p,)).fetchone()
|
|
103
|
+
if row is None:
|
|
104
|
+
return row
|
|
105
|
+
return row[0]
|
|
106
|
+
|
|
107
|
+
def getFileTime(self, path: Path):
|
|
108
|
+
p = str(path)
|
|
109
|
+
row = self.db.execute(
|
|
110
|
+
"SELECT time_updated FROM files where path = ?", (p,)
|
|
111
|
+
).fetchone()
|
|
112
|
+
if row is None:
|
|
113
|
+
return row
|
|
114
|
+
return row[0]
|
|
115
|
+
|
|
116
|
+
def _insertChunk(
|
|
117
|
+
self,
|
|
118
|
+
file_id: int,
|
|
119
|
+
start_line: int,
|
|
120
|
+
end_line: int,
|
|
121
|
+
chunk_num: int,
|
|
122
|
+
embedding: np.ndarray,
|
|
123
|
+
):
|
|
124
|
+
self.db.execute(
|
|
125
|
+
"""INSERT INTO chunks (file_id, start_line, end_line, chunk_num, embedding)
|
|
126
|
+
VALUES (?, ?, ?, ?, ?)""",
|
|
127
|
+
(
|
|
128
|
+
file_id,
|
|
129
|
+
start_line,
|
|
130
|
+
end_line,
|
|
131
|
+
chunk_num,
|
|
132
|
+
embedding.astype(np.float32).tobytes(),
|
|
133
|
+
),
|
|
134
|
+
)
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
from sentence_transformers import SentenceTransformer, util
|
|
2
|
+
|
|
3
|
+
|
|
4
|
+
class embed_engine:
|
|
5
|
+
def __init__(self, embeddings):
|
|
6
|
+
self.model = SentenceTransformer(
|
|
7
|
+
"nomic-ai/nomic-embed-text-v1", trust_remote_code=True
|
|
8
|
+
)
|
|
9
|
+
self.embeddings = embeddings
|
|
10
|
+
|
|
11
|
+
def embedDoc(self, raw_text):
|
|
12
|
+
embedding = self.model.encode(
|
|
13
|
+
f"search_document: {raw_text}", normalize_embeddings=True
|
|
14
|
+
)
|
|
15
|
+
return embedding
|
|
16
|
+
|
|
17
|
+
def embedAll(self, texts: list[str]):
|
|
18
|
+
prefixed = [f"search_document: {text}" for text in texts]
|
|
19
|
+
embedding = self.model.encode(
|
|
20
|
+
prefixed, normalize_embeddings=True, batch_size=32, show_progress_bar=True
|
|
21
|
+
)
|
|
22
|
+
return embedding
|
|
23
|
+
|
|
24
|
+
def _embedQuery(self, raw_text):
|
|
25
|
+
embedding = self.model.encode(
|
|
26
|
+
f"search_query: {raw_text}", normalize_embeddings=True
|
|
27
|
+
)
|
|
28
|
+
return embedding
|
|
29
|
+
|
|
30
|
+
def similarityFull(self, query, embeddings):
|
|
31
|
+
for embed in embeddings:
|
|
32
|
+
embed["similarity"] = self._similarity(query, embed["embedding"])
|
|
33
|
+
|
|
34
|
+
return embeddings
|
|
35
|
+
|
|
36
|
+
def _similarity(self, query, embed):
|
|
37
|
+
similarity = util.cos_sim(query, embed)
|
|
38
|
+
return similarity
|
|
39
|
+
|
|
40
|
+
def setEmbed(self, embeddings):
|
|
41
|
+
self.embeddings = embeddings
|
|
42
|
+
return
|
|
@@ -0,0 +1,81 @@
|
|
|
1
|
+
import typer
|
|
2
|
+
from pathlib import Path
|
|
3
|
+
from typing import Annotated
|
|
4
|
+
import os
|
|
5
|
+
|
|
6
|
+
app = typer.Typer(no_args_is_help=True)
|
|
7
|
+
|
|
8
|
+
|
|
9
|
+
@app.command()
|
|
10
|
+
def init(debug: Annotated[bool, typer.Option(help="Print debug logs")] = False):
|
|
11
|
+
"""Initialize engine into current directory."""
|
|
12
|
+
doInit(debug)
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def doInit(debug: bool = False):
|
|
16
|
+
initialized = False
|
|
17
|
+
cwd = Path.cwd()
|
|
18
|
+
# print(cwd)
|
|
19
|
+
for item in cwd.iterdir():
|
|
20
|
+
if item.name == "corpusEngine.db":
|
|
21
|
+
initialized = True
|
|
22
|
+
print("[INFO] Corpus Engine already initialized")
|
|
23
|
+
if not initialized:
|
|
24
|
+
from .worker import setup
|
|
25
|
+
|
|
26
|
+
setup(cwd, debug)
|
|
27
|
+
|
|
28
|
+
|
|
29
|
+
@app.command()
|
|
30
|
+
def query(debug: Annotated[bool, typer.Option(help="Print debug logs")] = False):
|
|
31
|
+
"""Query directory using input - returns top 5 search results"""
|
|
32
|
+
initialized = False
|
|
33
|
+
cwd = Path.cwd()
|
|
34
|
+
# print(cwd)
|
|
35
|
+
for item in cwd.iterdir():
|
|
36
|
+
if item.name == "corpusEngine.db":
|
|
37
|
+
initialized = True
|
|
38
|
+
|
|
39
|
+
if initialized:
|
|
40
|
+
from .worker import query
|
|
41
|
+
|
|
42
|
+
query(debug)
|
|
43
|
+
else:
|
|
44
|
+
print("[INFO] Corpus Engine not initialized")
|
|
45
|
+
|
|
46
|
+
|
|
47
|
+
@app.command()
|
|
48
|
+
def update():
|
|
49
|
+
initialized = False
|
|
50
|
+
cwd = Path.cwd()
|
|
51
|
+
# print(cwd)
|
|
52
|
+
for item in cwd.iterdir():
|
|
53
|
+
if item.name == "corpusEngine.db":
|
|
54
|
+
initialized = True
|
|
55
|
+
|
|
56
|
+
if initialized:
|
|
57
|
+
from .worker import updateEmbed
|
|
58
|
+
|
|
59
|
+
updateEmbed(cwd)
|
|
60
|
+
else:
|
|
61
|
+
print("[INFO] Corpus Engine not initialized")
|
|
62
|
+
|
|
63
|
+
|
|
64
|
+
@app.command()
|
|
65
|
+
def reset(debug: Annotated[bool, typer.Option(help="Print debug logs")] = False):
|
|
66
|
+
"""Remove Corpus Engine and reinitialize it."""
|
|
67
|
+
doRemove()
|
|
68
|
+
doInit(debug)
|
|
69
|
+
|
|
70
|
+
|
|
71
|
+
@app.command()
|
|
72
|
+
def remove():
|
|
73
|
+
doRemove()
|
|
74
|
+
|
|
75
|
+
|
|
76
|
+
def doRemove():
|
|
77
|
+
if os.path.exists("corpusEngine.db"):
|
|
78
|
+
os.remove("corpusEngine.db")
|
|
79
|
+
print("[INFO] Removed Corpus Engine")
|
|
80
|
+
else:
|
|
81
|
+
print("[INFO] Corpus Engine not initialized")
|
|
@@ -0,0 +1,35 @@
|
|
|
1
|
+
from pathlib import Path
|
|
2
|
+
import numpy as np
|
|
3
|
+
|
|
4
|
+
|
|
5
|
+
class entry:
|
|
6
|
+
path: Path
|
|
7
|
+
start_line: int
|
|
8
|
+
end_line: int
|
|
9
|
+
time: str
|
|
10
|
+
chunk_num: int
|
|
11
|
+
embedding: np.ndarray
|
|
12
|
+
|
|
13
|
+
def __init__(
|
|
14
|
+
self,
|
|
15
|
+
path: Path,
|
|
16
|
+
start_line: int,
|
|
17
|
+
end_line: int,
|
|
18
|
+
time: str,
|
|
19
|
+
chunk_num: int,
|
|
20
|
+
):
|
|
21
|
+
self.path = path
|
|
22
|
+
self.start_line = start_line
|
|
23
|
+
self.end_line = end_line
|
|
24
|
+
self.time = time
|
|
25
|
+
self.chunk_num = chunk_num
|
|
26
|
+
self.embedding = None
|
|
27
|
+
|
|
28
|
+
def set_embed(self, embed: np.ndarray):
|
|
29
|
+
self.embedding = embed
|
|
30
|
+
return
|
|
31
|
+
|
|
32
|
+
def printAll(self):
|
|
33
|
+
print(
|
|
34
|
+
f"Path: {self.path} Start: {self.start_line} End: {self.end_line} Time {self.time} Raw: {self.chunk_num}"
|
|
35
|
+
)
|
|
@@ -0,0 +1,464 @@
|
|
|
1
|
+
"""
|
|
2
|
+
vectorizable_extensions.py
|
|
3
|
+
==========================
|
|
4
|
+
|
|
5
|
+
A comprehensive catalog of file-type suffixes whose contents can be turned into
|
|
6
|
+
human-readable plaintext, and therefore chunked + embedded ("vectorized") for
|
|
7
|
+
search / RAG pipelines.
|
|
8
|
+
|
|
9
|
+
Two practical buckets:
|
|
10
|
+
|
|
11
|
+
1. PLAINTEXT_NATIVE -> the bytes on disk are already text; just decode them.
|
|
12
|
+
2. NEEDS_EXTRACTION -> binary/container formats that yield text only after a
|
|
13
|
+
parser/library extracts it (PDF, DOCX, EPUB, ...).
|
|
14
|
+
|
|
15
|
+
Use `VECTORIZABLE_EXTENSIONS` for a single flat membership check, or the
|
|
16
|
+
category dicts below if you want to route each file to the right loader.
|
|
17
|
+
|
|
18
|
+
All suffixes are lowercase and include the leading dot.
|
|
19
|
+
|
|
20
|
+
Optional buckets (images via OCR, audio/video via transcription) are provided
|
|
21
|
+
separately at the bottom and are NOT included in the defaults, because their
|
|
22
|
+
"text" is derived rather than contained in the file.
|
|
23
|
+
"""
|
|
24
|
+
|
|
25
|
+
from pathlib import Path
|
|
26
|
+
|
|
27
|
+
# ---------------------------------------------------------------------------
|
|
28
|
+
# 1. PLAINTEXT-NATIVE (read directly, e.g. open(path, encoding="utf-8"))
|
|
29
|
+
# ---------------------------------------------------------------------------
|
|
30
|
+
|
|
31
|
+
PLAIN_TEXT = {
|
|
32
|
+
".txt",
|
|
33
|
+
".text",
|
|
34
|
+
".log",
|
|
35
|
+
".nfo",
|
|
36
|
+
".me",
|
|
37
|
+
".readme",
|
|
38
|
+
".asc",
|
|
39
|
+
".diff",
|
|
40
|
+
".patch",
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
MARKUP_AND_DOCS_TEXT = {
|
|
44
|
+
".md",
|
|
45
|
+
".markdown",
|
|
46
|
+
".mdown",
|
|
47
|
+
".mkd",
|
|
48
|
+
".mdx",
|
|
49
|
+
".rst",
|
|
50
|
+
".adoc",
|
|
51
|
+
".asciidoc",
|
|
52
|
+
".org",
|
|
53
|
+
".textile",
|
|
54
|
+
".creole",
|
|
55
|
+
".wiki",
|
|
56
|
+
".tex",
|
|
57
|
+
".latex",
|
|
58
|
+
".ltx",
|
|
59
|
+
".bib",
|
|
60
|
+
".sty",
|
|
61
|
+
".cls",
|
|
62
|
+
".man",
|
|
63
|
+
".roff",
|
|
64
|
+
".1",
|
|
65
|
+
".2",
|
|
66
|
+
".3",
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
WEB_AND_TEMPLATES = {
|
|
70
|
+
".html",
|
|
71
|
+
".htm",
|
|
72
|
+
".xhtml",
|
|
73
|
+
".shtml",
|
|
74
|
+
".css",
|
|
75
|
+
".scss",
|
|
76
|
+
".sass",
|
|
77
|
+
".less",
|
|
78
|
+
".styl",
|
|
79
|
+
".vue",
|
|
80
|
+
".svelte",
|
|
81
|
+
".astro",
|
|
82
|
+
".jsx",
|
|
83
|
+
".tsx",
|
|
84
|
+
".haml",
|
|
85
|
+
".pug",
|
|
86
|
+
".jade",
|
|
87
|
+
".ejs",
|
|
88
|
+
".erb",
|
|
89
|
+
".hbs",
|
|
90
|
+
".handlebars",
|
|
91
|
+
".mustache",
|
|
92
|
+
".liquid",
|
|
93
|
+
".twig",
|
|
94
|
+
".njk",
|
|
95
|
+
".j2",
|
|
96
|
+
".jinja",
|
|
97
|
+
".jinja2",
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
DATA_AND_CONFIG = {
|
|
101
|
+
".json",
|
|
102
|
+
".jsonl",
|
|
103
|
+
".ndjson",
|
|
104
|
+
".json5",
|
|
105
|
+
".jsonc",
|
|
106
|
+
".geojson",
|
|
107
|
+
".yaml",
|
|
108
|
+
".yml",
|
|
109
|
+
".toml",
|
|
110
|
+
".ini",
|
|
111
|
+
".cfg",
|
|
112
|
+
".conf",
|
|
113
|
+
".config",
|
|
114
|
+
".properties",
|
|
115
|
+
".env",
|
|
116
|
+
".csv",
|
|
117
|
+
".tsv",
|
|
118
|
+
".psv",
|
|
119
|
+
".tab",
|
|
120
|
+
".xml",
|
|
121
|
+
".xsd",
|
|
122
|
+
".xsl",
|
|
123
|
+
".xslt",
|
|
124
|
+
".dtd",
|
|
125
|
+
".rss",
|
|
126
|
+
".atom",
|
|
127
|
+
".plist",
|
|
128
|
+
".svg",
|
|
129
|
+
".graphql",
|
|
130
|
+
".gql",
|
|
131
|
+
".proto",
|
|
132
|
+
".thrift",
|
|
133
|
+
".avsc",
|
|
134
|
+
".prisma",
|
|
135
|
+
".ron",
|
|
136
|
+
".hcl",
|
|
137
|
+
".editorconfig",
|
|
138
|
+
".gitignore",
|
|
139
|
+
".gitattributes",
|
|
140
|
+
".dockerignore",
|
|
141
|
+
".npmrc",
|
|
142
|
+
".yarnrc",
|
|
143
|
+
}
|
|
144
|
+
|
|
145
|
+
SOURCE_CODE = {
|
|
146
|
+
# Python
|
|
147
|
+
".py",
|
|
148
|
+
".pyw",
|
|
149
|
+
".pyi",
|
|
150
|
+
".pyx",
|
|
151
|
+
".pxd",
|
|
152
|
+
".rpy",
|
|
153
|
+
# JavaScript / TypeScript
|
|
154
|
+
".js",
|
|
155
|
+
".mjs",
|
|
156
|
+
".cjs",
|
|
157
|
+
".ts",
|
|
158
|
+
".mts",
|
|
159
|
+
".cts",
|
|
160
|
+
".coffee",
|
|
161
|
+
# C / C++
|
|
162
|
+
".c",
|
|
163
|
+
".h",
|
|
164
|
+
".cpp",
|
|
165
|
+
".cc",
|
|
166
|
+
".cxx",
|
|
167
|
+
".c++",
|
|
168
|
+
".hpp",
|
|
169
|
+
".hh",
|
|
170
|
+
".hxx",
|
|
171
|
+
".h++",
|
|
172
|
+
".ino",
|
|
173
|
+
# C# / .NET
|
|
174
|
+
".cs",
|
|
175
|
+
".csx",
|
|
176
|
+
".vb",
|
|
177
|
+
".fs",
|
|
178
|
+
".fsx",
|
|
179
|
+
".fsi",
|
|
180
|
+
# JVM languages
|
|
181
|
+
".java",
|
|
182
|
+
".kt",
|
|
183
|
+
".kts",
|
|
184
|
+
".scala",
|
|
185
|
+
".sc",
|
|
186
|
+
".groovy",
|
|
187
|
+
".gradle",
|
|
188
|
+
".clj",
|
|
189
|
+
".cljs",
|
|
190
|
+
".cljc",
|
|
191
|
+
".edn",
|
|
192
|
+
# Go / Rust / Zig / Nim / D / Crystal
|
|
193
|
+
".go",
|
|
194
|
+
".rs",
|
|
195
|
+
".zig",
|
|
196
|
+
".nim",
|
|
197
|
+
".nims",
|
|
198
|
+
".d",
|
|
199
|
+
".cr",
|
|
200
|
+
# Ruby
|
|
201
|
+
".rb",
|
|
202
|
+
".rake",
|
|
203
|
+
".gemspec",
|
|
204
|
+
".ru",
|
|
205
|
+
# PHP / Perl
|
|
206
|
+
".php",
|
|
207
|
+
".phtml",
|
|
208
|
+
".php3",
|
|
209
|
+
".php4",
|
|
210
|
+
".php5",
|
|
211
|
+
".pl",
|
|
212
|
+
".pm",
|
|
213
|
+
".pod",
|
|
214
|
+
".t",
|
|
215
|
+
# Swift / Objective-C
|
|
216
|
+
".swift",
|
|
217
|
+
".m",
|
|
218
|
+
".mm",
|
|
219
|
+
# Shell / scripting
|
|
220
|
+
".sh",
|
|
221
|
+
".bash",
|
|
222
|
+
".zsh",
|
|
223
|
+
".fish",
|
|
224
|
+
".ksh",
|
|
225
|
+
".csh",
|
|
226
|
+
".tcsh",
|
|
227
|
+
".ps1",
|
|
228
|
+
".psm1",
|
|
229
|
+
".psd1",
|
|
230
|
+
".bat",
|
|
231
|
+
".cmd",
|
|
232
|
+
# Lua / Tcl / Dart / Julia / R
|
|
233
|
+
".lua",
|
|
234
|
+
".tcl",
|
|
235
|
+
".dart",
|
|
236
|
+
".jl",
|
|
237
|
+
".r",
|
|
238
|
+
".rmd",
|
|
239
|
+
".rnw",
|
|
240
|
+
# Functional / academic
|
|
241
|
+
".ex",
|
|
242
|
+
".exs",
|
|
243
|
+
".erl",
|
|
244
|
+
".hrl",
|
|
245
|
+
".hs",
|
|
246
|
+
".lhs",
|
|
247
|
+
".ml",
|
|
248
|
+
".mli",
|
|
249
|
+
".elm",
|
|
250
|
+
".scm",
|
|
251
|
+
".ss",
|
|
252
|
+
".lisp",
|
|
253
|
+
".lsp",
|
|
254
|
+
".cl",
|
|
255
|
+
".el",
|
|
256
|
+
".rkt",
|
|
257
|
+
# Systems / legacy
|
|
258
|
+
".pas",
|
|
259
|
+
".pp",
|
|
260
|
+
".f",
|
|
261
|
+
".for",
|
|
262
|
+
".f90",
|
|
263
|
+
".f95",
|
|
264
|
+
".f03",
|
|
265
|
+
".f08",
|
|
266
|
+
".cob",
|
|
267
|
+
".cbl",
|
|
268
|
+
".asm",
|
|
269
|
+
".s",
|
|
270
|
+
".vhd",
|
|
271
|
+
".vhdl",
|
|
272
|
+
".v",
|
|
273
|
+
".sv",
|
|
274
|
+
".vbs",
|
|
275
|
+
# Other
|
|
276
|
+
".sql",
|
|
277
|
+
".sol",
|
|
278
|
+
".gd",
|
|
279
|
+
".gdscript",
|
|
280
|
+
".applescript",
|
|
281
|
+
".scpt",
|
|
282
|
+
".vala",
|
|
283
|
+
".hx",
|
|
284
|
+
".moon",
|
|
285
|
+
".p",
|
|
286
|
+
".ada",
|
|
287
|
+
".adb",
|
|
288
|
+
".ads",
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
BUILD_AND_INFRA = {
|
|
292
|
+
".dockerfile",
|
|
293
|
+
".containerfile",
|
|
294
|
+
".mk",
|
|
295
|
+
".cmake",
|
|
296
|
+
".make",
|
|
297
|
+
".bazel",
|
|
298
|
+
".bzl",
|
|
299
|
+
".buck",
|
|
300
|
+
".starlark",
|
|
301
|
+
".star",
|
|
302
|
+
".tf",
|
|
303
|
+
".tfvars",
|
|
304
|
+
".nomad",
|
|
305
|
+
".jenkinsfile",
|
|
306
|
+
".cabal",
|
|
307
|
+
".lock",
|
|
308
|
+
}
|
|
309
|
+
|
|
310
|
+
NOTEBOOKS_AND_LITERATE = {
|
|
311
|
+
".ipynb", # JSON container; cell text is readable after parsing
|
|
312
|
+
".rmd", # already above, kept for clarity
|
|
313
|
+
}
|
|
314
|
+
|
|
315
|
+
SUBTITLES = {
|
|
316
|
+
".srt",
|
|
317
|
+
".vtt",
|
|
318
|
+
".sub",
|
|
319
|
+
".ass",
|
|
320
|
+
".ssa",
|
|
321
|
+
".sbv",
|
|
322
|
+
".lrc",
|
|
323
|
+
}
|
|
324
|
+
|
|
325
|
+
EMAIL_TEXT = {
|
|
326
|
+
".eml",
|
|
327
|
+
".mbox",
|
|
328
|
+
".mbx",
|
|
329
|
+
}
|
|
330
|
+
|
|
331
|
+
# Aggregate of everything that is already plaintext on disk
|
|
332
|
+
PLAINTEXT_NATIVE = (
|
|
333
|
+
PLAIN_TEXT
|
|
334
|
+
| MARKUP_AND_DOCS_TEXT
|
|
335
|
+
| WEB_AND_TEMPLATES
|
|
336
|
+
| DATA_AND_CONFIG
|
|
337
|
+
| SOURCE_CODE
|
|
338
|
+
| BUILD_AND_INFRA
|
|
339
|
+
| NOTEBOOKS_AND_LITERATE
|
|
340
|
+
| SUBTITLES
|
|
341
|
+
| EMAIL_TEXT
|
|
342
|
+
)
|
|
343
|
+
|
|
344
|
+
# ---------------------------------------------------------------------------
|
|
345
|
+
# 2. NEEDS-EXTRACTION (binary/container; text via a parser library)
|
|
346
|
+
# Suggested library noted per format.
|
|
347
|
+
# ---------------------------------------------------------------------------
|
|
348
|
+
|
|
349
|
+
NEEDS_EXTRACTION = {
|
|
350
|
+
# Documents
|
|
351
|
+
".pdf", # pdfplumber / pypdf / PyMuPDF (fitz) / unstructured
|
|
352
|
+
".doc", # antiword / textract / LibreOffice
|
|
353
|
+
".docx", # python-docx / docx2txt / unstructured
|
|
354
|
+
".odt", # odfpy
|
|
355
|
+
".rtf", # striprtf
|
|
356
|
+
".wpd", # libwpd
|
|
357
|
+
".epub", # ebooklib / unstructured
|
|
358
|
+
".mobi", # mobi / kindleunpack
|
|
359
|
+
".azw",
|
|
360
|
+
".azw3",
|
|
361
|
+
".fb2", # ebook-convert (Calibre)
|
|
362
|
+
".pages", # Apple Pages (zip container; LibreOffice / textutil)
|
|
363
|
+
# Spreadsheets
|
|
364
|
+
".xls", # xlrd
|
|
365
|
+
".xlsx",
|
|
366
|
+
".xlsm", # openpyxl / pandas
|
|
367
|
+
".ods", # odfpy / pandas
|
|
368
|
+
".numbers", # numbers-parser
|
|
369
|
+
# Presentations
|
|
370
|
+
".ppt", # LibreOffice / textract
|
|
371
|
+
".pptx", # python-pptx
|
|
372
|
+
".odp", # odfpy
|
|
373
|
+
".key", # Apple Keynote (LibreOffice)
|
|
374
|
+
# Email containers
|
|
375
|
+
".msg", # extract-msg
|
|
376
|
+
".pst",
|
|
377
|
+
".ost", # libpff / readpst
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
# ---------------------------------------------------------------------------
|
|
381
|
+
# 3. FLAT SET — use this for a quick "is this file vectorizable?" check
|
|
382
|
+
# ---------------------------------------------------------------------------
|
|
383
|
+
|
|
384
|
+
VECTORIZABLE_EXTENSIONS = frozenset(PLAINTEXT_NATIVE | NEEDS_EXTRACTION)
|
|
385
|
+
|
|
386
|
+
# Sorted list form, if you prefer a list literal:
|
|
387
|
+
VECTORIZABLE_EXTENSIONS_LIST = sorted(VECTORIZABLE_EXTENSIONS)
|
|
388
|
+
|
|
389
|
+
# ---------------------------------------------------------------------------
|
|
390
|
+
# 4. OPTIONAL — derived text, NOT included by default
|
|
391
|
+
# (the file does not *contain* text; text is produced from it)
|
|
392
|
+
# ---------------------------------------------------------------------------
|
|
393
|
+
|
|
394
|
+
OCR_IMAGE_EXTENSIONS = {
|
|
395
|
+
".png",
|
|
396
|
+
".jpg",
|
|
397
|
+
".jpeg",
|
|
398
|
+
".tif",
|
|
399
|
+
".tiff",
|
|
400
|
+
".bmp",
|
|
401
|
+
".gif",
|
|
402
|
+
".webp",
|
|
403
|
+
# via pytesseract / easyocr / unstructured
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
TRANSCRIBABLE_AV_EXTENSIONS = {
|
|
407
|
+
".mp3",
|
|
408
|
+
".wav",
|
|
409
|
+
".m4a",
|
|
410
|
+
".flac",
|
|
411
|
+
".ogg",
|
|
412
|
+
".aac",
|
|
413
|
+
".mp4",
|
|
414
|
+
".mov",
|
|
415
|
+
".mkv",
|
|
416
|
+
".avi",
|
|
417
|
+
".webm",
|
|
418
|
+
".m4v",
|
|
419
|
+
# via whisper / faster-whisper
|
|
420
|
+
}
|
|
421
|
+
|
|
422
|
+
# ---------------------------------------------------------------------------
|
|
423
|
+
# 5. Helpers
|
|
424
|
+
# ---------------------------------------------------------------------------
|
|
425
|
+
|
|
426
|
+
|
|
427
|
+
def is_vectorizable(path, include_ocr=False, include_av=False) -> bool:
|
|
428
|
+
"""Return True if `path`'s suffix can be turned into text."""
|
|
429
|
+
ext = Path(path).suffix.lower()
|
|
430
|
+
if ext in VECTORIZABLE_EXTENSIONS:
|
|
431
|
+
return True
|
|
432
|
+
if include_ocr and ext in OCR_IMAGE_EXTENSIONS:
|
|
433
|
+
return True
|
|
434
|
+
if include_av and ext in TRANSCRIBABLE_AV_EXTENSIONS:
|
|
435
|
+
return True
|
|
436
|
+
# Common extension-less text files (Dockerfile, Makefile, etc.)
|
|
437
|
+
if ext == "" and Path(path).name.lower() in {
|
|
438
|
+
"dockerfile",
|
|
439
|
+
"makefile",
|
|
440
|
+
"rakefile",
|
|
441
|
+
"gemfile",
|
|
442
|
+
"procfile",
|
|
443
|
+
"jenkinsfile",
|
|
444
|
+
"vagrantfile",
|
|
445
|
+
"license",
|
|
446
|
+
"readme",
|
|
447
|
+
"authors",
|
|
448
|
+
}:
|
|
449
|
+
return True
|
|
450
|
+
return False
|
|
451
|
+
|
|
452
|
+
|
|
453
|
+
def needs_extraction_library(path) -> bool:
|
|
454
|
+
"""True if the file is binary and requires a parser to read its text."""
|
|
455
|
+
return Path(path).suffix.lower() in NEEDS_EXTRACTION
|
|
456
|
+
|
|
457
|
+
|
|
458
|
+
if __name__ == "__main__":
|
|
459
|
+
# print(f"Plaintext-native suffixes : {len(PLAINTEXT_NATIVE)}")
|
|
460
|
+
# print(f"Needs-extraction suffixes : {len(NEEDS_EXTRACTION)}")
|
|
461
|
+
# print(f"Total vectorizable : {len(VECTORIZABLE_EXTENSIONS)}")
|
|
462
|
+
# print()
|
|
463
|
+
# print(VECTORIZABLE_EXTENSIONS_LIST)
|
|
464
|
+
pass
|
|
@@ -0,0 +1,182 @@
|
|
|
1
|
+
from langchain_text_splitters import RecursiveCharacterTextSplitter
|
|
2
|
+
import sqlite3
|
|
3
|
+
from pathlib import Path
|
|
4
|
+
from datetime import datetime
|
|
5
|
+
from .embed import embed_engine
|
|
6
|
+
from .entryObj import entry
|
|
7
|
+
from .dbManager import dbManager
|
|
8
|
+
from .vectorCheck import is_vectorizable
|
|
9
|
+
|
|
10
|
+
scale = 2
|
|
11
|
+
config = {"chunk_size": 256 * scale, "overlap": 26 * scale}
|
|
12
|
+
debug = False
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
def setup(cwd: Path, dbg: bool = False):
|
|
16
|
+
# debug = dbg
|
|
17
|
+
print("[INFO] Initializing Corpus Engine...")
|
|
18
|
+
dbM = _initDB("corpusEngine.db")
|
|
19
|
+
dbM.initSQL()
|
|
20
|
+
|
|
21
|
+
splitter = RecursiveCharacterTextSplitter(
|
|
22
|
+
chunk_size=config["chunk_size"],
|
|
23
|
+
chunk_overlap=config["overlap"],
|
|
24
|
+
separators=[""],
|
|
25
|
+
strip_whitespace=False,
|
|
26
|
+
)
|
|
27
|
+
embedder = embed_engine([])
|
|
28
|
+
|
|
29
|
+
_lookDir(splitter=splitter, path=cwd, dbM=dbM, embedder=embedder)
|
|
30
|
+
print("[INFO] Finished initialization.")
|
|
31
|
+
|
|
32
|
+
|
|
33
|
+
def updateEmbed(cwd: Path, dbg: bool = False):
|
|
34
|
+
dbM = _initDB("corpusEngine.db")
|
|
35
|
+
embedder = embed_engine([])
|
|
36
|
+
splitter = RecursiveCharacterTextSplitter(
|
|
37
|
+
chunk_size=config["chunk_size"],
|
|
38
|
+
chunk_overlap=config["overlap"],
|
|
39
|
+
separators=[""],
|
|
40
|
+
strip_whitespace=False,
|
|
41
|
+
)
|
|
42
|
+
walked_dir = _listAll(cwd)
|
|
43
|
+
paths = {str(p.absolute()) for p in walked_dir}
|
|
44
|
+
db_rows = {
|
|
45
|
+
row[0]: row[1]
|
|
46
|
+
for row in dbM.getDB().execute("SELECT path, time_updated FROM files")
|
|
47
|
+
}
|
|
48
|
+
for path in paths:
|
|
49
|
+
time = datetime.fromtimestamp(Path(path).stat().st_mtime)
|
|
50
|
+
if path not in db_rows:
|
|
51
|
+
_splitFile(file=Path(path), splitter=splitter, dbM=dbM, embedder=embedder)
|
|
52
|
+
elif time != db_rows[path]:
|
|
53
|
+
print("[INFO] Updating file")
|
|
54
|
+
dbM.removeFile(Path(path))
|
|
55
|
+
_splitFile(file=Path(path), splitter=splitter, dbM=dbM, embedder=embedder)
|
|
56
|
+
for removed in db_rows.keys() - paths:
|
|
57
|
+
dbM.removeFile(Path(removed))
|
|
58
|
+
|
|
59
|
+
|
|
60
|
+
def query(debug: bool = False):
|
|
61
|
+
print("Querying")
|
|
62
|
+
dbM = _initDB("corpusEngine.db")
|
|
63
|
+
embeddings = dbM.getEmbeddings()
|
|
64
|
+
embedder = embed_engine(embeddings)
|
|
65
|
+
query = _getQuery(embedder)
|
|
66
|
+
sim_embeddings = embedder.similarityFull(query, embeddings)
|
|
67
|
+
similarities = sorted(sim_embeddings, key=lambda x: x["similarity"], reverse=True)[
|
|
68
|
+
:5
|
|
69
|
+
]
|
|
70
|
+
splitter = RecursiveCharacterTextSplitter(
|
|
71
|
+
chunk_size=config["chunk_size"],
|
|
72
|
+
chunk_overlap=config["overlap"],
|
|
73
|
+
separators=[""],
|
|
74
|
+
strip_whitespace=False,
|
|
75
|
+
)
|
|
76
|
+
|
|
77
|
+
for sim in similarities:
|
|
78
|
+
similarity = sim["similarity"]
|
|
79
|
+
path = sim["path"]
|
|
80
|
+
start_line = sim["start_line"]
|
|
81
|
+
end_line = sim["end_line"]
|
|
82
|
+
|
|
83
|
+
chunk_num = sim["chunk_num"]
|
|
84
|
+
with open(Path(path), "r") as f:
|
|
85
|
+
raw_full = f.read()
|
|
86
|
+
chunks = splitter.split_text(raw_full)
|
|
87
|
+
text = chunks[chunk_num]
|
|
88
|
+
print(f"Similarity: {similarity.item():.4f}")
|
|
89
|
+
print(f"File: {path}")
|
|
90
|
+
if int(start_line) == int(end_line):
|
|
91
|
+
print(f"Line: {start_line}")
|
|
92
|
+
else:
|
|
93
|
+
print(f"Lines: {start_line}-{end_line}")
|
|
94
|
+
print(f"Text: {text}")
|
|
95
|
+
print("-------------------------------------------------------------")
|
|
96
|
+
|
|
97
|
+
|
|
98
|
+
def _getQuery(embedder: embed_engine) -> str:
|
|
99
|
+
print("Query:")
|
|
100
|
+
raw = input("> ")
|
|
101
|
+
return embedder._embedQuery(raw)
|
|
102
|
+
|
|
103
|
+
|
|
104
|
+
def _lookDir(
|
|
105
|
+
splitter: RecursiveCharacterTextSplitter,
|
|
106
|
+
path: Path,
|
|
107
|
+
dbM: dbManager,
|
|
108
|
+
embedder: embed_engine,
|
|
109
|
+
_seen=None,
|
|
110
|
+
):
|
|
111
|
+
_seen = _seen if _seen is not None else set()
|
|
112
|
+
real = path.resolve()
|
|
113
|
+
if real in _seen:
|
|
114
|
+
return
|
|
115
|
+
_seen.add(real)
|
|
116
|
+
pathlist = []
|
|
117
|
+
for item in path.iterdir():
|
|
118
|
+
pathlist.append(item)
|
|
119
|
+
if item.is_dir():
|
|
120
|
+
_lookDir(splitter, item, dbM, embedder, _seen)
|
|
121
|
+
elif is_vectorizable(item):
|
|
122
|
+
_splitFile(item, splitter, dbM, embedder)
|
|
123
|
+
|
|
124
|
+
|
|
125
|
+
def _listAll(
|
|
126
|
+
path: Path,
|
|
127
|
+
_seen=None,
|
|
128
|
+
) -> list[Path]:
|
|
129
|
+
_seen = _seen if _seen is not None else set()
|
|
130
|
+
real = path.resolve()
|
|
131
|
+
if real in _seen:
|
|
132
|
+
return []
|
|
133
|
+
_seen.add(real)
|
|
134
|
+
|
|
135
|
+
paths = []
|
|
136
|
+
for item in path.iterdir():
|
|
137
|
+
if item.is_dir():
|
|
138
|
+
paths += _listAll(item, _seen)
|
|
139
|
+
elif is_vectorizable(item):
|
|
140
|
+
paths.append(item)
|
|
141
|
+
return paths
|
|
142
|
+
|
|
143
|
+
|
|
144
|
+
def _initDB(connection: str) -> dbManager:
|
|
145
|
+
db = sqlite3.connect(connection)
|
|
146
|
+
db.execute("PRAGMA foreign_keys = ON")
|
|
147
|
+
return dbManager(db)
|
|
148
|
+
|
|
149
|
+
|
|
150
|
+
def _splitFile(
|
|
151
|
+
file: Path,
|
|
152
|
+
splitter: RecursiveCharacterTextSplitter,
|
|
153
|
+
dbM: dbManager,
|
|
154
|
+
embedder: embed_engine,
|
|
155
|
+
):
|
|
156
|
+
time = datetime.fromtimestamp(file.stat().st_mtime)
|
|
157
|
+
with open(file, "r") as f:
|
|
158
|
+
raw_full = f.read()
|
|
159
|
+
chunks = splitter.split_text(raw_full)
|
|
160
|
+
to_embed = []
|
|
161
|
+
entries = []
|
|
162
|
+
line = 0
|
|
163
|
+
for i, chunk in enumerate(chunks):
|
|
164
|
+
start_line = line
|
|
165
|
+
line += chunk.count("\n")
|
|
166
|
+
end_line = line
|
|
167
|
+
|
|
168
|
+
to_embed.append(chunk)
|
|
169
|
+
ent = entry(
|
|
170
|
+
path=file,
|
|
171
|
+
start_line=start_line,
|
|
172
|
+
end_line=end_line,
|
|
173
|
+
time=time,
|
|
174
|
+
chunk_num=i,
|
|
175
|
+
)
|
|
176
|
+
# ent.printAll()
|
|
177
|
+
entries.append(ent)
|
|
178
|
+
embedded = embedder.embedAll(to_embed)
|
|
179
|
+
for chunk, ent in zip(embedded, entries):
|
|
180
|
+
ent.embedding = chunk
|
|
181
|
+
dbM.insertFull(entries)
|
|
182
|
+
print(f"[DEBUG] inserted all chunks of {file.name}")
|