diff --git a/README.md b/README.md index 1c5a0a63..e970eeb5 100644 --- a/README.md +++ b/README.md @@ -40,7 +40,7 @@ DIFFBOT_API_KEY="your-diffbot-key" if you only want OpenAI: ```env -LLM_MODELS="gpt-3.5,gpt-4o" +LLM_MODELS="diffbot,openai-gpt-3.5,openai-gpt-4o" OPENAI_API_KEY="your-openai-key" ``` @@ -70,6 +70,18 @@ GOOGLE_CLIENT_ID="xxxx" You can of course combine all (local, youtube, wikipedia, s3 and gcs) or remove any you don't want/need. +### Chat Modes + +By default,all of the chat modes will be available: vector, graph+vector and graph. +If none of the mode is mentioned in the chat modes variable all modes will be available: +```env +CHAT_MODES="" +``` + +If however you want to specifiy the only vector mode or only graph mode you can do that by specifying the mode in the env: +```env +CHAT_MODES="vector,graph+vector" +``` #### Running Backend and Frontend separately (dev environment) Alternatively, you can run the backend and frontend separately: @@ -134,7 +146,8 @@ Allow unauthenticated request : Yes | BACKEND_API_URL | Optional | http://localhost:8000 | URL for backend API | | BLOOM_URL | Optional | https://workspace-preview.neo4j.io/workspace/explore?connectURL={CONNECT_URL}&search=Show+me+a+graph&featureGenAISuggestions=true&featureGenAISuggestionsInternal=true | URL for Bloom visualization | | REACT_APP_SOURCES | Optional | local,youtube,wiki,s3 | List of input sources that will be available | -| LLM_MODELS | Optional | diffbot,gpt-3.5,gpt-4o | Models available for selection on the frontend, used for entities extraction and Q&A Chatbot | +| LLM_MODELS | Optional | diffbot,openai-gpt-3.5,openai-gpt-4o | Models available for selection on the frontend, used for entities extraction and Q&A +| CHAT_MODES | Optional | vector,graph+vector,graph | Chat modes available for Q&A | ENV | Optional | DEV | Environment variable for the app | | TIME_PER_CHUNK | Optional | 4 | Time per chunk for processing | | CHUNK_SIZE | Optional | 5242880 | Size of each chunk of file for upload | diff --git a/backend/src/QA_integration_new.py b/backend/src/QA_integration_new.py index ac2dc0c2..dbd08f7a 100644 --- a/backend/src/QA_integration_new.py +++ b/backend/src/QA_integration_new.py @@ -322,7 +322,7 @@ def QA_RAG(graph, model, question, document_names,session_id, mode): if mode == "graph": graph_chain, qa_llm,model_version = create_graph_chain(model,graph) graph_response = get_graph_response(graph_chain,question) - ai_response = AIMessage(content=graph_response["response"]) + ai_response = AIMessage(content=graph_response["response"]) if graph_response["response"] else AIMessage(content="Something went wrong") messages.append(ai_response) summarize_and_log(history, messages, qa_llm) @@ -342,7 +342,7 @@ def QA_RAG(graph, model, question, document_names,session_id, mode): elif mode == "vector": retrieval_query = VECTOR_SEARCH_QUERY else: - retrieval_query = VECTOR_GRAPH_SEARCH_QUERY + retrieval_query = VECTOR_GRAPH_SEARCH_QUERY.format(no_of_entites=VECTOR_GRAPH_SEARCH_ENTITY_LIMIT) llm, doc_retriever, model_version = setup_chat(model, graph, session_id, document_names,retrieval_query) diff --git a/backend/src/shared/constants.py b/backend/src/shared/constants.py index cc7bb466..7a1983e2 100644 --- a/backend/src/shared/constants.py +++ b/backend/src/shared/constants.py @@ -111,38 +111,102 @@ # """ +# VECTOR_GRAPH_SEARCH_QUERY = """ +# WITH node as chunk, score +# // find the document of the chunk +# MATCH (chunk)-[:PART_OF]->(d:Document) +# // fetch entities +# CALL { WITH chunk +# // entities connected to the chunk +# // todo only return entities that are actually in the chunk, remember we connect all extracted entities to all chunks +# MATCH (chunk)-[:HAS_ENTITY]->(e) + +# // depending on match to query embedding either 1 or 2 step expansion +# WITH CASE WHEN true // vector.similarity.cosine($embedding, e.embedding ) <= 0.95 +# THEN +# collect { MATCH path=(e)(()-[rels:!HAS_ENTITY&!PART_OF]-()){0,1}(:!Chunk&!Document) RETURN path } +# ELSE +# collect { MATCH path=(e)(()-[rels:!HAS_ENTITY&!PART_OF]-()){0,2}(:!Chunk&!Document) RETURN path } +# END as paths + +# RETURN collect{ unwind paths as p unwind relationships(p) as r return distinct r} as rels, +# collect{ unwind paths as p unwind nodes(p) as n return distinct n} as nodes +# } +# // aggregate chunk-details and de-duplicate nodes and relationships +# WITH d, collect(DISTINCT {chunk: chunk, score: score}) AS chunks, avg(score) as avg_score, apoc.coll.toSet(apoc.coll.flatten(collect(rels))) as rels, + +# // TODO sort by relevancy (embeddding comparision?) cut off after X (e.g. 25) nodes? +# apoc.coll.toSet(apoc.coll.flatten(collect( +# [r in rels |[startNode(r),endNode(r)]]),true)) as nodes + +# // generate metadata and text components for chunks, nodes and relationships +# WITH d, avg_score, +# [c IN chunks | c.chunk.text] AS texts, +# [c IN chunks | {id: c.chunk.id, score: c.score}] AS chunkdetails, +# apoc.coll.sort([n in nodes | + +# coalesce(apoc.coll.removeAll(labels(n),['__Entity__'])[0],"") +":"+ +# n.id + (case when n.description is not null then " ("+ n.description+")" else "" end)]) as nodeTexts, +# apoc.coll.sort([r in rels +# // optional filter if we limit the node-set +# // WHERE startNode(r) in nodes AND endNode(r) in nodes +# | +# coalesce(apoc.coll.removeAll(labels(startNode(r)),['__Entity__'])[0],"") +":"+ +# startNode(r).id + +# " " + type(r) + " " + +# coalesce(apoc.coll.removeAll(labels(endNode(r)),['__Entity__'])[0],"") +":" + +# endNode(r).id +# ]) as relTexts + +# // combine texts into response-text +# WITH d, avg_score,chunkdetails, +# "Text Content:\n" + +# apoc.text.join(texts,"\n----\n") + +# "\n----\nEntities:\n"+ +# apoc.text.join(nodeTexts,"\n") + +# "\n----\nRelationships:\n"+ +# apoc.text.join(relTexts,"\n") + +# as text +# RETURN text, avg_score as score, {length:size(text), source: COALESCE( CASE WHEN d.url CONTAINS "None" THEN d.fileName ELSE d.url END, d.fileName), chunkdetails: chunkdetails} AS metadata +# """ + +VECTOR_GRAPH_SEARCH_ENTITY_LIMIT = 25 + VECTOR_GRAPH_SEARCH_QUERY = """ WITH node as chunk, score // find the document of the chunk MATCH (chunk)-[:PART_OF]->(d:Document) + +// aggregate chunk-details +WITH d, collect(DISTINCT {{chunk: chunk, score: score}}) AS chunks, avg(score) as avg_score // fetch entities -CALL { WITH chunk +CALL {{ WITH chunks +UNWIND chunks as chunkScore +WITH chunkScore.chunk as chunk // entities connected to the chunk // todo only return entities that are actually in the chunk, remember we connect all extracted entities to all chunks -MATCH (chunk)-[:HAS_ENTITY]->(e) - +// todo sort by relevancy (embeddding comparision?) cut off after X (e.g. 25) nodes? +OPTIONAL MATCH (chunk)-[:HAS_ENTITY]->(e) +WITH e, count(*) as numChunks +ORDER BY numChunks DESC LIMIT {no_of_entites} // depending on match to query embedding either 1 or 2 step expansion WITH CASE WHEN true // vector.similarity.cosine($embedding, e.embedding ) <= 0.95 THEN -collect { MATCH path=(e)(()-[rels:!HAS_ENTITY&!PART_OF]-()){0,1}(:!Chunk&!Document) RETURN path } +collect {{ OPTIONAL MATCH path=(e)(()-[rels:!HAS_ENTITY&!PART_OF]-()){{0,1}}(:!Chunk&!Document) RETURN path }} ELSE -collect { MATCH path=(e)(()-[rels:!HAS_ENTITY&!PART_OF]-()){0,2}(:!Chunk&!Document) RETURN path } -END as paths - -RETURN collect{ unwind paths as p unwind relationships(p) as r return distinct r} as rels, -collect{ unwind paths as p unwind nodes(p) as n return distinct n} as nodes -} -// aggregate chunk-details and de-duplicate nodes and relationships -WITH d, collect(DISTINCT {chunk: chunk, score: score}) AS chunks, avg(score) as avg_score, apoc.coll.toSet(apoc.coll.flatten(collect(rels))) as rels, - -// TODO sort by relevancy (embeddding comparision?) cut off after X (e.g. 25) nodes? -apoc.coll.toSet(apoc.coll.flatten(collect( - [r in rels |[startNode(r),endNode(r)]]),true)) as nodes +collect {{ OPTIONAL MATCH path=(e)(()-[rels:!HAS_ENTITY&!PART_OF]-()){{0,2}}(:!Chunk&!Document) RETURN path }} +END as paths, e +WITH apoc.coll.toSet(apoc.coll.flatten(collect(distinct paths))) as paths, collect(distinct e) as entities +// de-duplicate nodes and relationships across chunks +RETURN collect{{ unwind paths as p unwind relationships(p) as r return distinct r}} as rels, +collect{{ unwind paths as p unwind nodes(p) as n return distinct n}} as nodes, entities +}} // generate metadata and text components for chunks, nodes and relationships WITH d, avg_score, [c IN chunks | c.chunk.text] AS texts, - [c IN chunks | {id: c.chunk.id, score: c.score}] AS chunkdetails, + [c IN chunks | {{id: c.chunk.id, score: c.score}}] AS chunkdetails, apoc.coll.sort([n in nodes | coalesce(apoc.coll.removeAll(labels(n),['__Entity__'])[0],"") +":"+ @@ -154,24 +218,20 @@ coalesce(apoc.coll.removeAll(labels(startNode(r)),['__Entity__'])[0],"") +":"+ startNode(r).id + " " + type(r) + " " + -coalesce(apoc.coll.removeAll(labels(endNode(r)),['__Entity__'])[0],"") +":" + -endNode(r).id +coalesce(apoc.coll.removeAll(labels(endNode(r)),['__Entity__'])[0],"") +":" + endNode(r).id ]) as relTexts - +, entities // combine texts into response-text -WITH d, avg_score,chunkdetails, -"Text Content:\n" + -apoc.text.join(texts,"\n----\n") + -"\n----\nEntities:\n"+ -apoc.text.join(nodeTexts,"\n") + -"\n----\nRelationships:\n"+ -apoc.text.join(relTexts,"\n") - -as text -RETURN text, avg_score as score, {length:size(text), source: COALESCE( CASE WHEN d.url CONTAINS "None" THEN d.fileName ELSE d.url END, d.fileName), chunkdetails: chunkdetails} AS metadata -""" - - +WITH d, avg_score,chunkdetails, +"Text Content:\\n" + +apoc.text.join(texts,"\\n----\\n") + +"\\n----\\nEntities:\\n"+ +apoc.text.join(nodeTexts,"\\n") + +"\\n----\\nRelationships:\\n" + +apoc.text.join(relTexts,"\\n") +as text,entities +RETURN text, avg_score as score, {{length:size(text), source: COALESCE( CASE WHEN d.url CONTAINS "None" THEN d.fileName ELSE d.url END, d.fileName), chunkdetails: chunkdetails}} AS metadata +""" \ No newline at end of file diff --git a/docker-compose.yml b/docker-compose.yml index afc7e968..2578073b 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -51,13 +51,14 @@ services: args: - BACKEND_API_URL=${BACKEND_API_URL-http://localhost:8000} - REACT_APP_SOURCES=${REACT_APP_SOURCES-local,youtube,wiki,s3} - - LLM_MODELS=${LLM_MODELS-diffbot,gpt-3.5,gpt-4o} + - LLM_MODELS=${LLM_MODELS-diffbot,openai-gpt-3.5,openai-gpt-4o} - GOOGLE_CLIENT_ID=${GOOGLE_CLIENT_ID-""} - BLOOM_URL=${BLOOM_URL-https://workspace-preview.neo4j.io/workspace/explore?connectURL={CONNECT_URL}&search=Show+me+a+graph&featureGenAISuggestions=true&featureGenAISuggestionsInternal=true} - TIME_PER_CHUNK=${TIME_PER_CHUNK-4} - TIME_PER_PAGE=${TIME_PER_PAGE-50} - CHUNK_SIZE=${CHUNK_SIZE-5242880} - ENV=${ENV-DEV} + - CHAT_MODES=${CHAT_MODES-""} volumes: - ./frontend:/app - /app/node_modules diff --git a/example.env b/example.env index 0c980d57..d9bc8a2d 100644 --- a/example.env +++ b/example.env @@ -28,9 +28,10 @@ ENTITY_EMBEDDING=True BACKEND_API_URL="http://localhost:8000" BLOOM_URL="https://workspace-preview.neo4j.io/workspace/explore?connectURL={CONNECT_URL}&search=Show+me+a+graph&featureGenAISuggestions=true&featureGenAISuggestionsInternal=true" REACT_APP_SOURCES="local,youtube,wiki,s3,web" -LLM_MODELS="diffbot,gpt-3.5,gpt-4o" # ",ollama_llama3" +LLM_MODELS="diffbot,openai-gpt-3.5,openai-gpt-4o" # ",ollama_llama3" ENV="DEV" TIME_PER_CHUNK=4 TIME_PER_PAGE=50 CHUNK_SIZE=5242880 GOOGLE_CLIENT_ID="" +CHAT_MODES="" diff --git a/frontend/README.md b/frontend/README.md index 7f7f7b08..11a830c7 100644 --- a/frontend/README.md +++ b/frontend/README.md @@ -1,6 +1,6 @@ # Neo4j Knowledge Graph Builder -Reactjs Responsive app for building an knowledge graph using [Neo4j Needle](https://www.neo4j.design/). +Reactjs app for building an knowledge graph using [Neo4j Needle](https://www.neo4j.design/). ## Features - 🚀 Responsive: Adapts to different screen sizes for optimal user experience. diff --git a/frontend/src/assets/images/web-search-darkmode-final.svg b/frontend/src/assets/images/web-search-darkmode-final.svg deleted file mode 100644 index 866b21a3..00000000 --- a/frontend/src/assets/images/web-search-darkmode-final.svg +++ /dev/null @@ -1 +0,0 @@ - \ No newline at end of file diff --git a/frontend/src/assets/images/web-search-darkmode.svg b/frontend/src/assets/images/web-search-darkmode-final2.svg similarity index 88% rename from frontend/src/assets/images/web-search-darkmode.svg rename to frontend/src/assets/images/web-search-darkmode-final2.svg index 792e8824..1cf29a09 100644 --- a/frontend/src/assets/images/web-search-darkmode.svg +++ b/frontend/src/assets/images/web-search-darkmode-final2.svg @@ -1,7 +1,7 @@ - + - + \ No newline at end of file diff --git a/frontend/src/assets/images/youtube-darkmode.svg b/frontend/src/assets/images/youtube-darkmode.svg new file mode 100644 index 00000000..bbcc14c3 --- /dev/null +++ b/frontend/src/assets/images/youtube-darkmode.svg @@ -0,0 +1,7 @@ + + + + + + + \ No newline at end of file diff --git a/frontend/src/assets/images/youtube-lightmode.svg b/frontend/src/assets/images/youtube-lightmode.svg new file mode 100644 index 00000000..9040340e --- /dev/null +++ b/frontend/src/assets/images/youtube-lightmode.svg @@ -0,0 +1,4 @@ + + + + \ No newline at end of file diff --git a/frontend/src/components/ChatBot/ChatInfoModal.tsx b/frontend/src/components/ChatBot/ChatInfoModal.tsx index 15d4c51b..e82693eb 100644 --- a/frontend/src/components/ChatBot/ChatInfoModal.tsx +++ b/frontend/src/components/ChatBot/ChatInfoModal.tsx @@ -84,7 +84,6 @@ const ChatInfoModal: React.FC = ({ setLoading(true); chunkEntitiesAPI(userCredentials as UserCredentials, chunk_ids.map((c) => c.id).join(',')) .then((response) => { - console.log({ response }); setInfoEntities(response.data.data.nodes); setNodes(response.data.data.nodes); setRelationships(response.data.data.relationships); @@ -161,10 +160,14 @@ const ChatInfoModal: React.FC = ({ - {mode != 'graph' && Sources used} - {(mode === 'graph+vector' || mode === 'graph') && Top Entities used} - {mode === 'graph' && cypher_query?.trim().length && Generated Cypher Query} - {mode != 'graph' && Chunks} + {mode != 'graph' ? Sources used : <>} + {mode === 'graph+vector' || mode === 'graph' ? Top Entities used : <>} + {mode === 'graph' && cypher_query?.trim().length ? ( + Generated Cypher Query + ) : ( + <> + )} + {mode != 'graph' ? Chunks : <>} @@ -289,7 +292,10 @@ const ChatInfoModal: React.FC = ({ className='flex items-center mb-2 text-ellipsis whitespace-nowrap max-w-[100%)] overflow-hidden' >
- {label[Object.keys(label)[0]]['id'] as string ?? Object.keys(label)[0]} + { + //@ts-ignore + label[Object.keys(label)[0]].id ?? Object.keys(label)[0] + }
)) diff --git a/frontend/src/components/ChatBot/ChatModeToggle.tsx b/frontend/src/components/ChatBot/ChatModeToggle.tsx index f5011091..a2becd6c 100644 --- a/frontend/src/components/ChatBot/ChatModeToggle.tsx +++ b/frontend/src/components/ChatBot/ChatModeToggle.tsx @@ -36,13 +36,15 @@ export default function ChatModeToggle({ setchatMode(m); }, disabledCondition: false, - description: - {chatMode === m && ( - <> - Selected - - )} - , + description: ( + + {chatMode === m && ( + <> + Selected + + )} + + ), }; }), [chatMode, chatModes] diff --git a/frontend/src/components/ChatBot/Chatbot.tsx b/frontend/src/components/ChatBot/Chatbot.tsx index 25b89e48..70c81135 100644 --- a/frontend/src/components/ChatBot/Chatbot.tsx +++ b/frontend/src/components/ChatBot/Chatbot.tsx @@ -47,9 +47,7 @@ const Chatbot: React.FC = (props) => { }); let selectedFileNames: CustomFile[] = []; selectedRows.forEach((id) => { - console.log(id); filesData.forEach((f) => { - console.log(f.id, id); if (f.id === id) { selectedFileNames.push(f); } @@ -301,14 +299,16 @@ const Chatbot: React.FC = (props) => {
{chat.message}
diff --git a/frontend/src/components/Content.tsx b/frontend/src/components/Content.tsx index eba399d3..da71cc9b 100644 --- a/frontend/src/components/Content.tsx +++ b/frontend/src/components/Content.tsx @@ -30,6 +30,9 @@ const Content: React.FC = ({ openTextSchema, isSchema, setIsSchema, + showEnhancementDialog, + setshowEnhancementDialog, + closeSettingModal }) => { const [init, setInit] = useState(false); const [openConnection, setOpenConnection] = useState(false); @@ -41,7 +44,6 @@ const Content: React.FC = ({ const [extractLoading, setextractLoading] = useState(false); const [isLargeFile, setIsLargeFile] = useState(false); const [showSettingnModal, setshowSettingModal] = useState(false); - const [showEnhancementDialog, setshowEnhancementDialog] = useState(false); const { filesData, @@ -286,8 +288,9 @@ const Content: React.FC = ({ const handleOpenGraphClick = () => { const bloomUrl = process.env.BLOOM_URL; const uriCoded = userCredentials?.uri.replace(/:\d+$/, ''); - const connectURL = `${uriCoded?.split('//')[0]}//${userCredentials?.userName}@${uriCoded?.split('//')[1]}:${userCredentials?.port ?? '7687' - }`; + const connectURL = `${uriCoded?.split('//')[0]}//${userCredentials?.userName}@${uriCoded?.split('//')[1]}:${ + userCredentials?.port ?? '7687' + }`; const encodedURL = encodeURIComponent(connectURL); const replacedUrl = bloomUrl?.replace('{CONNECT_URL}', encodedURL); window.open(replacedUrl, '_blank'); @@ -297,10 +300,10 @@ const Content: React.FC = ({ isLeftExpanded && isRightExpanded ? 'contentWithExpansion' : isRightExpanded - ? 'contentWithChatBot' - : !isLeftExpanded && !isRightExpanded - ? 'w-[calc(100%-128px)]' - : 'contentWithDropzoneExpansion'; + ? 'contentWithChatBot' + : !isLeftExpanded && !isRightExpanded + ? 'w-[calc(100%-128px)]' + : 'contentWithDropzoneExpansion'; const handleGraphView = () => { setOpenGraphView(true); @@ -586,6 +589,7 @@ const Content: React.FC = ({ )} @@ -615,10 +619,10 @@ const Content: React.FC = ({ )} {isSchema ? ( - Empty Graph Schema configured{' '} + {(!selectedNodes.length || !selectedNodes.length) && 'Empty'} Graph Schema configured {selectedNodes.length || selectedRels.length - ? `${selectedNodes.length} Labels + ${selectedRels.length} Rel Types` - : ''}{' '} + ? `(${selectedNodes.length} Labels + ${selectedRels.length} Rel Types)` + : ''} ) : ( No Graph Schema configured @@ -653,8 +657,9 @@ const Content: React.FC = ({ ref={childRef} > diff --git a/frontend/src/components/DataSources/Local/DropZone.tsx b/frontend/src/components/DataSources/Local/DropZone.tsx index e1c245c0..b775e423 100644 --- a/frontend/src/components/DataSources/Local/DropZone.tsx +++ b/frontend/src/components/DataSources/Local/DropZone.tsx @@ -278,4 +278,4 @@ const DropZone: FunctionComponent = () => { ); }; -export default DropZone; \ No newline at end of file +export default DropZone; diff --git a/frontend/src/components/FileTable.tsx b/frontend/src/components/FileTable.tsx index 302c2ce9..20277718 100644 --- a/frontend/src/components/FileTable.tsx +++ b/frontend/src/components/FileTable.tsx @@ -728,6 +728,6 @@ function IndeterminateCheckbox({ }, [ref, indeterminate]); return ( - + ); } diff --git a/frontend/src/components/Layout/PageLayout.tsx b/frontend/src/components/Layout/PageLayout.tsx index a57e7bfc..53b101ca 100644 --- a/frontend/src/components/Layout/PageLayout.tsx +++ b/frontend/src/components/Layout/PageLayout.tsx @@ -28,6 +28,7 @@ export default function PageLayoutNew({ const [showChatBot, setShowChatBot] = useState(false); const [showDrawerChatbot, setShowDrawerChatbot] = useState(true); const [clearHistoryData, setClearHistoryData] = useState(false); + const [showEnhancementDialog, setshowEnhancementDialog] = useState(false); const { userCredentials } = useCredentials(); const toggleLeftDrawer = () => setIsLeftExpanded(!isLeftExpanded); const toggleRightDrawer = () => setIsRightExpanded(!isRightExpanded); @@ -86,16 +87,26 @@ export default function PageLayoutNew({ { - setShowTextFromSchemaDialog(false); + setShowTextFromSchemaDialog({ triggeredFrom: '', show: false }); + switch (showTextFromSchemaDialog.triggeredFrom) { + case 'enhancementtab': + setshowEnhancementDialog(true); + break; + case 'schemadialog': + openSettingsDialog(); + break; + default: + break; + } }} showAlert={showAlert} > { - setShowTextFromSchemaDialog(true); + setShowTextFromSchemaDialog({ triggeredFrom: 'schemadialog', show: true }); }} open={isSettingPanelExpanded} onClose={closeSettingModal} @@ -109,10 +120,14 @@ export default function PageLayoutNew({ isRightExpanded={isRightExpanded} showChatBot={showChatBot} openTextSchema={() => { - setShowTextFromSchemaDialog(true); + setShowTextFromSchemaDialog({ triggeredFrom: 'schemadialog', show: true }); }} isSchema={isSchema} setIsSchema={setIsSchema} + showEnhancementDialog={showEnhancementDialog} + setshowEnhancementDialog={setshowEnhancementDialog} + closeSettingModal={closeSettingModal} + /> {showDrawerChatbot && ( diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/DeleteTabForOrphanNodes/index.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/DeleteTabForOrphanNodes/index.tsx index 3701a483..7a6fa98b 100644 --- a/frontend/src/components/Popups/GraphEnhancementDialog/DeleteTabForOrphanNodes/index.tsx +++ b/frontend/src/components/Popups/GraphEnhancementDialog/DeleteTabForOrphanNodes/index.tsx @@ -134,14 +134,12 @@ export default function DeletePopUpForOrphanNodes({ }, header: () => Related Documents , footer: (info) => info.column.id, - maxSize: 280, }), columnHelper.accessor((row) => row.chunkConnections, { id: 'Connected Chunks', cell: (info) => {info?.getValue()}, header: () => Connected Chunks, footer: (info) => info.column.id, - minSize: 280, }), ], [] @@ -226,6 +224,9 @@ export default function DeletePopUpForOrphanNodes({ zebraStriping: true, headerStyle: 'clean', }} + rootProps={{ + className: 'max-h-[355px] !overflow-y-auto', + }} isLoading={isLoading} components={{ Body: (props) => , diff --git a/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx b/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx index eb848726..e85ca660 100644 --- a/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx +++ b/frontend/src/components/Popups/GraphEnhancementDialog/index.tsx @@ -1,6 +1,6 @@ import { Dialog, Tabs, Box, Typography, Flex } from '@neo4j-ndl/react'; import graphenhancement from '../../../assets/images/graph-enhancements.svg'; -import { useState } from 'react'; +import { useEffect, useState } from 'react'; import DeletePopUpForOrphanNodes from './DeleteTabForOrphanNodes'; import deleteOrphanAPI from '../../../services/DeleteOrphanNodes'; import { UserCredentials } from '../../../types'; @@ -13,6 +13,7 @@ import { useFileContext } from '../../../context/UsersFiles'; export default function GraphEnhancementDialog({ open, onClose, + closeSettingModal }: { open: boolean; onClose: () => void; @@ -20,6 +21,7 @@ export default function GraphEnhancementDialog({ alertmsg: string, alerttype: OverridableStringUnion | undefined ) => void; + closeSettingModal:()=>void }) { const [orphanDeleteAPIloading, setorphanDeleteAPIloading] = useState(false); const { setShowTextFromSchemaDialog } = useFileContext(); @@ -35,6 +37,10 @@ export default function GraphEnhancementDialog({ console.log(error); } }; + useEffect(() => { + closeSettingModal() + }, []) + const [activeTab, setactiveTab] = useState(0); return ( - +
{ - setShowTextFromSchemaDialog(true); + setShowTextFromSchemaDialog({ triggeredFrom: 'enhancementtab', show: true }); }} colseEnhanceGraphSchemaDialog={onClose} settingView='headerView' diff --git a/frontend/src/components/WebSources/GenericSourceButton.tsx b/frontend/src/components/WebSources/GenericSourceButton.tsx index 6cad6288..fcde2c15 100644 --- a/frontend/src/components/WebSources/GenericSourceButton.tsx +++ b/frontend/src/components/WebSources/GenericSourceButton.tsx @@ -1,6 +1,6 @@ import CustomButton from '../UI/CustomButton'; import internet from '../../assets/images/web-search-svgrepo-com.svg'; -import internetdarkmode from '../../assets/images/web-search-darkmode-final.svg'; +import internetdarkmode from '../../assets/images/web-search-darkmode-final2.svg'; import { DataComponentProps } from '../../types'; import { Flex, Typography } from '@neo4j-ndl/react'; import IconButtonWithToolTip from '../UI/IconButtonToolTip'; diff --git a/frontend/src/components/WebSources/GenericSourceModal.tsx b/frontend/src/components/WebSources/GenericSourceModal.tsx index 9dfe91b4..44a3b5cd 100644 --- a/frontend/src/components/WebSources/GenericSourceModal.tsx +++ b/frontend/src/components/WebSources/GenericSourceModal.tsx @@ -1,5 +1,6 @@ import { Box, Dialog, Tabs, Typography } from '@neo4j-ndl/react'; -import youtubelogo from '../../assets/images/youtube.svg'; +import youtubelightmodelogo from '../../assets/images/youtube-lightmode.svg'; +import youtubedarkmodelogo from '../../assets/images/youtube-darkmode.svg'; import wikipedialogo from '../../assets/images/wikipedia.svg'; import weblogo from '../../assets/images/web.svg'; import webdarkmode from '../../assets/images/web-darkmode.svg'; @@ -44,7 +45,10 @@ export default function GenericModal({ {APP_SOURCES != undefined && APP_SOURCES.includes('youtube') && ( - + )} {APP_SOURCES != undefined && APP_SOURCES.includes('wiki') && ( diff --git a/frontend/src/context/ThemeWrapper.tsx b/frontend/src/context/ThemeWrapper.tsx index e22648b2..110e8638 100644 --- a/frontend/src/context/ThemeWrapper.tsx +++ b/frontend/src/context/ThemeWrapper.tsx @@ -11,7 +11,7 @@ interface ThemeWrapperProps { } const ThemeWrapper = ({ children }: ThemeWrapperProps) => { const prefersDarkMode = useMediaQuery('(prefers-color-scheme: dark)'); - //@ts-ignore + // @ts-ignore const defaultMode: 'light' | 'dark' = localStorage.getItem('mode'); const [mode, setMode] = useState<'light' | 'dark'>(prefersDarkMode ? 'dark' : defaultMode ?? 'light'); const [usingPreferredMode, setUsingPreferredMode] = useState(true); diff --git a/frontend/src/context/UsersFiles.tsx b/frontend/src/context/UsersFiles.tsx index b3cda429..26daf6dd 100644 --- a/frontend/src/context/UsersFiles.tsx +++ b/frontend/src/context/UsersFiles.tsx @@ -2,7 +2,10 @@ import { createContext, useContext, useState, Dispatch, SetStateAction, FC, useE import { CustomFile, FileContextProviderProps, OptionType } from '../types'; import { defaultLLM } from '../utils/Constants'; import { useCredentials } from './UserCredentials'; - +interface showTextFromSchemaDialogType { + triggeredFrom: string; + show: boolean; +} interface FileContextType { files: (File | null)[] | []; filesData: CustomFile[] | []; @@ -26,8 +29,8 @@ interface FileContextType { setchatMode: Dispatch>; isSchema: boolean; setIsSchema: React.Dispatch>; - showTextFromSchemaDialog: boolean; - setShowTextFromSchemaDialog: React.Dispatch>; + showTextFromSchemaDialog: showTextFromSchemaDialogType; + setShowTextFromSchemaDialog: React.Dispatch>; } const FileContext = createContext(undefined); @@ -47,7 +50,10 @@ const FileContextProvider: FC = ({ children }) => { const [chatMode, setchatMode] = useState('graph+vector'); const { userCredentials } = useCredentials(); const [isSchema, setIsSchema] = useState(false); - const [showTextFromSchemaDialog, setShowTextFromSchemaDialog] = useState(false); + const [showTextFromSchemaDialog, setShowTextFromSchemaDialog] = useState({ + triggeredFrom: '', + show: false, + }); useEffect(() => { if (selectedNodeLabelstr != null) { diff --git a/frontend/src/types.ts b/frontend/src/types.ts index bccfdaba..dc0cadf1 100644 --- a/frontend/src/types.ts +++ b/frontend/src/types.ts @@ -154,6 +154,9 @@ export interface ContentProps { openTextSchema: () => void; isSchema?: boolean; setIsSchema: Dispatch>; + showEnhancementDialog: boolean; + setshowEnhancementDialog: Dispatch>; + closeSettingModal:()=>void } export interface FileTableProps { diff --git a/frontend/src/utils/Constants.ts b/frontend/src/utils/Constants.ts index a87ef36c..1006b041 100644 --- a/frontend/src/utils/Constants.ts +++ b/frontend/src/utils/Constants.ts @@ -154,7 +154,7 @@ export const buttonCaptions = { details: 'Details', continueSettings: 'Continue', clearSettings: 'Clear Settings', - ask:'Ask' + ask: 'Ask', }; export const taskParam: string[] = ['update_similarity_graph', 'create_fulltext_index', 'create_entity_embedding']; diff --git a/frontend/src/utils/Utils.ts b/frontend/src/utils/Utils.ts index a5f1d068..9230c087 100644 --- a/frontend/src/utils/Utils.ts +++ b/frontend/src/utils/Utils.ts @@ -186,7 +186,9 @@ export const filterData = ( const entityNode = allNodes.filter((node) => !node.labels.includes('Document') && !node.labels.includes('Chunk')); filteredNodes = entityNode ? entityNode : []; // @ts-ignore - filteredRelations = allRelationships.filter((rel) => !['PART_OF', 'FIRST_CHUNK', 'HAS_ENTITY', 'SIMILAR', 'NEXT_CHUNK'].includes(rel.caption)); + filteredRelations = allRelationships.filter( + (rel) => !['PART_OF', 'FIRST_CHUNK', 'HAS_ENTITY', 'SIMILAR', 'NEXT_CHUNK'].includes(rel.caption) + ); filteredScheme = Object.fromEntries(entityTypes.map((key) => [key, scheme[key]])) as Scheme; } else if (!graphType.includes('Document') && !graphType.includes('Entities') && graphType.includes('Chunk')) { // Only Chunk @@ -198,16 +200,22 @@ export const filterData = ( } else if (graphType.includes('Document') && graphType.includes('Entities') && !graphType.includes('Chunk')) { // Document + Entity // @ts-ignore - filteredNodes = allNodes.filter((node) =>node.labels.includes('Document') || (!node.labels.includes('Document') && !node.labels.includes('Chunk')) + filteredNodes = allNodes.filter( + (node) => + node.labels.includes('Document') || (!node.labels.includes('Document') && !node.labels.includes('Chunk')) ); // @ts-ignore - filteredRelations = allRelationships.filter((rel) => !['PART_OF', 'FIRST_CHUNK', 'HAS_ENTITY', 'SIMILAR', 'NEXT_CHUNK'].includes(rel.caption)); + filteredRelations = allRelationships.filter( + (rel) => !['PART_OF', 'FIRST_CHUNK', 'HAS_ENTITY', 'SIMILAR', 'NEXT_CHUNK'].includes(rel.caption) + ); } else if (graphType.includes('Document') && !graphType.includes('Entities') && graphType.includes('Chunk')) { // Document + Chunk // @ts-ignore filteredNodes = allNodes.filter((node) => node.labels.includes('Document') || node.labels.includes('Chunk')); // @ts-ignore - filteredRelations = allRelationships.filter((rel) =>['PART_OF', 'FIRST_CHUNK', 'SIMILAR', 'NEXT_CHUNK'].includes(rel.caption)); + filteredRelations = allRelationships.filter((rel) => + ['PART_OF', 'FIRST_CHUNK', 'SIMILAR', 'NEXT_CHUNK'].includes(rel.caption) + ); filteredScheme = { Document: scheme.Document, Chunk: scheme.Chunk }; } else if (!graphType.includes('Document') && graphType.includes('Entities') && graphType.includes('Chunk')) { // Chunk + Entity