pembot 0.0.7__py2.py3-none-any.whl → 0.0.8__py2.py3-none-any.whl
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.
Potentially problematic release.
This version of pembot might be problematic. Click here for more details.
- pembot/TextEmbedder/mongodb_embedder.py +50 -19
- pembot/TextEmbedder/mongodb_index_creator.py +29 -24
- pembot/__init__.py +1 -1
- pembot/config/config.yaml +1 -1
- pembot/query.py +5 -4
- {pembot-0.0.7.dist-info → pembot-0.0.8.dist-info}/METADATA +1 -1
- {pembot-0.0.7.dist-info → pembot-0.0.8.dist-info}/RECORD +9 -9
- {pembot-0.0.7.dist-info → pembot-0.0.8.dist-info}/WHEEL +0 -0
- {pembot-0.0.7.dist-info → pembot-0.0.8.dist-info}/licenses/LICENSE +0 -0
|
@@ -29,6 +29,7 @@ def search_within_document(
|
|
|
29
29
|
limit: int = 5,
|
|
30
30
|
index_name: str = "test_search",
|
|
31
31
|
embeddings_collection_name: str= "doc_chunks",
|
|
32
|
+
document_belongs_to_a_type = "",
|
|
32
33
|
):
|
|
33
34
|
"""
|
|
34
35
|
Performs a vector similarity search within the chunks of a specific document
|
|
@@ -42,6 +43,7 @@ def search_within_document(
|
|
|
42
43
|
index_name: The name of your MongoDB Atlas Vector Search index.
|
|
43
44
|
You MUST have a vector search index created on the 'embedding' field
|
|
44
45
|
of the 'embeddings_collection' collection for this to work efficiently.
|
|
46
|
+
document_belongs_to_a_type: When search spaces intersect for different docIds, such that docId is an array field,
|
|
45
47
|
|
|
46
48
|
Returns:
|
|
47
49
|
A list of dictionaries, where each dictionary represents a matching chunk
|
|
@@ -50,10 +52,23 @@ def search_within_document(
|
|
|
50
52
|
embeddings_collection = db_client[embeddings_collection_name]
|
|
51
53
|
|
|
52
54
|
print(f"Searching within document (docId: {document_name_id})...")
|
|
55
|
+
# print(f" filter (slug: {document_belongs_to_a_type})...")
|
|
53
56
|
|
|
54
57
|
# MongoDB Atlas Vector Search aggregation pipeline
|
|
55
58
|
# The 'path' should point to the field containing the embeddings.
|
|
56
59
|
# The 'filter' stage is crucial for searching within a specific document.
|
|
60
|
+
#
|
|
61
|
+
project_dict= {
|
|
62
|
+
'_id': 0,
|
|
63
|
+
'docId': 1,
|
|
64
|
+
'chunk_number': 1,
|
|
65
|
+
'chunk_text': 1,
|
|
66
|
+
'score': { '$meta': 'vectorSearchScore' } # Get the similarity score
|
|
67
|
+
}
|
|
68
|
+
|
|
69
|
+
if document_belongs_to_a_type:
|
|
70
|
+
project_dict['type']= 1
|
|
71
|
+
|
|
57
72
|
pipeline = [
|
|
58
73
|
{
|
|
59
74
|
'$vectorSearch': {
|
|
@@ -66,25 +81,21 @@ def search_within_document(
|
|
|
66
81
|
'index': index_name,
|
|
67
82
|
|
|
68
83
|
#filter to search only within the specified document
|
|
69
|
-
'filter':
|
|
70
|
-
|
|
71
|
-
|
|
84
|
+
'filter':
|
|
85
|
+
{ "type": {"$in": [document_belongs_to_a_type ]} } if document_belongs_to_a_type else
|
|
86
|
+
{ 'docId': document_name_id }
|
|
72
87
|
}
|
|
73
88
|
},
|
|
74
89
|
|
|
75
90
|
# to exclude the MongoDB internal _id
|
|
76
91
|
{
|
|
77
|
-
'$project':
|
|
78
|
-
'_id': 0,
|
|
79
|
-
'docId': 1,
|
|
80
|
-
'chunk_number': 1,
|
|
81
|
-
'chunk_text': 1,
|
|
82
|
-
'score': { '$meta': 'vectorSearchScore' } # Get the similarity score
|
|
83
|
-
}
|
|
92
|
+
'$project': project_dict
|
|
84
93
|
}
|
|
85
94
|
]
|
|
86
95
|
|
|
96
|
+
# print("sesraching now:")
|
|
87
97
|
results = list(embeddings_collection.aggregate(pipeline))
|
|
98
|
+
# print("search results: ", results)
|
|
88
99
|
|
|
89
100
|
if not results:
|
|
90
101
|
print(f"No relevant chunks found for document '{document_name_id}' with the given query.")
|
|
@@ -100,15 +111,18 @@ def search_within_document(
|
|
|
100
111
|
|
|
101
112
|
|
|
102
113
|
|
|
103
|
-
def process_document_and_embed(
|
|
114
|
+
def process_document_and_embed(
|
|
115
|
+
db_client,
|
|
104
116
|
llm_client,
|
|
105
117
|
inference_client,
|
|
106
118
|
file_path: Path,
|
|
107
119
|
chunk_size: int,
|
|
108
|
-
embedding_model: str = '
|
|
120
|
+
embedding_model: str = 'BAAI/bge-en-icl',
|
|
109
121
|
embeddings_collection_name= "doc_chunks",
|
|
110
122
|
use_custom_id: str | None = None,
|
|
111
|
-
use_custom_input: str | None = None
|
|
123
|
+
use_custom_input: str | None = None,
|
|
124
|
+
document_belongs_to_a_type= "",
|
|
125
|
+
type_info= []
|
|
112
126
|
) -> list[dict]:
|
|
113
127
|
"""
|
|
114
128
|
Processes an input document by chunking its text, generating embeddings using
|
|
@@ -228,13 +242,30 @@ def process_document_and_embed(db_client,
|
|
|
228
242
|
'chunk_text': chunk,
|
|
229
243
|
'embedding': embedding,
|
|
230
244
|
'chunk_id_global': chunk_id_global,
|
|
231
|
-
'chunk_id_doc_specific': chunk_id_doc_specific
|
|
245
|
+
'chunk_id_doc_specific': chunk_id_doc_specific,
|
|
232
246
|
}
|
|
233
|
-
|
|
234
|
-
|
|
235
|
-
|
|
236
|
-
|
|
237
|
-
)
|
|
247
|
+
|
|
248
|
+
|
|
249
|
+
# TBD: this is NOT pushing array, this is creating a "$push" field with type: "" object
|
|
250
|
+
|
|
251
|
+
if len(type_info) > 0:
|
|
252
|
+
embeddings_collection.update_one(
|
|
253
|
+
{'docId': document_name_id, 'chunk_number': i + 1},
|
|
254
|
+
{
|
|
255
|
+
'$set': doc_set,
|
|
256
|
+
'$push': {
|
|
257
|
+
"type": type_info
|
|
258
|
+
}
|
|
259
|
+
},
|
|
260
|
+
upsert=True
|
|
261
|
+
)
|
|
262
|
+
else:
|
|
263
|
+
|
|
264
|
+
embeddings_collection.update_one(
|
|
265
|
+
{'docId': document_name_id, 'chunk_number': i + 1},
|
|
266
|
+
{'$set': doc_set},
|
|
267
|
+
upsert=True
|
|
268
|
+
)
|
|
238
269
|
print(f"Successfully stored chunk {i+1} for '{file_path.name}' in MongoDB.")
|
|
239
270
|
res.append({**doc_set, "docId": document_name_id, "chunk_number": i + 1})
|
|
240
271
|
|
|
@@ -4,7 +4,7 @@ from pymongo.operations import SearchIndexModel
|
|
|
4
4
|
import time
|
|
5
5
|
import os
|
|
6
6
|
|
|
7
|
-
def create_vector_index(collection: Collection, index_name: str, num_dimensions: int = 768):
|
|
7
|
+
def create_vector_index(collection: Collection, index_name: str, num_dimensions: int = 768, document_belongs_to_a_type= ""):
|
|
8
8
|
"""
|
|
9
9
|
Creates a MongoDB Atlas Vector Search index if it does not already exist.
|
|
10
10
|
|
|
@@ -13,14 +13,14 @@ def create_vector_index(collection: Collection, index_name: str, num_dimensions:
|
|
|
13
13
|
index_name: The desired name for the vector search index.
|
|
14
14
|
num_dimensions: The number of dimensions for the embedding vectors.
|
|
15
15
|
"""
|
|
16
|
-
|
|
16
|
+
|
|
17
17
|
# 1. Check if the index already exists
|
|
18
18
|
existing_indexes = list(collection.list_search_indexes())
|
|
19
|
-
|
|
19
|
+
|
|
20
20
|
for index in existing_indexes:
|
|
21
21
|
if index.get('name') == index_name:
|
|
22
22
|
print(f"Search index '{index_name}' already exists. Skipping creation.")
|
|
23
|
-
|
|
23
|
+
|
|
24
24
|
# Optional: You can also check if the existing index is "READY"
|
|
25
25
|
if index.get('status') == 'READY':
|
|
26
26
|
print(f"Index '{index_name}' is already ready for querying.")
|
|
@@ -33,20 +33,27 @@ def create_vector_index(collection: Collection, index_name: str, num_dimensions:
|
|
|
33
33
|
# 2. If the index does not exist, proceed to create it
|
|
34
34
|
print(f"Search index '{index_name}' does not exist. Creating it now...")
|
|
35
35
|
|
|
36
|
+
fields_arr= [
|
|
37
|
+
{
|
|
38
|
+
"type": "vector",
|
|
39
|
+
"path": "embedding",
|
|
40
|
+
"similarity": "dotProduct", # Or "cosine", "euclidean"
|
|
41
|
+
"numDimensions": num_dimensions,
|
|
42
|
+
"quantization": "scalar" # Or "none"
|
|
43
|
+
},
|
|
44
|
+
{
|
|
45
|
+
"type": "filter",
|
|
46
|
+
"path": "docId"
|
|
47
|
+
}
|
|
48
|
+
]
|
|
49
|
+
|
|
50
|
+
if document_belongs_to_a_type:
|
|
51
|
+
fields_arr.append({
|
|
52
|
+
"type": "filter",
|
|
53
|
+
"path": "type"
|
|
54
|
+
})
|
|
36
55
|
search_index_model = SearchIndexModel(definition={
|
|
37
|
-
"fields":
|
|
38
|
-
{
|
|
39
|
-
"type": "vector",
|
|
40
|
-
"path": "embedding",
|
|
41
|
-
"similarity": "dotProduct", # Or "cosine", "euclidean"
|
|
42
|
-
"numDimensions": num_dimensions,
|
|
43
|
-
"quantization": "scalar" # Or "none"
|
|
44
|
-
},
|
|
45
|
-
{
|
|
46
|
-
"type": "filter",
|
|
47
|
-
"path": "docId"
|
|
48
|
-
}
|
|
49
|
-
]
|
|
56
|
+
"fields": fields_arr
|
|
50
57
|
},
|
|
51
58
|
name=index_name,
|
|
52
59
|
type="vectorSearch"
|
|
@@ -70,7 +77,7 @@ def _wait_for_index_ready(collection: Collection, index_name: str):
|
|
|
70
77
|
Helper function to poll the index status until it's ready.
|
|
71
78
|
"""
|
|
72
79
|
print("Polling to check if the index is ready. This may take some time (up to a few minutes for large indexes).")
|
|
73
|
-
|
|
80
|
+
|
|
74
81
|
start_time = time.time()
|
|
75
82
|
timeout = 300 # 5 minutes timeout, adjust as needed
|
|
76
83
|
|
|
@@ -89,7 +96,7 @@ def _wait_for_index_ready(collection: Collection, index_name: str):
|
|
|
89
96
|
print(f"Index '{index_name}' status: {current_status}. Waiting...")
|
|
90
97
|
except Exception as e:
|
|
91
98
|
print(f"Error while polling index status: {e}. Retrying...")
|
|
92
|
-
|
|
99
|
+
|
|
93
100
|
if time.time() - start_time > timeout:
|
|
94
101
|
status= indices[0].get('status') if indices else 'N/A'
|
|
95
102
|
print(f"Timeout: Index '{index_name}' did not become ready within {timeout} seconds. Current status: {status}")
|
|
@@ -99,9 +106,9 @@ def _wait_for_index_ready(collection: Collection, index_name: str):
|
|
|
99
106
|
|
|
100
107
|
# --- Example Usage ---
|
|
101
108
|
if __name__ == "__main__":
|
|
102
|
-
|
|
109
|
+
|
|
103
110
|
# Replace with your database and collection names
|
|
104
|
-
DATABASE_NAME = "pembot"
|
|
111
|
+
DATABASE_NAME = "pembot"
|
|
105
112
|
COLLECTION_NAME = "doc_chunks"
|
|
106
113
|
VECTOR_INDEX_NAME = "test_search"
|
|
107
114
|
|
|
@@ -119,7 +126,7 @@ if __name__ == "__main__":
|
|
|
119
126
|
|
|
120
127
|
# Call the function to create the index, with existence check
|
|
121
128
|
create_vector_index(collection, VECTOR_INDEX_NAME, num_dimensions=EMBEDDING_DIMENSIONS)
|
|
122
|
-
|
|
129
|
+
|
|
123
130
|
# Test calling it again to see the "already exists" message
|
|
124
131
|
create_vector_index(collection, VECTOR_INDEX_NAME, num_dimensions=EMBEDDING_DIMENSIONS)
|
|
125
132
|
|
|
@@ -129,5 +136,3 @@ if __name__ == "__main__":
|
|
|
129
136
|
if 'mongo_client' in locals() and mongo_client:
|
|
130
137
|
mongo_client.close()
|
|
131
138
|
print("MongoDB connection closed.")
|
|
132
|
-
|
|
133
|
-
|
pembot/__init__.py
CHANGED
pembot/config/config.yaml
CHANGED
pembot/query.py
CHANGED
|
@@ -68,7 +68,8 @@ def multi_embedding_average(llm_client, inference_client, descriptions, model= "
|
|
|
68
68
|
except Exception as e:
|
|
69
69
|
print(f"Error generating embedding for description '{desc}': {e}")
|
|
70
70
|
# Decide how to handle errors: skip, raise, or use a placeholder
|
|
71
|
-
continue
|
|
71
|
+
# continue
|
|
72
|
+
raise e
|
|
72
73
|
time.sleep(1)
|
|
73
74
|
|
|
74
75
|
if not description_embeddings:
|
|
@@ -81,7 +82,7 @@ def multi_embedding_average(llm_client, inference_client, descriptions, model= "
|
|
|
81
82
|
|
|
82
83
|
|
|
83
84
|
|
|
84
|
-
def rag_query_llm(db_client, llm_client, inference_client, user_query: str, document_id: str, required_fields_descriptions: list[str], model_name: str = "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B", ollama_base_url: str = "http://localhost:11434", no_of_fields= 4, embedding_model= "BAAI/bge-en-icl", llm_provider_name: PROVIDER_T= "novita", index_name: str= "test_search", embeddings_collection= "doc_chunks"):
|
|
85
|
+
def rag_query_llm(db_client, llm_client, inference_client, user_query: str, document_id: str, required_fields_descriptions: list[str], model_name: str = "deepseek-ai/DeepSeek-R1-0528-Qwen3-8B", ollama_base_url: str = "http://localhost:11434", no_of_fields= 4, embedding_model= "BAAI/bge-en-icl", llm_provider_name: PROVIDER_T= "novita", index_name: str= "test_search", embeddings_collection= "doc_chunks", document_belongs_to_a_type= ""):
|
|
85
86
|
"""
|
|
86
87
|
Performs a RAG (Retrieval Augmented Generation) query using a Hugging Face
|
|
87
88
|
embedding model, ChromaDB for retrieval, and a local Ollama model for generation.
|
|
@@ -119,10 +120,10 @@ def rag_query_llm(db_client, llm_client, inference_client, user_query: str, docu
|
|
|
119
120
|
aggregate_query_embedding= multi_embedding_average(llm_client, inference_client, required_fields_descriptions, model= embedding_model, embed_locally= embed_locally)
|
|
120
121
|
print("Aggregate query embedding generated. length: ", len(aggregate_query_embedding))
|
|
121
122
|
|
|
122
|
-
create_vector_index(db_client[embeddings_collection], index_name, num_dimensions= len(aggregate_query_embedding))
|
|
123
|
+
create_vector_index(db_client[embeddings_collection], index_name, num_dimensions= len(aggregate_query_embedding), document_belongs_to_a_type= document_belongs_to_a_type)
|
|
123
124
|
|
|
124
125
|
# check the order of args
|
|
125
|
-
relevant_chunks= search_within_document(db_client, aggregate_query_embedding, document_id, limit= no_of_fields, index_name= index_name, embeddings_collection_name= embeddings_collection)
|
|
126
|
+
relevant_chunks= search_within_document(db_client, aggregate_query_embedding, document_id, limit= no_of_fields, index_name= index_name, embeddings_collection_name= embeddings_collection, document_belongs_to_a_type= document_belongs_to_a_type)
|
|
126
127
|
relevant_chunks= list(map(lambda x: x['chunk_text'], relevant_chunks))
|
|
127
128
|
|
|
128
129
|
if not relevant_chunks:
|
|
@@ -1,11 +1,11 @@
|
|
|
1
1
|
pembot/.gitignore,sha256=_7FTsZokJ_pzEyyPjOsGw5x5Xx3gUBFaafs7UlPsv9E,98
|
|
2
2
|
pembot/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
3
|
-
pembot/__init__.py,sha256=
|
|
3
|
+
pembot/__init__.py,sha256=XAG3pbUQGlrUZZgAKAxq96MjvCGaEPkpGTSKIwzNvGY,211
|
|
4
4
|
pembot/gartner.py,sha256=3ALknQ5mSXIimmwCa3JFDzB_EW2hHEcQO1T2odyBquk,5408
|
|
5
5
|
pembot/main.py,sha256=lZLIV8XPonvNoY4LVS-5fct1y9URMXWoSGJUKMw3Yg8,9667
|
|
6
6
|
pembot/output_structure_local.py,sha256=YfpHzfTNeLMSsB_CjAamha9D6Iz7E1IC-tW9xPCMWFc,3000
|
|
7
7
|
pembot/pem.py,sha256=mv6iGcN1peSY7z2dtCQ_BKj31EFBNfczBhps_d-0XDo,6377
|
|
8
|
-
pembot/query.py,sha256=
|
|
8
|
+
pembot/query.py,sha256=d6K2PyDDGoIOqwn7A_KIBr83w0zjMAHjhmx1S9VlVgg,8642
|
|
9
9
|
pembot/requirements.txt,sha256=6OV_n5JVco2lLA8Wq38tJX1bYgo_UU0R9RKgs4d2wfc,1360
|
|
10
10
|
pembot/.git/COMMIT_EDITMSG,sha256=H9feTx6U3VWbFycy9cq077mD4oxuv2gz4G3EUOdQmV4,30
|
|
11
11
|
pembot/.git/HEAD,sha256=KNJb-Cr0wOK3L1CVmyvrhZ4-YLljCl6MYD2tTdsrboA,21
|
|
@@ -108,10 +108,10 @@ pembot/AnyToText/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,
|
|
|
108
108
|
pembot/AnyToText/convertor.py,sha256=gqvhwFssUsAeirfO4n0Ztwga1hn8zHbdG96sMTjYrpE,17188
|
|
109
109
|
pembot/TextEmbedder/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
110
110
|
pembot/TextEmbedder/gemini_embedder.py,sha256=P679-2mmQESlYKML1vcrwx_-CSgWJgIQk7NL4F7BLQE,677
|
|
111
|
-
pembot/TextEmbedder/mongodb_embedder.py,sha256=
|
|
112
|
-
pembot/TextEmbedder/mongodb_index_creator.py,sha256=
|
|
111
|
+
pembot/TextEmbedder/mongodb_embedder.py,sha256=RotNlerS3WKEUGRNeQM5MTkl5BtaWNHVaXO1gN5NicI,10682
|
|
112
|
+
pembot/TextEmbedder/mongodb_index_creator.py,sha256=kopqdVYJii_wExVrXGZjMfqWZ2dD42b3PeNWo71weHI,5354
|
|
113
113
|
pembot/TextEmbedder/vector_query.py,sha256=Kh1uhx9CatB-oQlQtnW-1I2Qz7MGHI20n2h_8peAChM,1986
|
|
114
|
-
pembot/config/config.yaml,sha256=
|
|
114
|
+
pembot/config/config.yaml,sha256=y-2BklPelldaXJ_hxFD9k-bFpDA6OAZkaoh5XlvASCE,156
|
|
115
115
|
pembot/pdf2markdown/LICENSE,sha256=1JTJhQjUYDqJzFJhNtitm7mHyE71PRHgetIqRRWg6Pk,1068
|
|
116
116
|
pembot/pdf2markdown/README.md,sha256=jitM1pwI69oa0N4mXv5-SY1ka9Sz3jsRNCDdpW-50kY,4545
|
|
117
117
|
pembot/pdf2markdown/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
@@ -167,7 +167,7 @@ pembot/pdf2markdown/config/config.yaml,sha256=w75W2Eg4-tu8rRk_23PqxWDh0010kRKLmP
|
|
|
167
167
|
pembot/utils/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
|
|
168
168
|
pembot/utils/inference_client.py,sha256=jeURmY2P5heVlH1dCV0XSgiX3U2qYGEmrnUv0KFpdww,5380
|
|
169
169
|
pembot/utils/string_tools.py,sha256=gtRa5rBR0Q7GspTu2WtCnvhJQLFjPfWLvhmyiPkyStU,1883
|
|
170
|
-
pembot-0.0.
|
|
171
|
-
pembot-0.0.
|
|
172
|
-
pembot-0.0.
|
|
173
|
-
pembot-0.0.
|
|
170
|
+
pembot-0.0.8.dist-info/licenses/LICENSE,sha256=OXLcl0T2SZ8Pmy2_dmlvKuetivmyPd5m1q-Gyd-zaYY,35149
|
|
171
|
+
pembot-0.0.8.dist-info/WHEEL,sha256=Dyt6SBfaasWElUrURkknVFAZDHSTwxg3PaTza7RSbkY,100
|
|
172
|
+
pembot-0.0.8.dist-info/METADATA,sha256=kfa20bL5qROy6a8bsALEzDRlmF-JnTgmR7Qc8rz6PNQ,313
|
|
173
|
+
pembot-0.0.8.dist-info/RECORD,,
|
|
File without changes
|
|
File without changes
|