Skip to content

Dev #1132

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 105 commits into from
Mar 6, 2025
Merged

Dev #1132

Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
105 commits
Select commit Hold shift + click to select a range
26581db
Read only mode for unauthenticated users (#1046)
kartikpersistent Jan 30, 2025
449552d
langchain updates (#1048)
prakriti-solankey Jan 30, 2025
2d86c5c
testing script changed for better logging errors and results of vario…
kaustubh-darekar Jan 30, 2025
578efad
Deepseek models integration (#1051)
kaustubh-darekar Jan 30, 2025
339488a
fixed top-line of drop-area (#1049)
kartikpersistent Jan 30, 2025
1dbd902
Schema viz (#1035)
prakriti-solankey Jan 30, 2025
6f3f863
updated to new ndl minor version and fixed sources modal display for …
kartikpersistent Feb 3, 2025
38eb72e
Chunk size overlap config (#1059)
prakriti-solankey Feb 7, 2025
6e60361
fix-load-existing-schema (#1061)
dhiaaeddine16 Feb 10, 2025
228ab9b
added bug report feature request and format fixes
kartikpersistent Feb 11, 2025
fbd9d3e
configured dependenabot for python
kartikpersistent Feb 11, 2025
6893a26
configuration fix
kartikpersistent Feb 11, 2025
4b831df
Fixed the logging time issue
praveshkumar1988 Feb 11, 2025
d53ba43
Backend connection config (#1060)
prakriti-solankey Feb 11, 2025
71e013e
Unable to get the status of document node resolved due to leading spa…
kaustubh-darekar Feb 11, 2025
d9a89f8
updated dependency
kartikpersistent Feb 11, 2025
2c8fe2a
Merge branch 'dev' of https://github.com/neo4j-labs/llm-graph-builder…
kartikpersistent Feb 11, 2025
192a1bc
always show schema button
prakriti-solankey Feb 11, 2025
e8e576f
always show schema button
prakriti-solankey Feb 11, 2025
ed69115
uri
prakriti-solankey Feb 11, 2025
3624feb
Update README.md
kartikpersistent Feb 11, 2025
738eecc
Update README.md
kartikpersistent Feb 11, 2025
69a1003
Update README.md
kartikpersistent Feb 12, 2025
4c48124
Update README.md
kartikpersistent Feb 12, 2025
bd917ae
Fixed the create community issue for backend connection configuration
praveshkumar1988 Feb 12, 2025
8b8368b
removal of unused code
prakriti-solankey Feb 12, 2025
5591741
Support added for gpt 3o mini & gemini flash 2.0 in dev (#1069)
kaustubh-darekar Feb 12, 2025
6762367
Cancelling the API's on Unmounting phase (#1068)
kartikpersistent Feb 13, 2025
7f075d5
Merge branch 'staging' into dev
prakriti-solankey Feb 13, 2025
6a6c82c
removed unused neo4j-driver
kartikpersistent Feb 13, 2025
f198ca5
added auth0 in the frame src
kartikpersistent Feb 13, 2025
a72f3cf
message change
prakriti-solankey Feb 14, 2025
43d3bed
Update docker-compose.yml
kartikpersistent Feb 16, 2025
7bd5dd3
Bump tailwindcss from 3.4.9 to 4.0.6 in /frontend (#1091)
dependabot[bot] Feb 17, 2025
c198260
message check
prakriti-solankey Feb 17, 2025
a0cd597
V0.7.1 documentation updates (#1094)
kartikpersistent Feb 17, 2025
1cc723c
Merge branch 'staging' into dev
kartikpersistent Feb 17, 2025
db20e29
Merge branch 'staging' into dev
prakriti-solankey Feb 17, 2025
896bdee
Bump react-dropzone from 14.2.3 to 14.3.5 in /frontend (#1084)
dependabot[bot] Feb 18, 2025
8938cd1
Bump @typescript-eslint/eslint-plugin from 6.21.0 to 7.0.0 in /fronte…
dependabot[bot] Feb 18, 2025
4ea5305
Bump eslint-plugin-react-hooks from 4.6.2 to 5.1.0 in /frontend (#1082)
dependabot[bot] Feb 18, 2025
74e8bdc
Bump typescript from 5.5.4 to 5.7.3 in /frontend (#1081)
dependabot[bot] Feb 18, 2025
5444e6b
fix-additional-instructions (#1089)
dhiaaeddine16 Feb 18, 2025
041837d
V0.7.1 minor fixes (#1097)
praveshkumar1988 Feb 19, 2025
e81655d
remove try except from llm.py
praveshkumar1988 Feb 19, 2025
78f1015
Remove example.env from main folder (#1099)
praveshkumar1988 Feb 19, 2025
fcb6bcc
moved to taulwind 3
kartikpersistent Feb 20, 2025
5c2029c
tailwind 4 migration
kartikpersistent Feb 20, 2025
36f2548
format fixes
kartikpersistent Feb 20, 2025
7e7a2c3
Source list api convert to post (#1102)
kartikpersistent Feb 20, 2025
7186ac4
Merge branch 'staging' into dev
prakriti-solankey Feb 20, 2025
7f8b8c9
height issue
prakriti-solankey Feb 20, 2025
cf92222
fix: Profile CSS Fix
kartikpersistent Feb 20, 2025
2274706
fix: display flex issue fix
kartikpersistent Feb 20, 2025
28780c5
Merge branch 'staging' into dev
prakriti-solankey Feb 21, 2025
c1b7b4d
Update dependabot.yml (#1122)
kaustubh-darekar Feb 24, 2025
5ca76aa
added automated linting and formatting through husky hooks
kartikpersistent Feb 24, 2025
0cf3f32
renamed the files
kartikpersistent Feb 24, 2025
2c9d1d6
husky setup fix
kartikpersistent Feb 24, 2025
381dc16
added permission
kartikpersistent Feb 24, 2025
97f0fd2
test commiy
kartikpersistent Feb 24, 2025
e4f1e91
type checking through husky hooks
kartikpersistent Feb 24, 2025
cc158d1
something bad code
kartikpersistent Feb 24, 2025
b88c7df
some bad code
kartikpersistent Feb 24, 2025
17ff72c
some bad code
kartikpersistent Feb 24, 2025
2f3f164
testing pre-commit code
kartikpersistent Feb 24, 2025
36c9c53
testing pre-commit code
kartikpersistent Feb 24, 2025
2bb53b1
lint setup on staged commits
kartikpersistent Feb 24, 2025
53da28c
test commt
kartikpersistent Feb 24, 2025
06d9b4f
test commit with errors
kartikpersistent Feb 24, 2025
2d084da
fix
kartikpersistent Feb 24, 2025
0b46eba
added pypandoc-binary package for OSError: No pandoc was found during…
kaustubh-darekar Feb 24, 2025
269d76b
added document plus icon
kartikpersistent Feb 25, 2025
f79feb8
Bump axios from 1.7.3 to 1.7.9 in /frontend (#1113)
dependabot[bot] Feb 25, 2025
ddb5852
Bump eslint-plugin-react-refresh from 0.4.9 to 0.4.19 in /frontend (#…
dependabot[bot] Feb 25, 2025
fcae55a
Bump postcss from 8.4.41 to 8.5.3 in /frontend (#1114)
dependabot[bot] Feb 25, 2025
69db442
Bump react-icons from 5.2.1 to 5.5.0 in /frontend (#1115)
dependabot[bot] Feb 25, 2025
b761f23
different url web page having same title issue fixed (#1110)
kaustubh-darekar Feb 25, 2025
3a222cc
Text file encoding issue (#1126)
kaustubh-darekar Feb 25, 2025
ede3095
Resolved UnicodeDecodeError issue for files having other than utf-8 e…
kaustubh-darekar Feb 26, 2025
370ab9e
Sanitizing additional instruction (#1130)
kaustubh-darekar Feb 26, 2025
ebbabd3
resolved UnboundLocalError: local variable 'graphDb_data_Access' refe…
kaustubh-darekar Feb 26, 2025
aca4f81
connection not there message for data resources (#1131)
prakriti-solankey Feb 26, 2025
455269b
dockerfile updates and utils functions change
prakriti-solankey Feb 26, 2025
e81aa00
fix: readonly issue fix
kartikpersistent Feb 27, 2025
a1ed635
Resolved uploaded file extraction failing on deployed version (#1136)
kaustubh-darekar Feb 27, 2025
cf11494
UI fixes v0.7.2 (#1138)
kartikpersistent Mar 3, 2025
979434d
Update BreakDownPopOver.tsx
kartikpersistent Mar 3, 2025
41b0370
chunk_count_val
prakriti-solankey Mar 3, 2025
bfe127f
type error
prakriti-solankey Mar 3, 2025
93ff881
spell fixes and protected route fixes
kartikpersistent Mar 3, 2025
a1a998e
top entities not found - bug resolved (#1150)
kaustubh-darekar Mar 4, 2025
506dfb0
limiting content fetching to current wikipedia page (#1151)
kaustubh-darekar Mar 4, 2025
3c8d669
added the link for login redirectig
kartikpersistent Mar 4, 2025
a8fb41a
removed loading statw
kartikpersistent Mar 5, 2025
de69dbd
added the padding and changed the message
kartikpersistent Mar 5, 2025
1ee0112
Bump re-resizable from 6.9.17 to 6.11.2 in /frontend (#1149)
dependabot[bot] Mar 5, 2025
e26a2e2
Bump eslint-plugin-react from 7.35.0 to 7.37.4 in /frontend (#1148)
dependabot[bot] Mar 5, 2025
2fe68f7
Bump @types/node from 20.14.14 to 22.13.9 in /frontend (#1152)
dependabot[bot] Mar 5, 2025
6f1e96d
Bump eslint-config-prettier from 8.10.0 to 10.0.2 in /frontend (#1146)
dependabot[bot] Mar 5, 2025
9e427c8
Bump react-dropzone from 14.3.5 to 14.3.8 in /frontend (#1145)
dependabot[bot] Mar 5, 2025
0090ae1
Update dependabot.yml
kartikpersistent Mar 5, 2025
738bc5b
Update the query to check DB is gds version (#1153)
praveshkumar1988 Mar 6, 2025
36c3fa4
Entity details shown for entity mode (#1154)
kaustubh-darekar Mar 6, 2025
dc0b83c
Merge branch 'staging' into dev
kartikpersistent Mar 6, 2025
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions .github/dependabot.yml
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,11 @@ updates:
- package-ecosystem: 'npm'
directory: '/frontend'
schedule:
interval: 'weekly'
interval: 'monthly'
target-branch: 'dev'

- package-ecosystem: 'pip'
directory: '/backend'
schedule:
interval: 'weekly'
target-branch: 'dev'
interval: 'monthly'
target-branch: 'dev'
2 changes: 2 additions & 0 deletions backend/requirements.txt
Original file line number Diff line number Diff line change
Expand Up @@ -59,3 +59,5 @@ Secweb==1.11.0
ragas==0.2.11
rouge_score==0.1.2
langchain-neo4j==0.3.0
pypandoc-binary==1.15
chardet==5.2.0
34 changes: 31 additions & 3 deletions backend/score.py
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,27 @@
CHUNK_DIR = os.path.join(os.path.dirname(__file__), "chunks")
MERGED_DIR = os.path.join(os.path.dirname(__file__), "merged_files")

def sanitize_filename(filename):
"""
Sanitize the user-provided filename to prevent directory traversal and remove unsafe characters.
"""
# Remove path separators and collapse redundant separators
filename = os.path.basename(filename)
filename = os.path.normpath(filename)
return filename

def validate_file_path(directory, filename):
"""
Construct the full file path and ensure it is within the specified directory.
"""
file_path = os.path.join(directory, filename)
abs_directory = os.path.abspath(directory)
abs_file_path = os.path.abspath(file_path)
# Ensure the file path starts with the intended directory path
if not abs_file_path.startswith(abs_directory):
raise ValueError("Invalid file path")
return abs_file_path

def healthy_condition():
output = {"healthy": True}
return output
Expand Down Expand Up @@ -217,8 +238,9 @@ async def extract_knowledge_graph_from_file(
start_time = time.time()
graph = create_graph_database_connection(uri, userName, password, database)
graphDb_data_Access = graphDBdataAccess(graph)
merged_file_path = os.path.join(MERGED_DIR,file_name)
if source_type == 'local file':
file_name = sanitize_filename(file_name)
merged_file_path = validate_file_path(MERGED_DIR, file_name)
uri_latency, result = await extract_graph_from_file_local_file(uri, userName, password, database, model, merged_file_path, file_name, allowedNodes, allowedRelationship, token_chunk_size, chunk_overlap, chunks_to_combine, retry_condition, additional_instructions)

elif source_type == 's3 bucket' and source_url:
Expand Down Expand Up @@ -278,8 +300,11 @@ async def extract_knowledge_graph_from_file(
return create_api_response('Success', data=result, file_source= source_type)
except LLMGraphBuilderException as e:
error_message = str(e)
graph = create_graph_database_connection(uri, userName, password, database)
graphDb_data_Access = graphDBdataAccess(graph)
graphDb_data_Access.update_exception_db(file_name,error_message, retry_condition)
failed_file_process(uri,file_name, merged_file_path, source_type)
if source_type == 'local file':
failed_file_process(uri,file_name, merged_file_path)
node_detail = graphDb_data_Access.get_current_status_document_node(file_name)
# Set the status "Completed" in logging becuase we are treating these error already handled by application as like custom errors.
json_obj = {'api_name':'extract','message':error_message,'file_created_at':formatted_time(node_detail[0]['created_time']),'error_message':error_message, 'file_name': file_name,'status':'Completed',
Expand All @@ -290,8 +315,11 @@ async def extract_knowledge_graph_from_file(
except Exception as e:
message=f"Failed To Process File:{file_name} or LLM Unable To Parse Content "
error_message = str(e)
graph = create_graph_database_connection(uri, userName, password, database)
graphDb_data_Access = graphDBdataAccess(graph)
graphDb_data_Access.update_exception_db(file_name,error_message, retry_condition)
failed_file_process(uri,file_name, merged_file_path, source_type)
if source_type == 'local file':
failed_file_process(uri,file_name, merged_file_path)
node_detail = graphDb_data_Access.get_current_status_document_node(file_name)

json_obj = {'api_name':'extract','message':message,'file_created_at':formatted_time(node_detail[0]['created_time']),'error_message':error_message, 'file_name': file_name,'status':'Failed',
Expand Down
21 changes: 16 additions & 5 deletions backend/src/QA_integration.py
Original file line number Diff line number Diff line change
Expand Up @@ -177,7 +177,7 @@ def get_rag_chain(llm, system_template=CHAT_SYSTEM_TEMPLATE):
logging.error(f"Error creating RAG chain: {e}")
raise

def format_documents(documents, model):
def format_documents(documents, model,chat_mode_settings):
prompt_token_cutoff = 4
for model_names, value in CHAT_TOKEN_CUT_OFF.items():
if model in model_names:
Expand All @@ -197,9 +197,20 @@ def format_documents(documents, model):
try:
source = doc.metadata.get('source', "unknown")
sources.add(source)

entities = doc.metadata['entities'] if 'entities'in doc.metadata.keys() else entities
global_communities = doc.metadata["communitydetails"] if 'communitydetails'in doc.metadata.keys() else global_communities
if 'entities' in doc.metadata:
if chat_mode_settings["mode"] == CHAT_ENTITY_VECTOR_MODE:
entity_ids = [entry['entityids'] for entry in doc.metadata['entities'] if 'entityids' in entry]
entities.setdefault('entityids', set()).update(entity_ids)
else:
if 'entityids' in doc.metadata['entities']:
entities.setdefault('entityids', set()).update(doc.metadata['entities']['entityids'])
if 'relationshipids' in doc.metadata['entities']:
entities.setdefault('relationshipids', set()).update(doc.metadata['entities']['relationshipids'])

if 'communitydetails' in doc.metadata:
existing_ids = {entry['id'] for entry in global_communities}
new_entries = [entry for entry in doc.metadata["communitydetails"] if entry['id'] not in existing_ids]
global_communities.extend(new_entries)

formatted_doc = (
"Document start\n"
Expand All @@ -218,7 +229,7 @@ def process_documents(docs, question, messages, llm, model,chat_mode_settings):
start_time = time.time()

try:
formatted_docs, sources, entitydetails, communities = format_documents(docs, model)
formatted_docs, sources, entitydetails, communities = format_documents(docs, model,chat_mode_settings)

rag_chain = get_rag_chain(llm=llm)

Expand Down
2 changes: 1 addition & 1 deletion backend/src/chunkid_entities.py
Original file line number Diff line number Diff line change
Expand Up @@ -190,7 +190,7 @@ def get_entities_from_chunkids(uri, username, password, database ,nodedetails,en
elif mode == CHAT_ENTITY_VECTOR_MODE:

if "entitydetails" in nodedetails and nodedetails["entitydetails"]:
entity_ids = [item["id"] for item in nodedetails["entitydetails"]]
entity_ids = [item for item in nodedetails["entitydetails"]["entityids"]]
logging.info(f"chunkid_entities module: Starting for entity ids: {entity_ids}")
result = process_entityids(driver, entity_ids)
if "chunk_data" in result.keys():
Expand Down
10 changes: 5 additions & 5 deletions backend/src/document_sources/gcs_bucket.py
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
import os
import logging
from google.cloud import storage
from langchain_community.document_loaders import GCSFileLoader, GCSDirectoryLoader
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.document_loaders import GCSFileLoader
from langchain_core.documents import Document
from PyPDF2 import PdfReader
import io
Expand Down Expand Up @@ -42,8 +41,9 @@ def get_gcs_bucket_files_info(gcs_project_id, gcs_bucket_name, gcs_bucket_folder
logging.exception(f'Exception Stack trace: {error_message}')
raise LLMGraphBuilderException(error_message)

def load_pdf(file_path):
return PyMuPDFLoader(file_path)
def gcs_loader_func(file_path):
loader, _ = load_document_content(file_path)
return loader

def get_documents_from_gcs(gcs_project_id, gcs_bucket_name, gcs_bucket_folder, gcs_blob_filename, access_token=None):
nltk.download('punkt')
Expand All @@ -64,7 +64,7 @@ def get_documents_from_gcs(gcs_project_id, gcs_bucket_name, gcs_bucket_folder, g
blob = bucket.blob(blob_name)

if blob.exists():
loader = GCSFileLoader(project_name=gcs_project_id, bucket=gcs_bucket_name, blob=blob_name, loader_func=load_document_content)
loader = GCSFileLoader(project_name=gcs_project_id, bucket=gcs_bucket_name, blob=blob_name, loader_func=gcs_loader_func)
pages = loader.load()
else :
raise LLMGraphBuilderException('File does not exist, Please re-upload the file and try again.')
Expand Down
63 changes: 47 additions & 16 deletions backend/src/document_sources/local_file.py
Original file line number Diff line number Diff line change
Expand Up @@ -3,30 +3,61 @@
from langchain_community.document_loaders import PyMuPDFLoader
from langchain_community.document_loaders import UnstructuredFileLoader
from langchain_core.documents import Document
import chardet
from langchain_core.document_loaders import BaseLoader

class ListLoader(BaseLoader):
"""A wrapper to make a list of Documents compatible with BaseLoader."""
def __init__(self, documents):
self.documents = documents
def load(self):
return self.documents

def detect_encoding(file_path):
"""Detects the file encoding to avoid UnicodeDecodeError."""
with open(file_path, 'rb') as f:
raw_data = f.read(4096)
result = chardet.detect(raw_data)
return result['encoding'] or "utf-8"

def load_document_content(file_path):
if Path(file_path).suffix.lower() == '.pdf':
return PyMuPDFLoader(file_path)
file_extension = Path(file_path).suffix.lower()
encoding_flag = False
if file_extension == '.pdf':
loader = PyMuPDFLoader(file_path)
return loader,encoding_flag
elif file_extension == ".txt":
encoding = detect_encoding(file_path)
logging.info(f"Detected encoding for {file_path}: {encoding}")
if encoding.lower() == "utf-8":
loader = UnstructuredFileLoader(file_path, mode="elements",autodetect_encoding=True)
return loader,encoding_flag
else:
with open(file_path, encoding=encoding, errors="replace") as f:
content = f.read()
loader = ListLoader([Document(page_content=content, metadata={"source": file_path})])
encoding_flag = True
return loader,encoding_flag
else:
return UnstructuredFileLoader(file_path, mode="elements",autodetect_encoding=True)
loader = UnstructuredFileLoader(file_path, mode="elements",autodetect_encoding=True)
return loader,encoding_flag

def get_documents_from_file_by_path(file_path,file_name):
file_path = Path(file_path)
if file_path.exists():
logging.info(f'file {file_name} processing')
file_extension = file_path.suffix.lower()
try:
loader = load_document_content(file_path)
if file_extension == ".pdf":
pages = loader.load()
else:
unstructured_pages = loader.load()
pages= get_pages_with_page_numbers(unstructured_pages)
except Exception as e:
raise Exception('Error while reading the file content or metadata')
else:
if not file_path.exists():
logging.info(f'File {file_name} does not exist')
raise Exception(f'File {file_name} does not exist')
logging.info(f'file {file_name} processing')
try:
loader, encoding_flag = load_document_content(file_path)
file_extension = file_path.suffix.lower()
if file_extension == ".pdf" or (file_extension == ".txt" and encoding_flag):
pages = loader.load()
else:
unstructured_pages = loader.load()
pages = get_pages_with_page_numbers(unstructured_pages)
except Exception as e:
raise Exception(f'Error while reading the file content or metadata, {e}')
return file_name, pages , file_extension

def get_pages_with_page_numbers(unstructured_pages):
Expand Down
8 changes: 1 addition & 7 deletions backend/src/document_sources/web_pages.py
Original file line number Diff line number Diff line change
Expand Up @@ -5,12 +5,6 @@
def get_documents_from_web_page(source_url:str):
try:
pages = WebBaseLoader(source_url, verify_ssl=False).load()
try:
file_name = pages[0].metadata['title'].strip()
if not file_name:
file_name = last_url_segment(source_url)
except:
file_name = last_url_segment(source_url)
return file_name, pages
return pages
except Exception as e:
raise LLMGraphBuilderException(str(e))
2 changes: 1 addition & 1 deletion backend/src/document_sources/wikipedia.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

def get_documents_from_Wikipedia(wiki_query:str, language:str):
try:
pages = WikipediaLoader(query=wiki_query.strip(), lang=language, load_all_available_meta=False).load()
pages = WikipediaLoader(query=wiki_query.strip(), lang=language, load_all_available_meta=False,doc_content_chars_max=100000,load_max_docs=1).load()
file_name = wiki_query.strip()
logging.info(f"Total Pages from Wikipedia = {len(pages)}")
return file_name, pages
Expand Down
14 changes: 10 additions & 4 deletions backend/src/graphDB_dataAccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -198,10 +198,7 @@ def check_account_access(self, database):
def check_gds_version(self):
try:
gds_procedure_count = """
SHOW PROCEDURES
YIELD name
WHERE name STARTS WITH "gds."
RETURN COUNT(*) AS totalGdsProcedures
SHOW FUNCTIONS YIELD name WHERE name STARTS WITH 'gds.version' RETURN COUNT(*) AS totalGdsProcedures
"""
result = self.graph.query(gds_procedure_count)
total_gds_procedures = result[0]['totalGdsProcedures'] if result else 0
Expand Down Expand Up @@ -564,3 +561,12 @@ def get_nodelabels_relationships(self):
except Exception as e:
print(f"Error in getting node labels/relationship types from db: {e}")
return []

def get_websource_url(self,file_name):
logging.info("Checking if same title with different URL exist in db ")
query = """
MATCH(d:Document {fileName : $file_name}) WHERE d.fileSource = "web-url"
RETURN d.url AS url
"""
param = {"file_name" : file_name}
return self.execute_query(query, param)
28 changes: 26 additions & 2 deletions backend/src/llm.py
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import boto3
import google.auth
from src.shared.constants import ADDITIONAL_INSTRUCTIONS
import re

def get_llm(model: str):
"""Retrieve the specified language model based on the model name."""
Expand Down Expand Up @@ -166,7 +167,8 @@ def get_chunk_id_as_doc_metadata(chunkId_chunkDoc_list):
async def get_graph_document_list(
llm, combined_chunk_document_list, allowedNodes, allowedRelationship, additional_instructions=None
):
futures = []
if additional_instructions:
additional_instructions = sanitize_additional_instruction(additional_instructions)
graph_document_list = []
if "diffbot_api_key" in dir(llm):
llm_transformer = llm
Expand Down Expand Up @@ -210,4 +212,26 @@ async def get_graph_from_llm(model, chunkId_chunkDoc_list, allowedNodes, allowed
graph_document_list = await get_graph_document_list(
llm, combined_chunk_document_list, allowedNodes, allowedRelationship, additional_instructions
)
return graph_document_list
return graph_document_list

def sanitize_additional_instruction(instruction: str) -> str:
"""
Sanitizes additional instruction by:
- Replacing curly braces `{}` with `[]` to prevent variable interpretation.
- Removing potential injection patterns like `os.getenv()`, `eval()`, `exec()`.
- Stripping problematic special characters.
- Normalizing whitespace.
Args:
instruction (str): Raw additional instruction input.
Returns:
str: Sanitized instruction safe for LLM processing.
"""
logging.info("Sanitizing additional instructions")
instruction = instruction.replace("{", "[").replace("}", "]") # Convert `{}` to `[]` for safety
# Step 2: Block dangerous function calls
injection_patterns = [r"os\.getenv\(", r"eval\(", r"exec\(", r"subprocess\.", r"import os", r"import subprocess"]
for pattern in injection_patterns:
instruction = re.sub(pattern, "[BLOCKED]", instruction, flags=re.IGNORECASE)
# Step 4: Normalize spaces
instruction = re.sub(r'\s+', ' ', instruction).strip()
return instruction
28 changes: 16 additions & 12 deletions backend/src/main.py
Original file line number Diff line number Diff line change
Expand Up @@ -124,7 +124,12 @@ def create_source_node_graph_web_url(graph, model, source_url, source_type):
raise LLMGraphBuilderException(message)
try:
title = pages[0].metadata['title'].strip()
if not title:
if title:
graphDb_data_Access = graphDBdataAccess(graph)
existing_url = graphDb_data_Access.get_websource_url(title)
if existing_url != source_url:
title = str(title) + "-" + str(last_url_segment(source_url)).strip()
else:
title = last_url_segment(source_url)
language = pages[0].metadata['language']
except:
Expand Down Expand Up @@ -253,7 +258,7 @@ async def extract_graph_from_file_s3(uri, userName, password, database, model, s

async def extract_graph_from_web_page(uri, userName, password, database, model, source_url, file_name, allowedNodes, allowedRelationship, token_chunk_size, chunk_overlap, chunks_to_combine, retry_condition, additional_instructions):
if not retry_condition:
file_name, pages = get_documents_from_web_page(source_url)
pages = get_documents_from_web_page(source_url)
if pages==None or len(pages)==0:
raise LLMGraphBuilderException(f'Content is not available for given URL : {file_name}')
return await processing_source(uri, userName, password, database, model, file_name, pages, allowedNodes, allowedRelationship, token_chunk_size, chunk_overlap, chunks_to_combine, additional_instructions=additional_instructions)
Expand Down Expand Up @@ -742,14 +747,13 @@ def set_status_retry(graph, file_name, retry_condition):
logging.info(obj_source_node)
graphDb_data_Access.update_source_node(obj_source_node)

def failed_file_process(uri,file_name, merged_file_path, source_type):
def failed_file_process(uri,file_name, merged_file_path):
gcs_file_cache = os.environ.get('GCS_FILE_CACHE')
if source_type == 'local file':
if gcs_file_cache == 'True':
folder_name = create_gcs_bucket_folder_name_hashed(uri,file_name)
copy_failed_file(BUCKET_UPLOAD, BUCKET_FAILED_FILE, folder_name, file_name)
time.sleep(5)
delete_file_from_gcs(BUCKET_UPLOAD,folder_name,file_name)
else:
logging.info(f'Deleted File Path: {merged_file_path} and Deleted File Name : {file_name}')
delete_uploaded_local_file(merged_file_path,file_name)
if gcs_file_cache == 'True':
folder_name = create_gcs_bucket_folder_name_hashed(uri,file_name)
copy_failed_file(BUCKET_UPLOAD, BUCKET_FAILED_FILE, folder_name, file_name)
time.sleep(5)
delete_file_from_gcs(BUCKET_UPLOAD,folder_name,file_name)
else:
logging.info(f'Deleted File Path: {merged_file_path} and Deleted File Name : {file_name}')
delete_uploaded_local_file(merged_file_path,file_name)
Loading