Skip to content

Merge Graph Communities to Graph Backend #733

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 4 commits into from
Sep 12, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
32 changes: 27 additions & 5 deletions backend/src/graphDB_dataAccess.py
Original file line number Diff line number Diff line change
Expand Up @@ -140,6 +140,27 @@ def update_KNN_graph(self):
)
else:
logging.info("Vector index does not exist, So KNN graph not update")

def check_gds_version(self):
try:
gds_procedure_count = """
SHOW PROCEDURES
YIELD name
WHERE name STARTS WITH "gds."
RETURN COUNT(*) AS totalGdsProcedures
"""
result = self.graph.query(gds_procedure_count)
total_gds_procedures = result[0]['totalGdsProcedures'] if result else 0

if total_gds_procedures > 0:
logging.info("GDS is available in the database.")
return True
else:
logging.info("GDS is not available in the database.")
return False
except Exception as e:
logging.error(f"An error occurred while checking GDS version: {e}")
return False

def connection_check_and_get_vector_dimensions(self):
"""
Expand All @@ -166,19 +187,20 @@ def connection_check_and_get_vector_dimensions(self):
embedding_model = os.getenv('EMBEDDING_MODEL')
embeddings, application_dimension = load_embedding_model(embedding_model)
logging.info(f'embedding model:{embeddings} and dimesion:{application_dimension}')
# print(chunks_exists)

gds_status = self.check_gds_version()

if self.graph:
if len(db_vector_dimension) > 0:
return {'db_vector_dimension': db_vector_dimension[0]['vector_dimensions'], 'application_dimension':application_dimension, 'message':"Connection Successful"}
return {'db_vector_dimension': db_vector_dimension[0]['vector_dimensions'], 'application_dimension':application_dimension, 'message':"Connection Successful","gds_status":gds_status}
else:
if len(db_vector_dimension) == 0 and len(result_chunks) == 0:
logging.info("Chunks and vector index does not exists in database")
return {'db_vector_dimension': 0, 'application_dimension':application_dimension, 'message':"Connection Successful","chunks_exists":False}
return {'db_vector_dimension': 0, 'application_dimension':application_dimension, 'message':"Connection Successful","chunks_exists":False,"gds_status":gds_status}
elif len(db_vector_dimension) == 0 and result_chunks[0]['hasEmbedding']==0 and result_chunks[0]['chunks'] > 0:
return {'db_vector_dimension': 0, 'application_dimension':application_dimension, 'message':"Connection Successful","chunks_exists":True}
return {'db_vector_dimension': 0, 'application_dimension':application_dimension, 'message':"Connection Successful","chunks_exists":True,"gds_status":gds_status}
else:
return {'message':"Connection Successful"}
return {'message':"Connection Successful","gds_status":gds_status}

def execute_query(self, query, param=None):
return self.graph.query(query, param)
Expand Down
7 changes: 5 additions & 2 deletions backend/src/graph_query.py
Original file line number Diff line number Diff line change
Expand Up @@ -61,15 +61,18 @@ def process_node(node):
with datetime objects formatted as ISO strings.
"""
try:
labels = set(node.labels)
labels.discard("__Entity__")

node_element = {
"element_id": node.element_id,
"labels": list(node.labels),
"labels": list(labels),
"properties": {}
}
# logging.info(f"Processing node with element ID: {node.element_id}")

for key in node:
if key in ["embedding", "text"]:
if key in ["embedding", "text", "summary"]:
continue
value = node.get(key)
if isinstance(value, time.DateTime):
Expand Down
18 changes: 15 additions & 3 deletions backend/src/shared/constants.py
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@
PROJECT_ID = 'llm-experiments-387609'
GRAPH_CHUNK_LIMIT = 50


#query
GRAPH_QUERY = """
MATCH docs = (d:Document)
Expand All @@ -38,15 +39,26 @@
CALL {{
WITH selectedChunks
UNWIND selectedChunks as c

OPTIONAL MATCH entities=(c:Chunk)-[:HAS_ENTITY]->(e)
OPTIONAL MATCH entityRels=(e)--(e2:!Chunk) WHERE exists {{
(e2)<-[:HAS_ENTITY]-(other) WHERE other IN selectedChunks
}}
RETURN collect(entities) as entities, collect(entityRels) as entityRels
RETURN entities , entityRels, collect(DISTINCT e) as entity
}}
WITH docs,chunks,chunkRels, collect(entities) as entities, collect(entityRels) as entityRels, entity

WITH *

CALL {{
with entity
unwind entity as n
OPTIONAL MATCH community=(n:__Entity__)-[:IN_COMMUNITY]->(p:__Community__)
OPTIONAL MATCH parentcommunity=(p)-[:PARENT_COMMUNITY*]->(p2:__Community__)
return collect(community) as communities , collect(parentcommunity) as parentCommunities
}}

WITH apoc.coll.flatten(docs + chunks + chunkRels + entities + entityRels, true) as paths
WITH apoc.coll.flatten(docs + chunks + chunkRels + entities + entityRels + communities + parentCommunities, true) as paths

// distinct nodes and rels
CALL {{ WITH paths UNWIND paths AS path UNWIND nodes(path) as node WITH distinct node
Expand Down
49 changes: 33 additions & 16 deletions frontend/src/components/ChatBot/ChatModeToggle.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,8 @@ import { useFileContext } from '../../context/UsersFiles';
import CustomMenu from '../UI/Menu';
import { chatModes } from '../../utils/Constants';
import { capitalize } from '@mui/material';

import { capitalizeWithPlus } from '../../utils/Utils';
import { useCredentials } from '../../context/UserCredentials';
export default function ChatModeToggle({
menuAnchor,
closeHandler = () => {},
Expand All @@ -18,25 +19,21 @@ export default function ChatModeToggle({
anchorPortal?: boolean;
disableBackdrop?: boolean;
}) {
const { setchatMode, chatMode } = useFileContext();

const { setchatMode, chatMode, postProcessingTasks } = useFileContext();
const isCommunityAllowed = postProcessingTasks.includes('create_communities');
const { isGdsActive } = useCredentials();
return (
<CustomMenu
closeHandler={closeHandler}
open={open}
MenuAnchor={menuAnchor}
anchorPortal={anchorPortal}
disableBackdrop={disableBackdrop}
items={useMemo(
() =>
chatModes?.map((m) => {
items={useMemo(() => {
if (isGdsActive && isCommunityAllowed) {
return chatModes?.map((m) => {
return {
title: m.includes('+')
? m
.split('+')
.map((s) => capitalize(s))
.join('+')
: capitalize(m),
title: m.includes('+') ? capitalizeWithPlus(m) : capitalize(m),
onClick: () => {
setchatMode(m);
},
Expand All @@ -51,9 +48,29 @@ export default function ChatModeToggle({
</span>
),
};
}),
[chatMode, chatModes]
)}
></CustomMenu>
});
}
return chatModes
?.filter((s) => !s.includes('community'))
?.map((m) => {
return {
title: m.includes('+') ? capitalizeWithPlus(m) : capitalize(m),
onClick: () => {
setchatMode(m);
},
disabledCondition: false,
description: (
<span>
{chatMode === m && (
<>
<StatusIndicator type={`${chatMode === m ? 'success' : 'unknown'}`} /> Selected
</>
)}
</span>
),
};
});
}, [chatMode, chatModes, isCommunityAllowed, isGdsActive])}
/>
);
}
2 changes: 1 addition & 1 deletion frontend/src/components/ChatBot/Chatbot.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import { v4 as uuidv4 } from 'uuid';
import { useFileContext } from '../../context/UsersFiles';
import clsx from 'clsx';
import ReactMarkdown from 'react-markdown';
import IconButtonWithToolTip from '../UI/IconButtonToolTip';
import { IconButtonWithToolTip } from '../UI/IconButtonToolTip';
import { buttonCaptions, tooltips } from '../../utils/Constants';
import useSpeechSynthesis from '../../hooks/useSpeech';
import ButtonWithToolTip from '../UI/ButtonWithToolTip';
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import { TrashIconOutline, XMarkIconOutline } from '@neo4j-ndl/react/icons';
import ChatModeToggle from './ChatModeToggle';
import { Box, IconButton } from '@neo4j-ndl/react';
import { IconProps } from '../../types';
import IconButtonWithToolTip from '../UI/IconButtonToolTip';
import { IconButtonWithToolTip } from '../UI/IconButtonToolTip';
import { tooltips } from '../../utils/Constants';
import { useState } from 'react';
import { RiChatSettingsLine } from 'react-icons/ri';
Expand Down
18 changes: 10 additions & 8 deletions frontend/src/components/Content.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,7 @@ import DeletePopUp from './Popups/DeletePopUp/DeletePopUp';
import GraphEnhancementDialog from './Popups/GraphEnhancementDialog';
import { tokens } from '@neo4j-ndl/base';
import axios from 'axios';
import DatabaseStatusIcon from './UI/DatabaseStatusIcon';

const ConnectionModal = lazy(() => import('./Popups/ConnectionModal/ConnectionModal'));
const ConfirmationDialog = lazy(() => import('./Popups/LargeFilePopUp/ConfirmationDialog'));
Expand All @@ -58,7 +59,7 @@ const Content: React.FC<ContentProps> = ({
const [openGraphView, setOpenGraphView] = useState<boolean>(false);
const [inspectedName, setInspectedName] = useState<string>('');
const [connectionStatus, setConnectionStatus] = useState<boolean>(false);
const { setUserCredentials, userCredentials } = useCredentials();
const { setUserCredentials, userCredentials, isGdsActive, setGdsActive } = useCredentials();
const [showConfirmationModal, setshowConfirmationModal] = useState<boolean>(false);
const [extractLoading, setextractLoading] = useState<boolean>(false);

Expand Down Expand Up @@ -126,6 +127,9 @@ const Content: React.FC<ContentProps> = ({
database: neo4jConnection.database,
port: neo4jConnection.uri.split(':')[2],
});
if (neo4jConnection.isgdsActive !== undefined) {
setGdsActive(neo4jConnection.isgdsActive);
}
} else {
setOpenConnection((prev) => ({ ...prev, openPopUp: true }));
}
Expand Down Expand Up @@ -711,16 +715,14 @@ const Content: React.FC<ContentProps> = ({
chunksExistsWithDifferentEmbedding={openConnection.chunksExistsWithDifferentDimension}
/>
</Suspense>

<div className='connectionstatus__container'>
<span className='h6 px-1'>Neo4j connection</span>
<Typography variant='body-medium'>
{!connectionStatus ? <StatusIndicator type='danger' /> : <StatusIndicator type='success' />}
{connectionStatus ? (
<span className='n-body-small'>{userCredentials?.uri}</span>
) : (
<span className='n-body-small'>Not Connected</span>
)}
<DatabaseStatusIcon
isConnected={connectionStatus}
isGdsActive={isGdsActive}
uri={userCredentials && userCredentials?.uri}
/>
<div className='pt-1'>
{!isSchema ? (
<StatusIndicator type='danger' />
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/DataSources/Local/DropZone.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ import CustomAlert from '../../UI/Alert';
import { CustomFile, CustomFileBase, UserCredentials, alertStateType } from '../../../types';
import { buttonCaptions, chunkSize } from '../../../utils/Constants';
import { InformationCircleIconOutline } from '@neo4j-ndl/react/icons';
import IconButtonWithToolTip from '../../UI/IconButtonToolTip';
import { IconButtonWithToolTip } from '../../UI/IconButtonToolTip';
import { uploadAPI } from '../../../utils/FileAPI';

const DropZone: FunctionComponent = () => {
Expand Down
2 changes: 1 addition & 1 deletion frontend/src/components/FileTable.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ import useServerSideEvent from '../hooks/useSse';
import { AxiosError } from 'axios';
import { XMarkIconOutline } from '@neo4j-ndl/react/icons';
import cancelAPI from '../services/CancelAPI';
import IconButtonWithToolTip from './UI/IconButtonToolTip';
import { IconButtonWithToolTip } from './UI/IconButtonToolTip';
import { batchSize, largeFileSize, llms } from '../utils/Constants';
import IndeterminateCheckbox from './UI/CustomCheckBox';
let onlyfortheFirstRender = true;
Expand Down
14 changes: 11 additions & 3 deletions frontend/src/components/Graph/CheckboxSelection.tsx
Original file line number Diff line number Diff line change
@@ -1,21 +1,29 @@
import { Checkbox } from '@neo4j-ndl/react';
import React from 'react';
import { CheckboxSectionProps } from '../../types';
const CheckboxSelection: React.FC<CheckboxSectionProps> = ({ graphType, loading, handleChange }) => (
import { graphLabels } from '../../utils/Constants';

const CheckboxSelection: React.FC<CheckboxSectionProps> = ({ graphType, loading, handleChange, isgds }) => (
<div className='flex gap-5 mt-2 justify-between'>
<div className='flex gap-5'>
<Checkbox
checked={graphType.includes('DocumentChunk')}
label='Document & Chunk'
label={graphLabels.docChunk}
disabled={loading}
onChange={() => handleChange('DocumentChunk')}
/>
<Checkbox
checked={graphType.includes('Entities')}
label='Entities'
label={graphLabels.entities}
disabled={loading}
onChange={() => handleChange('Entities')}
/>
{isgds && (<Checkbox
checked={graphType.includes('Communities')}
label={graphLabels.community}
disabled={loading}
onChange={() => handleChange('Communities')}
/>)}
</div>
</div>
);
Expand Down
Loading