Merge pull request #97 from topoteretes/feat/frontend-redesign

feat: frontend redesign
This commit is contained in:
Vasilije 2024-05-27 07:35:44 +02:00 committed by GitHub
commit 402f0afe39
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
77 changed files with 1047 additions and 838 deletions

View file

@ -10,7 +10,7 @@
"dependencies": {
"classnames": "^2.5.1",
"next": "14.2.3",
"ohmy-ui": "^0.0.2",
"ohmy-ui": "^0.0.3",
"react": "^18",
"react-dom": "^18",
"uuid": "^9.0.1"
@ -25,6 +25,45 @@
"typescript": "^5"
}
},
"../../../Guerrilla/ohmy-ui": {
"version": "0.0.3",
"extraneous": true,
"license": "MIT",
"dependencies": {
"classnames": "^2.3.2"
},
"devDependencies": {
"@rollup/plugin-commonjs": "^25.0.7",
"@rollup/plugin-node-resolve": "^15.2.3",
"@rollup/plugin-terser": "^0.4.4",
"@rollup/plugin-typescript": "^11.1.6",
"@types/node": "^20",
"@types/react": "^18",
"@types/react-dom": "^18",
"eslint": "^8",
"eslint-config-next": "14.0.4",
"next": "14.0.4",
"postcss": "^8.4.32",
"postcss-custom-media": "^10.0.2",
"postcss-import": "^15.1.0",
"postcss-preset-env": "^9.3.0",
"rollup": "^4.17.2",
"rollup-plugin-dts": "^6.1.0",
"rollup-plugin-peer-deps-external": "^2.2.4",
"rollup-plugin-postcss": "^4.0.2",
"typescript": "^5"
},
"peerDependencies": {
"@types/react": "^18",
"react": "^18",
"react-dom": "^18"
},
"peerDependenciesMeta": {
"@types/react": {
"optional": true
}
}
},
"node_modules/@babel/runtime": {
"version": "7.24.5",
"dev": true,
@ -2853,8 +2892,9 @@
}
},
"node_modules/ohmy-ui": {
"version": "0.0.2",
"license": "MIT",
"version": "0.0.3",
"resolved": "https://registry.npmjs.org/ohmy-ui/-/ohmy-ui-0.0.3.tgz",
"integrity": "sha512-GXtTZxbL+sXdlT26+mcSrqvUPUIWNUiZOtKT+0lxK6DglGezB0Y/NVQHBcsoPCzmXHY3wi4UBaCH6//8DjHKXA==",
"dependencies": {
"classnames": "^2.3.2"
},

View file

@ -11,7 +11,7 @@
"dependencies": {
"classnames": "^2.5.1",
"next": "14.2.3",
"ohmy-ui": "^0.0.2",
"ohmy-ui": "^0.0.3",
"react": "^18",
"react-dom": "^18",
"uuid": "^9.0.1"

View file

@ -1,6 +1,6 @@
:root {
--max-width: 1100px;
--border-radius: 12px;
--border-radius: 2px;
--font-mono: ui-monospace, Menlo, Monaco, "Cascadia Mono", "Segoe UI Mono",
"Roboto Mono", "Oxygen Mono", "Ubuntu Monospace", "Source Code Pro",
"Fira Mono", "Droid Sans Mono", "Courier New", monospace;

View file

@ -1,7 +1,6 @@
.main {
display: flex;
flex-direction: column;
padding: 32px;
min-height: 100vh;
}
.main.noData {

View file

@ -2,17 +2,17 @@
import { useCallback, useEffect, useState } from 'react';
import styles from "./page.module.css";
import { Notification, NotificationContainer, Text, useNotifications } from 'ohmy-ui';
import { GhostButton, Notification, NotificationContainer, Spacer, Stack, Text, useBoolean, useNotifications } from 'ohmy-ui';
import useDatasets from '@/modules/ingestion/useDatasets';
import DataView, { Data } from '@/modules/ingestion/DataView';
import DatasetsView from '@/modules/ingestion/DatasetsView';
import classNames from 'classnames';
import addData from '@/modules/ingestion/addData';
import cognifyDataset from '@/modules/datasets/cognifyDataset';
import deleteDataset from '@/modules/datasets/deleteDataset';
import getDatasetData from '@/modules/datasets/getDatasetData';
import getExplorationGraphUrl from '@/modules/exploration/getExplorationGraphUrl';
import { Footer } from '@/ui/Partials';
import { Footer, SettingsModal } from '@/ui/Partials';
import { TextLogo } from '@/ui/App';
import { SettingsIcon } from '@/ui/Icons';
export default function Home() {
const {
@ -60,45 +60,49 @@ export default function Home() {
});
}, [showNotification]);
const onDatasetDelete = useCallback((dataset: { id: string }) => {
deleteDataset(dataset)
.then(() => {
showNotification(`Dataset "${dataset.id}" deleted.`, 5000);
refreshDatasets();
})
}, [refreshDatasets, showNotification]);
const onDatasetExplore = useCallback((dataset: { id: string }) => {
return getExplorationGraphUrl(dataset);
}, []);
const {
value: isSettingsModalOpen,
setTrue: openSettingsModal,
setFalse: closeSettingsModal,
} = useBoolean(false);
return (
<main className={styles.main}>
<div className={styles.data}>
<div className={classNames(styles.datasetsView, {
[styles.openDatasetData]: datasetData.length > 0,
})}>
<DatasetsView
datasets={datasets}
onDataAdd={onDataAdd}
onDatasetClick={openDatasetData}
onDatasetCognify={onDatasetCognify}
onDatasetDelete={onDatasetDelete}
onDatasetExplore={onDatasetExplore}
/>
</div>
{datasetData.length > 0 && selectedDataset && (
<div className={styles.dataView}>
<DataView
data={datasetData}
datasetId={selectedDataset}
onClose={closeDatasetData}
onDataAdd={onDataAdd}
<Spacer inset vertical="1" horizontal="2">
<Stack orientation="horizontal" gap="between" align="center">
<TextLogo width={225} height={64} />
<GhostButton hugContent onClick={openSettingsModal}>
<SettingsIcon />
</GhostButton>
</Stack>
</Spacer>
<SettingsModal isOpen={isSettingsModalOpen} onClose={closeSettingsModal} />
<Spacer inset vertical="1" horizontal="3">
<div className={styles.data}>
<div className={classNames(styles.datasetsView, {
[styles.openDatasetData]: datasetData.length > 0,
})}>
<DatasetsView
datasets={datasets}
onDatasetClick={openDatasetData}
onDatasetCognify={onDatasetCognify}
/>
</div>
)}
</div>
<Footer />
{datasetData.length > 0 && selectedDataset && (
<div className={styles.dataView}>
<DataView
data={datasetData}
datasetId={selectedDataset}
onClose={closeDatasetData}
onDataAdd={onDataAdd}
/>
</div>
)}
</div>
</Spacer>
<Spacer inset horizontal="3" wrap>
<Footer />
</Spacer>
<NotificationContainer gap="1" bottom right>
{notifications.map((notification, index: number) => (
<Notification

View file

@ -0,0 +1,8 @@
.files {
width: 100%;
padding: 4px;
}
.fileSize {
display: block;
}

View file

@ -0,0 +1,97 @@
import { useCallback, useState } from 'react';
import { CTAButton, GhostButton, Stack, Text, TrashIcon, UploadIcon, UploadInput, useBoolean } from 'ohmy-ui';
import { Divider } from '@/ui/Layout';
import addData from '@/modules/ingestion/addData';
import { LoadingIndicator } from '@/ui/App';
import styles from './AddStep.module.css';
import { WizardHeading } from '@/ui/Partials/Wizard';
interface ConfigStepProps {
onNext: () => void;
}
export default function AddStep({ onNext }: ConfigStepProps) {
const [files, setFiles] = useState<File[]>([]);
const {
value: isUploading,
setTrue: disableUploading,
setFalse: enableUploading,
} = useBoolean(false);
const uploadFiles = useCallback(() => {
disableUploading()
addData({ id: 'main' }, files)
.then(() => {
onNext();
})
.finally(() => enableUploading());
}, [disableUploading, enableUploading, files, onNext]);
const addFiles = useCallback((files: File[]) => {
setFiles((existingFiles) => {
const newFiles = files.filter((file) => !existingFiles.some((existingFile) => existingFile.name === file.name));
return [...existingFiles, ...newFiles]
});
}, []);
const removeFile = useCallback((file: File) => {
setFiles((files) => files.filter((f) => f !== file));
}, []);
return (
<Stack orientation="vertical" gap="6">
<WizardHeading><Text light size="large">Step 2/3</Text> Add knowledge</WizardHeading>
<Divider />
<Text align="center">
Cognee lets you process your personal data, books, articles or company data.
Simply add datasets to get started.
</Text>
<Stack gap="1">
<UploadInput onChange={addFiles}>
<Stack gap="2" orientation="horizontal" align="center/center">
<UploadIcon key={files.length} />
<Text>Upload your data</Text>
</Stack>
</UploadInput>
<Stack gap="3" className={styles.files}>
{files.map((file, index) => (
<Stack gap="between" orientation="horizontal" align="center/" key={index}>
<div key={index}>
<Text bold>{file.name}</Text>
<Text className={styles.fileSize} size="small">
{getBiggestUnitSize(file.size)}
</Text>
</div>
<GhostButton hugContent onClick={() => removeFile(file)}>
<TrashIcon />
</GhostButton>
</Stack>
))}
</Stack>
</Stack>
<Stack align="/end">
<CTAButton disabled={isUploading || files.length === 0} onClick={uploadFiles}>
<Stack gap="2" orientation="horizontal" align="center/center">
<Text>Next</Text>
{isUploading && (
<LoadingIndicator />
)}
</Stack>
</CTAButton>
</Stack>
</Stack>
)
}
function getBiggestUnitSize(sizeInBytes: number): string {
const units = ['B', 'KB', 'MB', 'GB'];
let i = 0;
while (sizeInBytes >= 1024 && i < units.length - 1) {
sizeInBytes /= 1024;
i++;
}
return `${sizeInBytes.toFixed(2)} ${units[i]}`;
}

View file

@ -0,0 +1 @@
export { default } from './AddStep';

View file

@ -0,0 +1,47 @@
import { useCallback, useEffect } from 'react';
import { CTAButton, Spacer, Stack, Text, useBoolean } from 'ohmy-ui';
import { Divider } from '@/ui/Layout';
import { CognifyLoadingIndicator, LoadingIndicator } from '@/ui/App';
import { getExplorationGraphUrl } from '@/modules/exploration';
import { WizardHeading } from '@/ui/Partials/Wizard';
import cognifyDataset from '@/modules/datasets/cognifyDataset';
interface ConfigStepProps {
onNext: () => void;
dataset: { id: string }
}
export default function CognifyStep({ onNext, dataset }: ConfigStepProps) {
const {
value: isCognifyRunning,
setFalse: stopCognifyIndicator,
} = useBoolean(true);
useEffect(() => {
cognifyDataset(dataset)
.then(() => {
stopCognifyIndicator();
});
}, [stopCognifyIndicator, dataset]);
return (
<Stack orientation="vertical" gap="6">
<WizardHeading><Text light size="large">Step 3/3</Text> Cognify</WizardHeading>
<Divider />
<Stack align="/center">
<CognifyLoadingIndicator isLoading={isCognifyRunning} />
</Stack>
<Text align="center">
Cognee decomposes your data into facts and connects them in relevant clusters,
so that you can navigate your knowledge better.
</Text>
<CTAButton disabled={isCognifyRunning} onClick={onNext}>
<Stack gap="2" orientation="horizontal" align="center/center">
<Text>Explore data</Text>
</Stack>
</CTAButton>
</Stack>
)
}

View file

@ -0,0 +1 @@
export { default } from './CognifyStep';

View file

@ -0,0 +1,22 @@
import { Stack, Text } from 'ohmy-ui';
import { Divider } from '@/ui/Layout';
import Settings from '@/ui/Partials/SettingsModal/Settings';
import { WizardContent, WizardHeading } from '@/ui/Partials/Wizard';
interface ConfigStepProps {
onNext: () => void;
}
export default function ConfigStep({ onNext }: ConfigStepProps) {
return (
<Stack orientation="vertical" gap="6">
<WizardHeading><Text light size="large">Step 1/3</Text> Basic configuration</WizardHeading>
<Divider />
<Text align="center">
Cognee helps you process your data and create a mind-like structure you can explore.
To get started you need an OpenAI API key.
</Text>
<Settings onDone={onNext} submitButtonText="Next" />
</Stack>
)
}

View file

@ -0,0 +1 @@
export { default } from './ConfigStep';

View file

@ -0,0 +1,20 @@
.explorer {
flex: 1;
min-height: 100%;
}
.explorerContent {
flex: 1;
}
.graphExplorer {
width: 65%;
overflow: hidden;
border-radius: var(--border-radius);
}
.chat {
width: 35%;
display: flex;
}

View file

@ -0,0 +1,14 @@
import { Explorer } from '@/ui/Partials';
import { Spacer } from 'ohmy-ui';
interface ExploreStepProps {
dataset: { id: string };
}
export default function ExploreStep({ dataset }: ExploreStepProps) {
return (
<Spacer horizontal="3">
<Explorer dataset={dataset!} />
</Spacer>
)
}

View file

@ -0,0 +1 @@
export { default } from './ExploreStep';

View file

@ -2,23 +2,12 @@
display: flex;
flex-direction: row;
flex-direction: column;
padding: 32px;
padding: 0;
min-height: 100vh;
gap: 32px;
}
.wizardContainer {
flex: 1;
width: 100%;
}
.wizardDataset {
border: 2px solid white;
border-radius: var(--border-radius);
padding: 24px;
min-width: 350px;
}
.fileSize {
display: block;
display: flex;
padding: 24px 0;
}

View file

@ -1,198 +1,83 @@
import { useCallback, useState } from 'react';
import { IFrameView } from '@/ui';
import { useState } from 'react';
import { CloseIcon, GhostButton, Spacer, Stack, useBoolean } from 'ohmy-ui';
import { TextLogo } from '@/ui/App';
import { SettingsIcon } from '@/ui/Icons';
import { LoadingIndicator, TextLogo } from '@/modules/app';
import { CTAButton, GhostButton, H1, Stack, Text, UploadInput, useBoolean } from 'ohmy-ui';
import { Footer, SettingsModal } from '@/ui/Partials';
import ConfigStep from './ConfigStep';
import AddStep from './AddStep';
import CognifyStep from './CognifyStep';
import ExploreStep from './ExploreStep';
import { WizardContent } from '@/ui/Partials/Wizard';
import styles from './WizardPage.module.css';
interface ExplorationWindowConfig {
url: string;
title: string;
}
import { Divider } from '@/ui/Layout';
import { useSearchParams } from 'next/navigation';
interface WizardPageProps {
onDataAdd: (dataset: { id: string }, files: File[]) => Promise<void>;
onDataCognify: (dataset: { id: string }) => Promise<void>;
onDataExplore: (dataset: { id: string }) => Promise<ExplorationWindowConfig>;
onFinish: () => void;
}
export default function WizardPage({
onDataAdd,
onDataCognify,
onDataExplore,
onFinish,
}: WizardPageProps) {
const [wizardStep, setWizardStep] = useState<'add' | 'upload' | 'cognify' | 'explore'>('add');
const [wizardData, setWizardData] = useState<File[] | null>(null);
const addWizardData = useCallback((files: File[]) => {
setWizardData(files);
setWizardStep('upload');
}, []);
const {
value: isUploadRunning,
setTrue: disableUploadRun,
setFalse: enableUploadRun,
} = useBoolean(false);
const uploadWizardData = useCallback(() => {
disableUploadRun()
onDataAdd({ id: 'main' }, wizardData!)
.then(() => {
setWizardStep('cognify')
})
.finally(() => enableUploadRun());
}, [disableUploadRun, enableUploadRun, onDataAdd, wizardData]);
const {
value: isCognifyRunning,
setTrue: disableCognifyRun,
setFalse: enableCognifyRun,
} = useBoolean(false);
const cognifyWizardData = useCallback(() => {
disableCognifyRun();
onDataCognify({ id: 'main' })
.then(() => {
setWizardStep('explore');
})
.finally(() => enableCognifyRun());
}, [onDataCognify, disableCognifyRun, enableCognifyRun]);
const {
value: isExploreLoading,
setTrue: startLoadingExplore,
setFalse: finishLoadingExplore,
} = useBoolean(false);
const [explorationWindowProps, setExplorationWindowProps] = useState<ExplorationWindowConfig | null>(null);
const {
value: isExplorationWindowShown,
setTrue: showExplorationWindow,
} = useBoolean(false);
const openExplorationWindow = useCallback((explorationWindowProps: ExplorationWindowConfig) => {
setExplorationWindowProps(explorationWindowProps);
showExplorationWindow();
}, [showExplorationWindow]);
const exploreWizardData = useCallback(() => {
startLoadingExplore();
onDataExplore({ id: 'main' })
.then((exploreWindowConfig) => {
openExplorationWindow(exploreWindowConfig);
})
.finally(() => {
finishLoadingExplore();
});
}, [finishLoadingExplore, onDataExplore, openExplorationWindow, startLoadingExplore]);
const searchParams = useSearchParams()
const presetWizardStep = searchParams.get('step') as 'config';
const [wizardStep, setWizardStep] = useState<'config' | 'add' | 'cognify' | 'explore'>(presetWizardStep || 'config');
const {
value: isSettingsModalOpen,
setTrue: openSettingsModal,
setFalse: closeSettingsModal,
} = useBoolean(false);
const dataset = { id: 'main' };
return (
<main className={styles.main}>
<Stack orientation="horizontal" gap="between" align="center">
<TextLogo />
<GhostButton onClick={openSettingsModal}>
<SettingsIcon />
</GhostButton>
</Stack>
<SettingsModal isOpen={isSettingsModalOpen} onClose={closeSettingsModal} />
<Stack gap="4" orientation="vertical" align="center/center" className={styles.wizardContainer}>
{wizardStep === 'explore'
? (<H1>Explore the Knowledge</H1>)
: (<H1>Add Knowledge</H1>)}
<Stack gap="4" orientation="vertical" align="center/center">
{wizardStep === 'upload' && wizardData && (
<Stack gap="4" className={styles.wizardDataset}>
{wizardData.map((file, index) => (
<div key={index}>
<Text bold>{file.name}</Text>
<Text className={styles.fileSize} size="small">
{getBiggestUnitSize(file.size)}
</Text>
</div>
))}
</Stack>
)}
{(wizardStep === 'add' || wizardStep === 'upload') && (
<Text>No data in the system. Let&apos;s add your data.</Text>
)}
{wizardStep === 'cognify' && (
<Text>Process data and make it explorable.</Text>
<Spacer inset vertical="1" horizontal="2">
<Stack orientation="horizontal" gap="between" align="center">
<TextLogo width={225} height={64} />
{wizardStep === 'explore' && (
<GhostButton hugContent onClick={onFinish}>
<CloseIcon />
</GhostButton>
)}
{wizardStep === 'add' && (
<UploadInput onChange={addWizardData}>
<Text>Add data</Text>
</UploadInput>
)}
{wizardStep === 'upload' && (
<CTAButton disabled={isUploadRunning} onClick={uploadWizardData}>
<Stack gap="2" orientation="horizontal" align="center/center">
<Text>Upload</Text>
{isUploadRunning && (
<LoadingIndicator />
)}
</Stack>
</CTAButton>
)}
{wizardStep === 'cognify' && (
<>
{isCognifyRunning && (
<Text>Processing may take a minute, depending on data size.</Text>
)}
<CTAButton disabled={isCognifyRunning} onClick={cognifyWizardData}>
<Stack gap="2" orientation="horizontal" align="center/center">
<Text>Cognify</Text>
{isCognifyRunning && (
<LoadingIndicator />
)}
</Stack>
</CTAButton>
</>
)}
{wizardStep === 'explore' && (
<>
{!isExplorationWindowShown && (
<CTAButton onClick={exploreWizardData}>
<Stack gap="2" orientation="horizontal" align="center/center">
<Text>Start exploring</Text>
{isExploreLoading && (
<LoadingIndicator />
)}
</Stack>
</CTAButton>
)}
{isExplorationWindowShown && (
<IFrameView
src={explorationWindowProps!.url}
title={explorationWindowProps!.title}
onClose={onFinish}
/>
)}
</>
<GhostButton hugContent onClick={openSettingsModal}>
<SettingsIcon />
</GhostButton>
)}
</Stack>
</Stack>
<Footer />
</Spacer>
<Divider />
<SettingsModal isOpen={isSettingsModalOpen} onClose={closeSettingsModal} />
<div className={styles.wizardContainer}>
{wizardStep === 'config' && (
<WizardContent>
<ConfigStep onNext={() => setWizardStep('add')} />
</WizardContent>
)}
{wizardStep === 'add' && (
<WizardContent>
<AddStep onNext={() => setWizardStep('cognify')} />
</WizardContent>
)}
{wizardStep === 'cognify' && (
<WizardContent>
<CognifyStep dataset={dataset} onNext={() => setWizardStep('explore')} />
</WizardContent>
)}
{wizardStep === 'explore' && (
<Spacer inset top="4" bottom="1" horizontal="4">
<ExploreStep dataset={dataset} onClose={onFinish} />
</Spacer>
)}
</div>
<Spacer inset horizontal="3" wrap>
<Footer />
</Spacer>
</main>
)
}
function getBiggestUnitSize(sizeInBytes: number): string {
const units = ['B', 'KB', 'MB', 'GB'];
let i = 0;
while (sizeInBytes >= 1024 && i < units.length - 1) {
sizeInBytes /= 1024;
i++;
}
return `${sizeInBytes.toFixed(2)} ${units[i]}`;
}

View file

@ -2,30 +2,14 @@
import { useCallback } from 'react';
import WizardPage from './WizardPage';
import addData from '@/modules/ingestion/addData';
import cognifyDataset from '@/modules/datasets/cognifyDataset';
import getExplorationGraphUrl from '@/modules/exploration/getExplorationGraphUrl';
export default function Page() {
const onDataExplore = useCallback((dataset: { id: string }) => {
return getExplorationGraphUrl(dataset)
.then((explorationWindowUrl) => {
return {
url: explorationWindowUrl,
title: dataset.id,
};
});
}, []);
const finishWizard = useCallback(() => {
window.location.href = '/';
}, []);
return (
<WizardPage
onDataAdd={addData}
onDataCognify={cognifyDataset}
onDataExplore={onDataExplore}
onFinish={finishWizard}
/>
);

View file

@ -1 +0,0 @@
export { default as LoadingIndicator } from './LoadingIndicator';

View file

@ -1,2 +0,0 @@
export { default as Logo } from './Logo';
export { default as TextLogo } from './TextLogo';

View file

@ -1,3 +0,0 @@
export { default as Logo } from './Logo/Logo';
export { default as TextLogo } from './Logo/TextLogo';
export { default as LoadingIndicator } from './Loading/LoadingIndicator';

View file

@ -0,0 +1 @@
export { default as getExplorationGraphUrl } from './getExplorationGraphUrl';

View file

@ -30,7 +30,7 @@ interface DataViewProps {
}
export default function DataView({ datasetId, data, onClose, onDataAdd }: DataViewProps) {
const handleDataDelete = () => {};
// const handleDataDelete = () => {};
const [rawData, setRawData] = useState<ArrayBuffer | null>(null);
const [selectedData, setSelectedData] = useState<Data | null>(null);
@ -40,6 +40,8 @@ export default function DataView({ datasetId, data, onClose, onDataAdd }: DataVi
fetch(`http://0.0.0.0:8000/datasets/${datasetId}/data/${dataItem.id}/raw`)
.then((response) => response.arrayBuffer())
.then(setRawData);
document.body.click(); // Close the dropdown menu.
}, [datasetId]);
const resetDataPreview = useCallback(() => {
@ -54,10 +56,12 @@ export default function DataView({ datasetId, data, onClose, onDataAdd }: DataVi
return (
<Stack orientation="vertical" gap="4">
<Stack gap="2" orientation="horizontal" align="/end">
<UploadInput onChange={handleDataAdd}>
<Text>Add data</Text>
</UploadInput>
<GhostButton onClick={onClose}>
<div>
<UploadInput onChange={handleDataAdd}>
<Text>Add data</Text>
</UploadInput>
</div>
<GhostButton hugContent onClick={onClose}>
<CloseIcon />
</GhostButton>
</Stack>

View file

@ -0,0 +1,8 @@
.dataPreviewModal {
left: 5% !important;
padding: 0 !important;
max-width: 90% !important;
height: 80%;
top: 5% !important;
}

View file

@ -1,4 +1,6 @@
import { IFrameView } from '@/ui';
import { IFrameView } from '@/ui/Partials';
import { CloseIcon, GhostButton, Modal, Spacer, Stack, Text } from 'ohmy-ui';
import styles from './RawDataPreview.module.css';
interface RawDataPreviewProps {
fileName: string;
@ -12,11 +14,12 @@ export default function RawDataPreview({ fileName, rawData, onClose }: RawDataPr
const src = `data:application/pdf;base64,${arrayBufferToBase64(rawData)}`.replace(';', file_header + encodeURIComponent(fileName) + ';');
return (
<IFrameView
src={src}
title={fileName}
onClose={onClose}
/>
<Modal isOpen onClose={onClose} className={styles.dataPreviewModal}>
<Spacer horizontal="2" vertical="3" wrap>
<Text>{fileName}</Text>
</Spacer>
<IFrameView src={src} />
</Modal>
);
}

View file

@ -4,3 +4,13 @@
border-radius: var(--border-radius);
padding: 4px;
}
.explorerModal {
left: 5% !important;
padding: 0 !important;
max-width: 90% !important;
height: 80%;
top: 5% !important;
display: flex;
flex-direction: column;
}

View file

@ -1,10 +1,10 @@
import { useCallback, useState } from 'react';
import { IFrameView } from '@/ui';
import { useState } from 'react';
import Link from 'next/link';
import { Explorer } from '@/ui/Partials';
import StatusIcon from './StatusIcon';
import { LoadingIndicator } from '@/modules/app';
import { DropdownMenu, GhostButton, Stack, Text, UploadInput, CTAButton, useBoolean, NeutralButton } from "ohmy-ui";
import { LoadingIndicator } from '@/ui/App';
import { DropdownMenu, GhostButton, Stack, Text, CTAButton, useBoolean, Modal, Spacer } from "ohmy-ui";
import styles from "./DatasetsView.module.css";
import { SearchView } from '@/ui/Partials';
interface Dataset {
id: string;
@ -12,29 +12,18 @@ interface Dataset {
status: string;
}
const DatasetItem = GhostButton.mixClassName()("div")
interface ExplorationWindowConfig {
url: string;
title: string;
}
const DatasetItem = GhostButton.remix({ Component: 'div' });
interface DatasetsViewProps {
datasets: Dataset[];
onDataAdd: (dataset: Dataset, files: File[]) => void;
onDatasetClick: (dataset: Dataset) => void;
onDatasetDelete: (dataset: Dataset) => void;
onDatasetCognify: (dataset: Dataset) => Promise<void>;
onDatasetExplore: (dataset: Dataset) => Promise<string>;
}
export default function DatasetsView({
datasets,
onDatasetClick,
onDataAdd,
onDatasetCognify,
onDatasetDelete,
onDatasetExplore,
}: DatasetsViewProps) {
const {
value: isCognifyRunning,
@ -51,55 +40,18 @@ export default function DatasetsView({
.finally(() => enableCognifyRun());
}
// const handleDatasetDelete = (event: React.MouseEvent<HTMLButtonElement>, dataset: Dataset) => {
// event.stopPropagation();
// onDatasetDelete(dataset);
// }
const [explorationWindowProps, setExplorationWindowProps] = useState<ExplorationWindowConfig | null>(null);
const [dataset, setExplorationDataset] = useState<{ id: string } | null>(null);
const {
value: isExplorationWindowShown,
setTrue: showExplorationWindow,
setFalse: hideExplorationWindow,
} = useBoolean(false);
const openExplorationWindow = useCallback((explorationWindowProps: ExplorationWindowConfig) => {
setExplorationWindowProps(explorationWindowProps);
showExplorationWindow();
}, [showExplorationWindow]);
const {
value: isExploreLoading,
setTrue: startLoadingExplore,
setFalse: finishLoadingExplore,
} = useBoolean(false);
const handleExploreDataset = (event: React.MouseEvent<HTMLButtonElement>, dataset: Dataset) => {
event.stopPropagation();
startLoadingExplore();
onDatasetExplore(dataset)
.then((explorationWindowUrl) => {
openExplorationWindow({
url: explorationWindowUrl,
title: dataset.id,
});
})
.finally(() => finishLoadingExplore());
}
const handleDataAdd = (dataset: Dataset, files: File[]) => {
onDataAdd(dataset, files);
}
const {
value: isSearchWindowOpen,
setTrue: openSearchWindow,
setFalse: closeSearchWindow,
} = useBoolean(false);
const handleSearchDataset = (event: React.MouseEvent<HTMLButtonElement>, dataset: Dataset) => {
event.stopPropagation();
openSearchWindow();
setExplorationDataset(dataset);
showExplorationWindow();
}
return (
@ -114,23 +66,11 @@ export default function DatasetsView({
<DropdownMenu>
<Stack gap="1" className={styles.datasetMenu} orientation="vertical">
{dataset.status === 'DATASET_PROCESSING_FINISHED' ? (
<>
<CTAButton
onClick={(event: React.MouseEvent<HTMLButtonElement>) => handleExploreDataset(event, dataset)}
>
<Stack gap="2" orientation="horizontal" align="center/center">
<Text>Explore</Text>
{isExploreLoading && (
<LoadingIndicator />
)}
</Stack>
</CTAButton>
<NeutralButton
onClick={(event: React.MouseEvent<HTMLButtonElement>) => handleSearchDataset(event, dataset)}
>
<Text>Search</Text>
</NeutralButton>
</>
<CTAButton
onClick={(event: React.MouseEvent<HTMLButtonElement>) => handleExploreDataset(event, dataset)}
>
<Text>Explore</Text>
</CTAButton>
) : (
<CTAButton
onClick={(event: React.MouseEvent<HTMLButtonElement>) => handleCognifyDataset(event, dataset)}
@ -143,14 +83,11 @@ export default function DatasetsView({
</Stack>
</CTAButton>
)}
<UploadInput as={GhostButton} onChange={(files: File[]) => handleDataAdd(dataset, files)}>
<Text>Add data</Text>
</UploadInput>
{/* <NegativeButton
onClick={(event: React.MouseEvent<HTMLButtonElement>) => handleDatasetDelete(event, dataset)}
>
<Text>Delete</Text>
</NegativeButton> */}
<Link href="/wizard?step=add">
<GhostButton>
<Text>Add data</Text>
</GhostButton>
</Link>
</Stack>
</DropdownMenu>
</Stack>
@ -158,16 +95,12 @@ export default function DatasetsView({
</DatasetItem>
))}
</Stack>
{isSearchWindowOpen && (
<SearchView onClose={closeSearchWindow} />
)}
{isExplorationWindowShown && (
<IFrameView
src={explorationWindowProps!.url}
title={explorationWindowProps!.title}
onClose={hideExplorationWindow}
/>
)}
<Modal onClose={hideExplorationWindow} isOpen={isExplorationWindowShown} className={styles.explorerModal}>
<Spacer horizontal="2" vertical="3" wrap>
<Text>{dataset?.id}</Text>
</Spacer>
<Explorer dataset={dataset!} />
</Modal>
</>
);
}

View file

@ -0,0 +1,56 @@
.donut1 {
width: 106px;
height: 106px;
border-radius: 50%;
background: linear-gradient(90deg, #D82EB5 0.52%, #9245FD 103.83%);
}
.donut1.spin {
animation: rotate1 1s linear infinite;
}
.donut2 {
width: 76px;
height: 76px;
border-radius: 50%;
background: linear-gradient(90deg, #D82EB5 0.52%, #9245FD 103.83%);
position: relative;
left: 15px;
top: 15px;
}
.donut2.spin {
background: linear-gradient(270deg, #D82EB5 0.52%, #9245FD 103.83%);
animation: rotate1 1s linear infinite;
}
.donut3 {
width: 46px;
height: 46px;
border-radius: 50%;
background: linear-gradient(90deg, #D82EB5 0.52%, #9245FD 103.83%);
position: relative;
left: 15px;
top: 15px;
}
.donut3.spin {
animation: rotate1 1s linear infinite;
}
.dot {
width: 16px;
height: 16px;
border-radius: 50%;
background: #351A4B;
position: relative;
left: 15px;
top: 15px;
}
@keyframes rotate1 {
0% {
transform: rotate(0deg);
}
100% {
transform: rotate(360deg);
}
}

View file

@ -0,0 +1,16 @@
import classNames from 'classnames';
import styles from './CognifyLoadingIndicator.module.css';
function CognifyLoadingIndicator({ isLoading = true }) {
return (
<div className={classNames(styles.donut1, isLoading && styles.spin)}>
<div className={classNames(styles.donut2, isLoading && styles.spin)}>
<div className={classNames(styles.donut3, isLoading && styles.spin)}>
<div className={styles.dot} />
</div>
</div>
</div>
);
}
export default CognifyLoadingIndicator;

View file

@ -3,7 +3,7 @@
width: 16px;
height: 16px;
border-radius: 50%;
border: 2px solid white;
border: 2px solid var(--global-color-primary);
border-top-color: transparent;
border-bottom-color: transparent;
animation: spin 2s linear infinite;

View file

@ -10,6 +10,5 @@ export default function TextLogo({ width = 160, height = 42, color = 'currentCol
</linearGradient>
</defs>
</svg>
);
}

View file

@ -0,0 +1,29 @@
export default function TextLogo({ width = 285, height = 81, color = 'currentColor' }) {
return (
<svg width={width} height={height} viewBox="0 0 285 81" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M42.0964 46.4597C39.6678 49.6212 36.2632 51.8922 32.4114 52.92C28.5596 53.9479 24.4762 53.6749 20.7954 52.1436C17.1147 50.6123 14.0426 47.9083 12.0565 44.4517C10.0704 40.9951 9.2813 36.9793 9.81189 33.0282C10.3425 29.0771 12.163 25.4118 14.9907 22.6016C17.8184 19.7914 21.4949 17.9937 25.4493 17.4877C29.4036 16.9816 33.4144 17.7956 36.8586 19.8032" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M190.754 51.1936L199.5 35.4365L208.699 19.7656C205.249 17.7739 201.238 16.9762 197.289 17.4961C193.34 18.016 189.672 19.8246 186.856 22.6413C184.039 25.458 182.23 29.1253 181.71 33.0746C181.191 37.0239 181.988 41.0345 183.98 44.4842C185.972 47.9339 189.046 50.63 192.726 52.1544C196.406 53.6787 200.487 53.9462 204.334 52.9152C208.182 51.8842 211.582 49.6125 214.007 46.4522C214.701 45.5482 215.303 44.5859 215.811 43.5794" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M260.873 35.4365C260.873 39.4199 259.559 43.292 257.134 46.4522C254.709 49.6124 251.309 51.8842 247.461 52.9152C243.614 53.9461 239.533 53.6787 235.853 52.1543C232.173 50.63 229.099 47.9338 227.107 44.4841C225.115 41.0344 224.317 37.0239 224.837 33.0746C225.357 29.1253 227.166 25.4579 229.983 22.6413C232.799 19.8246 236.467 18.016 240.416 17.4961C244.365 16.9761 248.376 17.7739 251.825 19.7656L242.627 35.4365L233.881 51.1936" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<circle cx="112.945" cy="32.4206" r="15.2302" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M128.126 52.627C128.126 62.6207 120.025 70.7222 110.031 70.7222C104.317 70.7222 99.221 68.0734 95.9048 63.9365" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M128.175 17.1905L128.175 52.627" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M170.698 30.7619L170.698 52.4762" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M170.698 30.1587C170.698 22.9966 164.143 17.1905 156.056 17.1905" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M141.746 30.1587C141.746 22.9966 148.153 17.1905 156.056 17.1905" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<path d="M141.746 17.3413L141.746 52.627" stroke={color} strokeWidth="6.03175" strokeLinecap="round"/>
<circle cx="69.0635" cy="35.4365" r="18.0952" stroke="url(#paint0_linear_103_348)" strokeWidth="6.03175" strokeLinecap="round"/>
<path fillRule="evenodd" clipRule="evenodd" d="M69.0637 56.3968C67.3981 56.3968 66.0479 55.0466 66.0479 53.3809L66.0479 17.4921C66.0479 15.8264 67.3981 14.4762 69.0637 14.4762C70.7293 14.4762 72.0796 15.8264 72.0796 17.4921L72.0796 53.3809C72.0796 55.0466 70.7293 56.3968 69.0637 56.3968Z" fill="url(#paint1_linear_103_348)"/>
<defs>
<linearGradient id="paint0_linear_103_348" x1="50.8501" y1="13.1077" x2="87.277" y2="13.1084" gradientUnits="userSpaceOnUse">
<stop stopColor="#D82EB5"/>
<stop offset="1" stopColor="#9245FD"/>
</linearGradient>
<linearGradient id="paint1_linear_103_348" x1="72.0443" y1="56.5336" x2="72.1623" y2="14.3395" gradientUnits="userSpaceOnUse">
<stop stopColor="#D82EB5"/>
<stop offset="1" stopColor="#9245FD"/>
</linearGradient>
</defs>
</svg>
);
}

View file

@ -0,0 +1,4 @@
export { default as Logo } from './Logo/Logo';
export { default as TextLogo } from './Logo/TextLogo.v2';
export { default as LoadingIndicator } from './Loading/DefaultLoadingIndicator/LoadingIndicator';
export { default as CognifyLoadingIndicator } from './Loading/CognifyLoadingIndicator/CognifyLoadingIndicator';

View file

@ -1,12 +0,0 @@
.iFrameViewContainer {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 30%;
background: var(--global-background-default);
border-radius: var(--border-radius);
border: 1px solid white;
z-index: 10;
}

View file

@ -1,28 +0,0 @@
import { CloseIcon, GhostButton, Spacer, Stack, Text } from 'ohmy-ui';
import styles from './IFrameView.module.css';
interface IFrameViewProps {
src: string;
title: string;
onClose: () => void;
}
export default function IFrameView({ title, src, onClose }: IFrameViewProps) {
return (
<div className={styles.iFrameViewContainer}>
<Stack gap="between" align="center/" orientation="horizontal">
<Spacer horizontal="2">
<Text>{title}</Text>
</Spacer>
<GhostButton onClick={onClose}>
<CloseIcon />
</GhostButton>
</Stack>
<iframe
src={src}
width="100%"
height="100%"
/>
</div>
);
}

View file

@ -1,66 +1,7 @@
export default function SettingsIcon({ width = "32px", height = "32px", color = "white" }) {
export default function SettingsIcon({ width = 32, height = 33, color = "#E8EAED" }) {
return (
<svg fill={color} height={height} width={width} version="1.1" xmlns="http://www.w3.org/2000/svg"
viewBox="0 0 397.516 397.516" xmlSpace="preserve">
<g>
<path d="M126.32,156.454c-37.993,0-68.901,30.911-68.901,68.905c0,37.991,30.909,68.9,68.901,68.9s68.9-30.909,68.9-68.9
C195.22,187.365,164.311,156.454,126.32,156.454z M126.32,279.641c-29.932,0-54.283-24.351-54.283-54.281
c0-29.934,24.352-54.286,54.283-54.286s54.282,24.353,54.282,54.286C180.602,255.29,156.251,279.641,126.32,279.641z"/>
<path d="M241.133,193.697l-9.568-2.638c-1.085-0.299-2.955-2.038-3.333-3.102l-2.717-6.683l-0.152-0.346
c-0.483-1.028-0.382-3.607,0.179-4.597l4.819-8.491c3.36-5.921,2.264-14.015-2.549-18.824l-23.776-23.779
c-2.852-2.848-6.952-4.482-11.248-4.482c-2.723,0-5.341,0.669-7.57,1.935l-8.038,4.561c-0.324,0.184-1.251,0.458-2.478,0.458
c-1.061,0-1.766-0.207-1.991-0.316l-8.275-3.484l-0.348-0.136c-1.068-0.385-2.818-2.276-3.121-3.375l-2.719-9.851
c-1.81-6.563-8.307-11.511-15.113-11.511h-33.629c-6.807,0-13.303,4.949-15.11,11.508l-2.723,9.855
c-0.303,1.101-2.06,3.003-3.132,3.393l-8.905,3.768l-0.378,0.173c-0.223,0.11-0.926,0.318-1.988,0.318
c-1.202,0.001-2.109-0.267-2.429-0.448l-7.565-4.295c-2.231-1.266-4.851-1.936-7.575-1.936c-4.3,0-8.4,1.636-11.247,4.484
l-23.782,23.778c-4.812,4.813-5.906,12.904-2.546,18.822l4.736,8.343c0.565,0.998,0.677,3.584,0.198,4.613
c-1.323,2.844-4.967,8.298-6.713,9.848l-8.841,2.438C4.946,195.509,0,202.006,0,208.812v33.626c0,6.803,4.946,13.3,11.506,15.112
l9.568,2.641c1.088,0.3,2.96,2.038,3.338,3.101l2.945,7.17l0.149,0.338c0.484,1.024,0.39,3.586-0.169,4.568l-4.362,7.68
c-3.356,5.916-2.261,14.006,2.55,18.822l23.78,23.777c2.85,2.85,6.95,4.484,11.248,4.484l0,0c2.723,0,5.342-0.669,7.576-1.936
l7.361-4.177c0.327-0.186,1.26-0.461,2.492-0.461c1.062,0,1.769,0.206,1.995,0.315l8.39,3.522l0.357,0.139
c1.065,0.382,2.81,2.264,3.112,3.358l2.56,9.276c1.808,6.561,8.305,11.511,15.111,11.511h33.629
c6.806,0,13.303-4.948,15.113-11.511l2.558-9.279c0.3-1.087,2.038-2.957,3.099-3.335l7.735-3.188l0.355-0.158
c0.225-0.107,0.931-0.311,1.99-0.311c1.259,0,2.214,0.282,2.548,0.472l7.823,4.443c2.232,1.267,4.851,1.936,7.576,1.936
c4.3,0,8.4-1.636,11.248-4.485l23.778-23.777c4.814-4.812,5.91-12.904,2.549-18.825l-4.441-7.82
c-0.556-0.979-0.647-3.525-0.163-4.541l3.188-7.659l0.134-0.347c0.379-1.064,2.253-2.805,3.343-3.105l9.57-2.64
c6.559-1.812,11.505-8.309,11.505-15.112v-33.623C252.641,202.006,247.695,195.508,241.133,193.697z M237.247,243.459
l-9.568,2.64c-5.615,1.549-11.11,6.61-13.151,12.086l-2.914,7.023c-2.439,5.314-2.139,12.778,0.738,17.851l4.422,7.782
c0.124,0.31,0.021,1.075-0.152,1.31L192.875,315.9c-0.096,0.073-0.467,0.233-0.944,0.233c-0.22,0-0.366-0.046-0.357-0.03
l-7.824-4.443c-2.702-1.534-6.17-2.379-9.766-2.379c-2.072,0-5.137,0.288-8.082,1.641l-7.098,2.934
c-5.479,2.037-10.544,7.533-12.093,13.151l-2.544,9.234c-0.13,0.305-0.73,0.766-1.066,0.82l-33.553,0.002
c-0.331-0.045-0.946-0.513-1.064-0.78l-2.56-9.276c-1.546-5.609-6.598-11.106-12.064-13.157l-7.725-3.232
c-2.97-1.383-6.063-1.678-8.155-1.678c-3.572,0-7.02,0.841-9.707,2.366l-7.32,4.155c-0.036,0.015-0.178,0.053-0.402,0.053
c-0.478,0-0.85-0.161-0.913-0.204l-23.747-23.741c-0.204-0.268-0.309-1.036-0.206-1.304l4.36-7.676
c2.873-5.058,3.185-12.52,0.766-17.839l-2.701-6.555c-2.037-5.48-7.535-10.548-13.153-12.097l-9.521-2.625
c-0.309-0.132-0.778-0.748-0.822-1.035l-0.002-33.581c0.045-0.333,0.514-0.949,0.777-1.067l9.563-2.637
c8.015-2.207,15.287-17.422,15.357-17.572c2.473-5.313,2.164-12.878-0.737-17.994l-4.718-8.307
c-0.124-0.312-0.021-1.076,0.15-1.309l23.749-23.748c0.096-0.073,0.467-0.232,0.943-0.232c0.222,0,0.363,0.041,0.359,0.03
l7.562,4.292c2.674,1.52,6.101,2.357,9.649,2.357c2.116,0,5.241-0.303,8.236-1.722l8.238-3.494
c5.445-2.071,10.479-7.573,12.021-13.166l2.709-9.813c0.131-0.308,0.746-0.776,1.032-0.819l33.584-0.002
c0.333,0.045,0.948,0.514,1.066,0.781l2.719,9.85c1.545,5.604,6.591,11.105,12.048,13.164l7.61,3.193
c2.975,1.39,6.073,1.686,8.17,1.686c3.568,0,7.012-0.84,9.694-2.363l7.995-4.538c0.036-0.015,0.176-0.051,0.396-0.051
c0.48,0,0.853,0.161,0.914,0.202l23.744,23.744c0.203,0.267,0.306,1.032,0.201,1.304l-4.819,8.493
c-2.868,5.056-3.189,12.511-0.79,17.823l2.489,6.102c2.034,5.487,7.535,10.562,13.154,12.11l9.523,2.623
c0.309,0.132,0.777,0.748,0.82,1.036l0.002,33.581C237.98,242.726,237.511,243.342,237.247,243.459z"/>
<path d="M393.377,112.81l-6.573-1.953c-2.321-0.688-4.846-3.132-5.611-5.428l-1.713-4.439c-0.983-2.211-0.778-5.725,0.459-7.805
l3.443-5.806c1.236-2.08,0.875-5.212-0.8-6.958L366.48,63.675c-1.679-1.746-4.794-2.232-6.922-1.076l-5.609,3.038
c-2.13,1.154-5.636,1.198-7.793,0.097l-5.418-2.399c-2.262-0.866-4.599-3.496-5.199-5.843l-1.745-6.844
c-0.598-2.345-3.066-4.304-5.487-4.352l-23.232-0.457c-2.42-0.048-4.965,1.814-5.654,4.133l-2.013,6.77
c-0.691,2.321-3.129,4.861-5.42,5.645l-5.954,2.389c-2.19,1.027-5.692,0.856-7.772-0.38l-5.166-3.07
c-2.083-1.237-5.215-0.876-6.96,0.805l-16.751,16.1c-1.742,1.676-2.226,4.793-1.073,6.921l3.159,5.831
c1.153,2.13,1.23,5.645,0.169,7.813c-1.061,2.167-5.21,8.66-7.557,9.256l-6.643,1.693c-2.345,0.599-4.305,3.07-4.353,5.49
l-0.456,23.228c-0.047,2.422,1.814,4.965,4.134,5.655l6.573,1.954c2.322,0.688,4.849,3.132,5.616,5.43l1.852,4.759
c0.992,2.211,0.795,5.721-0.444,7.802l-3.113,5.241c-1.238,2.084-0.875,5.215,0.803,6.961l16.104,16.746
c1.678,1.747,4.793,2.232,6.924,1.078l5.14-2.785c2.128-1.155,5.638-1.197,7.796-0.101l5.501,2.428
c2.261,0.864,4.605,3.488,5.2,5.837l1.642,6.442c0.598,2.348,3.067,4.307,5.488,4.354l23.231,0.455
c2.422,0.049,4.964-1.811,5.654-4.133l1.894-6.373c0.687-2.323,3.131-4.851,5.43-5.617l5.146-2.013
c2.207-0.997,5.719-0.802,7.798,0.436l5.342,3.172c2.082,1.238,5.215,0.876,6.958-0.804l16.751-16.1
c1.744-1.68,2.229-4.794,1.074-6.921l-2.962-5.467c-1.152-2.129-1.21-5.644-0.123-7.808l2.192-5.01
c0.86-2.266,3.482-4.609,5.829-5.206l6.645-1.693c2.343-0.599,4.305-3.066,4.352-5.488l0.457-23.229
C397.557,116.047,395.695,113.5,393.377,112.81z M314.236,170.826c-23.495-0.462-42.171-19.886-41.709-43.381
c0.462-23.5,19.886-42.176,43.381-41.715c23.497,0.463,42.172,19.889,41.71,43.387
C357.156,152.614,337.733,171.288,314.236,170.826z"/>
</g>
<svg width={width} height={height} viewBox="0 0 54 56" fill="none" xmlns="http://www.w3.org/2000/svg">
<path d="M21.2482 55.75L20.1336 46.8322C19.1495 46.5357 18.0853 46.0691 16.9408 45.4324C15.7964 44.7962 14.8231 44.1145 14.0209 43.3874L5.79611 46.8854L0.0449219 36.8646L7.15432 31.5042C7.06336 30.9472 6.98833 30.3731 6.92923 29.7822C6.86962 29.1912 6.83982 28.6169 6.83982 28.0594C6.83982 27.5414 6.86962 26.9969 6.92923 26.426C6.98833 25.8545 7.06336 25.2111 7.15432 24.4958L0.0449219 19.1354L5.79611 9.23329L13.9615 12.672C14.8824 11.9053 15.8786 11.2136 16.9501 10.5969C18.021 9.98023 19.0624 9.50385 20.0743 9.16777L21.2482 0.25H32.7522L33.8668 9.22713C35.0487 9.64235 36.0932 10.1187 37.0002 10.6562C37.9072 11.1938 38.8412 11.8657 39.8022 12.672L48.2043 9.23329L53.9555 19.1354L46.6087 24.6739C46.7788 25.31 46.8738 25.8941 46.8939 26.426C46.9134 26.9573 46.9232 27.482 46.9232 28C46.9232 28.4784 46.9034 28.9833 46.8638 29.5147C46.8242 30.0466 46.7333 30.69 46.5909 31.4449L53.819 36.8646L48.0678 46.8854L39.8022 43.328C38.8412 44.1343 37.8746 44.826 36.9023 45.4031C35.93 45.9802 34.9182 46.4368 33.8668 46.7729L32.7522 55.75H21.2482ZM23.9169 52.6667H29.9471L31.0856 44.3178C32.6391 43.9067 34.0374 43.3424 35.2805 42.625C36.5241 41.9076 37.7901 40.9243 39.0784 39.675L46.769 42.9542L49.8346 37.7125L43.0867 32.6427C43.3437 31.765 43.5138 30.9577 43.597 30.2208C43.6797 29.4833 43.7211 28.7431 43.7211 28C43.7211 27.2173 43.6797 26.4771 43.597 25.7792C43.5138 25.0819 43.3437 24.3141 43.0867 23.476L49.9533 18.2875L46.8877 13.0458L39.019 16.3427C38.0863 15.319 36.8599 14.3593 35.3398 13.4636C33.8203 12.5684 32.3824 11.9746 31.0263 11.6822L30.0835 3.33333H23.9346L22.9741 11.6229C21.4206 11.9548 19.9925 12.4895 18.6898 13.227C17.3876 13.9639 16.0921 14.9768 14.8033 16.2656L7.11269 13.0458L4.04709 18.2875L10.7356 23.2802C10.4787 23.9719 10.2988 24.7229 10.196 25.5333C10.0932 26.3437 10.0419 27.1857 10.0419 28.0594C10.0419 28.842 10.0932 29.6187 10.196 30.3896C10.2988 31.1604 10.4589 31.9115 10.6763 32.6427L4.04709 37.7125L7.11269 42.9542L14.7439 39.7167C15.9536 40.9382 17.2096 41.9177 18.5118 42.6551C19.8145 43.392 21.2822 43.966 22.9148 44.3771L23.9169 52.6667ZM26.9169 35.7083C29.0676 35.7083 30.8901 34.9611 32.3845 33.4668C33.8783 31.9729 34.6253 30.1506 34.6253 28C34.6253 25.8494 33.8783 24.0271 32.3845 22.5333C30.8901 21.0389 29.0676 20.2917 26.9169 20.2917C24.755 20.2917 22.9297 21.0389 21.4409 22.5333C19.9527 24.0271 19.2086 25.8494 19.2086 28C19.2086 30.1506 19.9527 31.9729 21.4409 33.4668C22.9297 34.9611 24.755 35.7083 26.9169 35.7083Z" fill={color} />
</svg>
);
}
}

View file

@ -0,0 +1,5 @@
.divider {
height: 1px;
background: linear-gradient(90deg, #D82EB5 0.52%, #9245FD 103.83%);
}

View file

@ -0,0 +1,7 @@
import styles from './Divider.module.css';
export default function Divider() {
return (
<div className={styles.divider} />
);
}

View file

@ -0,0 +1 @@
export { default as Divider } from './Divider/Divider';

View file

@ -0,0 +1,21 @@
.explorer {
flex: 1;
min-height: 100%;
flex-direction: column;
}
.explorerContent {
flex: 1;
}
.graphExplorer {
width: 65%;
overflow: hidden;
border-radius: var(--border-radius);
}
.chat {
width: 35%;
display: flex;
}

View file

@ -0,0 +1,50 @@
import { useCallback, useEffect, useState } from 'react';
import classNames from 'classnames';
import { Spacer, Stack } from 'ohmy-ui';
import { getExplorationGraphUrl } from '@/modules/exploration';
import { IFrameView, SearchView } from '@/ui/Partials';
import { LoadingIndicator } from '@/ui/App';
import styles from './Explorer.module.css';
interface ExplorerProps {
dataset: { id: string };
className?: string;
style?: React.CSSProperties;
}
export default function Explorer({ dataset, className, style }: ExplorerProps) {
const [graphUrl, setGraphUrl] = useState<string | null>(null);
const exploreData = useCallback(() => {
getExplorationGraphUrl(dataset)
.then((graphUrl) => {
setGraphUrl(graphUrl);
});
}, [dataset]);
useEffect(() => {
exploreData();
}, [exploreData]);
return (
<Stack
gap="6"
style={style}
orientation="horizontal"
className={classNames(styles.explorerContent, className)}
>
<div className={styles.graphExplorer}>
{!graphUrl ? (
<Spacer horizontal="2" wrap>
<LoadingIndicator />
</Spacer>
) : (
<IFrameView src={graphUrl} />
)}
</div>
<div className={styles.chat}>
<SearchView />
</div>
</Stack>
)
}

View file

@ -1,7 +1,7 @@
import Link from 'next/link';
import { Stack } from 'ohmy-ui';
import { DiscordIcon, GithubIcon } from '@/ui/Icons';
import { TextLogo } from '@/modules/app';
// import { TextLogo } from '@/ui/App';
import styles from './Footer.module.css';
export default function Footer() {
@ -9,7 +9,7 @@ export default function Footer() {
<footer className={styles.footer}>
<Stack orientation="horizontal" gap="between">
<div className={styles.leftSide}>
<TextLogo width={92} height={24} />
{/* <TextLogo width={92} height={24} /> */}
</div>
<div className={styles.rightSide}>
<Link target="_blank" href="https://github.com/topoteretes/cognee">

View file

@ -0,0 +1,13 @@
interface IFrameViewProps {
src: string;
}
export default function IFrameView({ src }: IFrameViewProps) {
return (
<iframe
src={src}
width="100%"
height="100%"
/>
);
}

View file

@ -1,21 +1,10 @@
.searchViewContainer {
position: absolute;
top: 0;
bottom: 0;
right: 0;
left: 30%;
background: var(--global-background-default);
border-radius: var(--border-radius);
border: 1px solid white;
z-index: 10;
}
.searchContainer {
flex: 1;
padding: 16px;
border-top: 2px solid white;
overflow: hidden;
border: 1px solid white;
background: var(--global-background-default);
border-radius: var(--border-radius);
}
.messagesContainer {

View file

@ -1,12 +1,8 @@
import { CTAButton, CloseIcon, GhostButton, Input, Spacer, Stack, Text, DropdownSelect } from 'ohmy-ui';
import styles from './SearchView.module.css';
import { useCallback, useState } from 'react';
import { v4 } from 'uuid';
import classNames from 'classnames';
interface SearchViewProps {
onClose: () => void;
}
import { useCallback, useState } from 'react';
import { CTAButton, Stack, Text, DropdownSelect, TextArea, useBoolean } from 'ohmy-ui';
import styles from './SearchView.module.css';
interface Message {
id: string;
@ -14,29 +10,31 @@ interface Message {
text: string;
}
export default function SearchView({ onClose }: SearchViewProps) {
interface SelectOption {
value: string;
label: string;
}
export default function SearchView() {
const [messages, setMessages] = useState<Message[]>([]);
const [inputValue, setInputValue] = useState<string>("");
const handleInputChange = useCallback((event: React.ChangeEvent<HTMLInputElement>) => {
const handleInputChange = useCallback((event: React.ChangeEvent<HTMLTextAreaElement>) => {
setInputValue(event.target.value);
}, []);
const searchOptions = [{
value: 'SIMILARITY',
label: 'Similarity',
}, {
value: 'NEIGHBOR',
label: 'Neighbor',
label: 'Look for similar graph nodes',
}, {
value: 'SUMMARY',
label: 'Summary',
label: 'Get a summary related to query',
}, {
value: 'ADJACENT',
label: 'Adjacent',
label: 'Look for graph node\'s neighbors',
}, {
value: 'CATEGORIES',
label: 'Categories',
label: 'Search by categories (Comma separated categories)',
}];
const [searchType, setSearchType] = useState(searchOptions[0]);
@ -78,43 +76,39 @@ export default function SearchView({ onClose }: SearchViewProps) {
})
}, [inputValue, searchType]);
const {
value: isInputExpanded,
setTrue: expandInput,
setFalse: contractInput,
} = useBoolean(false);
return (
<Stack className={styles.searchViewContainer}>
<Stack gap="between" align="center/" orientation="horizontal">
<Spacer horizontal="2">
<Text>Search</Text>
</Spacer>
<GhostButton onClick={onClose}>
<CloseIcon />
</GhostButton>
</Stack>
<Stack className={styles.searchContainer}>
<div className={styles.messagesContainer}>
<Stack gap="2" className={styles.messages} align="end">
{messages.map((message) => (
<Text
key={message.id}
className={classNames(styles.message, {
[styles.userMessage]: message.user === "user",
})}
>
{message.text}
</Text>
))}
</Stack>
</div>
<form onSubmit={handleSearchSubmit}>
<Stack orientation="horizontal" gap="2">
<DropdownSelect
value={searchType}
options={searchOptions}
onChange={setSearchType}
/>
<Input value={inputValue} onChange={handleInputChange} name="searchInput" placeholder="Search" />
<CTAButton type="submit">Search</CTAButton>
</Stack>
</form>
</Stack>
<DropdownSelect<SelectOption>
value={searchType}
options={searchOptions}
onChange={setSearchType}
/>
<div className={styles.messagesContainer}>
<Stack gap="2" className={styles.messages} align="end">
{messages.map((message) => (
<Text
key={message.id}
className={classNames(styles.message, {
[styles.userMessage]: message.user === "user",
})}
>
{message.text}
</Text>
))}
</Stack>
</div>
<form onSubmit={handleSearchSubmit}>
<Stack orientation="horizontal" align="end/" gap="2">
<TextArea style={{ height: isInputExpanded ? '128px' : '38px' }} onFocus={expandInput} onBlur={contractInput} value={inputValue} onChange={handleInputChange} name="searchInput" placeholder="Search" />
<CTAButton hugContent type="submit">Search</CTAButton>
</Stack>
</form>
</Stack>
);
}

View file

@ -0,0 +1,187 @@
import { useCallback, useEffect, useState } from 'react';
import {
CTAButton,
DropdownSelect,
FormGroup,
FormInput,
FormLabel,
H3,
Input,
Spacer,
Stack,
useBoolean,
} from 'ohmy-ui';
import { LoadingIndicator } from '@/ui/App';
interface SelectOption {
label: string;
value: string;
}
export default function Settings({ onDone = () => {}, submitButtonText = 'Save' }) {
const [llmConfig, setLLMConfig] = useState<{
apiKey: string;
model: SelectOption;
models: {
openai: SelectOption[];
ollama: SelectOption[];
anthropic: SelectOption[];
};
provider: SelectOption;
providers: SelectOption[];
}>();
const [vectorDBConfig, setVectorDBConfig] = useState<{
url: string;
apiKey: string;
provider: SelectOption;
options: SelectOption[];
}>();
const {
value: isSaving,
setTrue: startSaving,
setFalse: stopSaving,
} = useBoolean(false);
const saveConfig = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newVectorConfig = {
provider: vectorDBConfig?.provider.value,
url: event.target.vectorDBUrl.value,
apiKey: event.target.vectorDBApiKey.value,
};
const newLLMConfig = {
provider: llmConfig?.provider.value,
model: llmConfig?.model.value,
apiKey: event.target.llmApiKey.value,
};
startSaving();
fetch('http://0.0.0.0:8000/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
llm: newLLMConfig,
vectorDB: newVectorConfig,
}),
})
.then(() => {
onDone();
})
.finally(() => stopSaving());
};
const handleVectorDBChange = useCallback((newVectorDBProvider: SelectOption) => {
setVectorDBConfig((config) => {
if (config?.provider !== newVectorDBProvider) {
return {
...config,
provider: newVectorDBProvider,
url: '',
apiKey: '',
};
}
return config;
});
}, []);
const handleLLMProviderChange = useCallback((newLLMProvider: SelectOption) => {
setLLMConfig((config) => {
if (config?.provider !== newLLMProvider) {
return {
...config,
provider: newLLMProvider,
model: config?.models[newLLMProvider.value][0],
apiKey: '',
};
}
return config;
});
}, []);
const handleLLMModelChange = useCallback((newLLMModel: SelectOption) => {
setLLMConfig((config) => {
if (config?.model !== newLLMModel) {
return {
...config,
model: newLLMModel,
};
}
return config;
});
}, []);
useEffect(() => {
const fetchConfig = async () => {
const response = await fetch('http://0.0.0.0:8000/settings');
const settings = await response.json();
if (!settings.llm.model) {
settings.llm.model = settings.llm.models[settings.llm.provider.value][0];
}
setLLMConfig(settings.llm);
setVectorDBConfig(settings.vectorDB);
};
fetchConfig();
}, []);
return (
<form onSubmit={saveConfig} style={{ width: '100%' }}>
<Stack gap="4" orientation="vertical">
<Stack gap="4" orientation="vertical">
<FormGroup orientation="vertical" align="center/" gap="2">
<FormLabel>LLM provider:</FormLabel>
<DropdownSelect
value={llmConfig?.provider || null}
options={llmConfig?.providers || []}
onChange={handleLLMProviderChange}
/>
</FormGroup>
<FormGroup orientation="vertical" align="center/" gap="2">
<FormLabel>LLM model:</FormLabel>
<DropdownSelect
value={llmConfig?.model || null}
options={llmConfig?.provider ? llmConfig?.models[llmConfig?.provider.value] : []}
onChange={handleLLMModelChange}
/>
</FormGroup>
<FormInput>
<Input defaultValue={llmConfig?.apiKey} name="llmApiKey" placeholder="LLM API key" />
</FormInput>
</Stack>
<Stack gap="2" orientation="vertical">
<FormGroup orientation="vertical" align="center/" gap="2">
<FormLabel>Vector DB provider:</FormLabel>
<DropdownSelect
value={vectorDBConfig?.provider || null}
options={vectorDBConfig?.options || []}
onChange={handleVectorDBChange}
/>
</FormGroup>
<FormInput>
<Input defaultValue={vectorDBConfig?.url} name="vectorDBUrl" placeholder="Vector DB instance url" />
</FormInput>
<FormInput>
<Input defaultValue={vectorDBConfig?.apiKey} name="vectorDBApiKey" placeholder="Vector DB API key" />
</FormInput>
<Stack align="/end">
<Spacer top="2">
<CTAButton type="submit">
<Stack gap="2" orientation="vertical" align="center/">
{submitButtonText}
{isSaving && <LoadingIndicator />}
</Stack>
</CTAButton>
</Spacer>
</Stack>
</Stack>
</Stack>
</form>
)
}

View file

@ -1,177 +1,10 @@
import { CTAButton, DropdownSelect, FormGroup, FormInput, FormLabel, H2, H3, Input, Modal, Spacer, Stack, useBoolean } from 'ohmy-ui';
import { useCallback, useEffect, useState } from 'react';
interface SelectOption {
label: string;
value: string;
}
import { Modal } from 'ohmy-ui';
import Settings from './Settings';
export default function SettingsModal({ isOpen = false, onClose = () => {} }) {
const [llmConfig, setLLMConfig] = useState<{
apiKey: string;
model: SelectOption;
models: {
openai: SelectOption[];
ollama: SelectOption[];
anthropic: SelectOption[];
};
provider: SelectOption;
providers: SelectOption[];
}>();
const [vectorDBConfig, setVectorDBConfig] = useState<{
url: string;
apiKey: string;
provider: SelectOption;
options: SelectOption[];
}>();
const {
value: isSaving,
setTrue: startSaving,
setFalse: stopSaving,
} = useBoolean(false);
const saveConfig = (event: React.FormEvent<HTMLFormElement>) => {
event.preventDefault();
const newVectorConfig = {
provider: vectorDBConfig?.provider.value,
url: event.target.vectorDBUrl.value,
apiKey: event.target.vectorDBApiKey.value,
};
const newLLMConfig = {
provider: llmConfig?.provider.value,
model: llmConfig?.model.value,
apiKey: event.target.llmApiKey.value,
};
startSaving();
fetch('http://0.0.0.0:8000/settings', {
method: 'POST',
headers: {
'Content-Type': 'application/json',
},
body: JSON.stringify({
llm: newLLMConfig,
vectorDB: newVectorConfig,
}),
})
.then(() => {
onClose();
})
.finally(() => stopSaving());
};
const handleVectorDBChange = useCallback((newVectorDBProvider: SelectOption) => {
setVectorDBConfig((config) => {
if (config?.provider !== newVectorDBProvider) {
return {
...config,
provider: newVectorDBProvider,
url: '',
apiKey: '',
};
}
return config;
});
}, []);
const handleLLMProviderChange = useCallback((newLLMProvider: SelectOption) => {
setLLMConfig((config) => {
if (config?.provider !== newLLMProvider) {
return {
...config,
provider: newLLMProvider,
model: config?.models[newLLMProvider.value][0],
apiKey: '',
};
}
return config;
});
}, []);
const handleLLMModelChange = useCallback((newLLMModel: SelectOption) => {
setLLMConfig((config) => {
if (config?.model !== newLLMModel) {
return {
...config,
model: newLLMModel,
};
}
return config;
});
}, []);
useEffect(() => {
const fetchVectorDBChoices = async () => {
const response = await fetch('http://0.0.0.0:8000/settings');
const settings = await response.json();
if (!settings.llm.model) {
settings.llm.model = settings.llm.models[settings.llm.provider.value][0];
}
setLLMConfig(settings.llm);
setVectorDBConfig(settings.vectorDB);
};
isOpen && fetchVectorDBChoices();
}, [isOpen]);
return (
<Modal isOpen={isOpen} onClose={onClose}>
<Stack gap="8" orientation="vertical" align="center/">
<H2>Settings</H2>
<form onSubmit={saveConfig} style={{ width: '100%' }}>
<Stack gap="4" orientation="vertical">
<Stack gap="2" orientation="vertical">
<H3>LLM Config</H3>
<FormGroup orientation="horizontal" align="center/" gap="4">
<FormLabel>LLM provider:</FormLabel>
<DropdownSelect
value={llmConfig?.provider}
options={llmConfig?.providers}
onChange={handleLLMProviderChange}
/>
</FormGroup>
<FormGroup orientation="horizontal" align="center/" gap="4">
<FormLabel>LLM model:</FormLabel>
<DropdownSelect
value={llmConfig?.model}
options={llmConfig?.provider ? llmConfig?.models[llmConfig?.provider.value] : []}
onChange={handleLLMModelChange}
/>
</FormGroup>
<FormInput>
<Input defaultValue={llmConfig?.apiKey} name="llmApiKey" placeholder="LLM API key" />
</FormInput>
</Stack>
<Stack gap="2" orientation="vertical">
<H3>Vector Database Config</H3>
<FormGroup orientation="horizontal" align="center/" gap="4">
<FormLabel>Vector DB provider:</FormLabel>
<DropdownSelect
value={vectorDBConfig?.provider}
options={vectorDBConfig?.options}
onChange={handleVectorDBChange}
/>
</FormGroup>
<FormInput>
<Input defaultValue={vectorDBConfig?.url} name="vectorDBUrl" placeholder="Vector DB instance url" />
</FormInput>
<FormInput>
<Input defaultValue={vectorDBConfig?.apiKey} name="vectorDBApiKey" placeholder="Vector DB API key" />
</FormInput>
<Stack align="/end">
<Spacer top="2">
<CTAButton type="submit">Save</CTAButton>
</Spacer>
</Stack>
</Stack>
</Stack>
</form>
</Stack>
<Settings onDone={onClose} />
</Modal>
)
}

View file

@ -0,0 +1,23 @@
.wizardContent {
width: 100%;
max-width: 400px;
background: linear-gradient(90deg, #D82EB5 0.52%, #9245FD 103.83%);
padding: 24px;
margin: 0 auto;
position: relative;
}
.wizardContent::before {
content: '';
width: calc(100% - 6px);
height: calc(100% - 6px);
max-width: 394px;
position: absolute;
top: 3px;
left: 3px;
background-color: #351A4B;
}
.wizardContent > * {
position: relative;
}

View file

@ -0,0 +1,6 @@
import { withStyles } from 'ohmy-ui';
import styles from './WizardContent.module.css';
const WizardContent = withStyles<{ children: React.ReactNode }>('div', { className: styles.wizardContent });
export default WizardContent;

View file

@ -0,0 +1,11 @@
import { H1 } from 'ohmy-ui';
interface WizardHeadingProps {
children: React.ReactNode;
}
export default function WizardHeading({ children, ...props }: WizardHeadingProps) {
return (
<H1 {...props} align="center" size="small" style={{ color: '#40A9FF' }}>{children}</H1>
);
}

View file

@ -0,0 +1,2 @@
export { default as WizardHeading } from './WizardHeading';
export { default as WizardContent } from './WizardContent/WizardContent';

View file

@ -1,3 +1,5 @@
export { default as Footer } from './Footer/Footer';
export { default as SettingsModal } from './SettingsModal/SettingsModal';
export { default as SearchView } from './SearchView/SearchView';
export { default as IFrameView } from './IFrameView/IFrameView';
export { default as Explorer } from './Explorer/Explorer';

View file

@ -1 +0,0 @@
export { default as IFrameView } from './IFrameView/IFrameView';

View file

@ -4,7 +4,7 @@ import aiohttp
import uvicorn
import json
import logging
from typing import Dict, Any, List, Union, Optional
from typing import Dict, Any, List, Union, Optional, Literal
from typing_extensions import Annotated
from fastapi import FastAPI, HTTPException, Form, File, UploadFile, Query
from fastapi.responses import JSONResponse, FileResponse
@ -106,9 +106,10 @@ async def get_dataset_data(dataset_id: str):
async def get_dataset_status(datasets: Annotated[List[str], Query(alias="dataset")] = None):
from cognee import datasets as cognee_datasets
datasets_statuses = cognee_datasets.get_status(datasets)
return JSONResponse(
status_code=200,
content=datasets_statuses
status_code = 200,
content = { dataset["data_id"]: dataset["status"] for dataset in datasets_statuses },
)
@app.get("/datasets/{dataset_id}/data/{data_id}/raw", response_class=FileResponse)

View file

@ -30,6 +30,8 @@ from cognee.shared.data_models import ChunkStrategy, KnowledgeGraph
from cognee.utils import send_telemetry
from cognee.modules.tasks import create_task_status_table, update_task_status
from cognee.shared.SourceCodeGraph import SourceCodeGraph
from asyncio import Lock
from cognee.modules.tasks import get_task_status
from cognee.base_config import get_base_config
from cognee.infrastructure.data.chunking.config import get_chunk_config
from cognee.modules.cognify.config import get_cognify_config
@ -55,6 +57,8 @@ USER_ID = "default_user"
logger = logging.getLogger("cognify")
update_status_lock = Lock()
async def cognify(datasets: Union[str, List[str]] = None):
"""This function is responsible for the cognitive processing of the content."""
# Has to be loaded in advance, multithreading doesn't work without it.
@ -73,10 +77,22 @@ async def cognify(datasets: Union[str, List[str]] = None):
awaitables = []
async def handle_cognify_task(dataset_name: str):
async with update_status_lock:
task_status = get_task_status([dataset_name])
if task_status == "DATASET_PROCESSING_STARTED":
logger.error(f"Dataset {dataset_name} is already being processed.")
return
update_task_status(dataset_name, "DATASET_PROCESSING_STARTED")
await cognify(dataset_name)
update_task_status(dataset_name, "DATASET_PROCESSING_FINISHED")
# datasets is a list of dataset names
if isinstance(datasets, list):
for dataset in datasets:
awaitables.append(cognify(dataset))
for dataset_name in datasets:
awaitables.append(handle_cognify_task(dataset_name))
graphs = await asyncio.gather(*awaitables)
return graphs[0]
@ -140,8 +156,6 @@ async def cognify(datasets: Union[str, List[str]] = None):
file_count = 0
files_batch = []
update_task_status(dataset_name, "DATASET_PROCESSING_STARTED")
for (dataset_name, files) in dataset_files:
for file_metadata in files:
graph_topology = graph_config.graph_model
@ -169,8 +183,6 @@ async def cognify(datasets: Union[str, List[str]] = None):
if len(files_batch) > 0:
await process_batch(files_batch)
update_task_status(dataset_name, "DATASET_PROCESSING_FINISHED")
return graph_client.graph

View file

@ -1,6 +1,4 @@
""" This module is used to set the configuration of the system."""
from cognee.infrastructure import infrastructure_config
from cognee.base_config import get_base_config
from cognee.infrastructure.databases.graph.config import get_graph_config
from cognee.infrastructure.data.chunking.config import get_chunk_config

View file

@ -1,6 +1,6 @@
from duckdb import CatalogException
from cognee.modules.discovery import discover_directory_datasets
from cognee.infrastructure import infrastructure_config
from cognee.modules.tasks import get_task_status
from cognee.infrastructure.databases.relational.config import get_relationaldb_config
relational_config = get_relationaldb_config()
@ -25,11 +25,8 @@ class datasets():
@staticmethod
def get_status(dataset_ids: list[str]) -> dict:
db = relational_config.db_engine
try:
return db.get_data("cognee_task_status", {
"data_id": dataset_ids
})
return get_task_status(dataset_ids)
except CatalogException:
return {}

View file

@ -2,20 +2,13 @@ from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict
from cognee.root_dir import get_absolute_path
from cognee.shared.data_models import MonitoringTool
# Monitoring tool
class BaseConfig(BaseSettings):
system_root_directory: str = get_absolute_path(".cognee_system")
data_root_directory: str = get_absolute_path(".data")
monitoring_tool: object = MonitoringTool.LANGFUSE
model_config = SettingsConfigDict(env_file = ".env", extra = "allow")
def to_dict(self) -> dict:
@ -27,4 +20,4 @@ class BaseConfig(BaseSettings):
@lru_cache
def get_base_config():
return BaseConfig()
return BaseConfig()

View file

@ -3,13 +3,10 @@ import os
from cognee.config import Config
from .data.chunking.config import get_chunk_config
from .databases.relational import DuckDBAdapter, DatabaseEngine
from .databases.vector.vector_db_interface import VectorDBInterface
# from .databases.vector.embeddings.DefaultEmbeddingEngine import DefaultEmbeddingEngine
from .databases.relational import DatabaseEngine
from .llm.llm_interface import LLMInterface
from .llm.get_llm_client import get_llm_client
from .files.storage import LocalStorage
from .data.chunking.DefaultChunkEngine import DefaultChunkEngine
from ..shared.data_models import GraphDBType, DefaultContentPrediction, KnowledgeGraph, SummarizedContent, \
LabeledContent, DefaultCognitiveLayer
@ -27,8 +24,6 @@ class InfrastructureConfig():
data_root_directory: str = config.data_root_directory
llm_provider: str = config.llm_provider
database_engine: DatabaseEngine = None
vector_engine: VectorDBInterface = None
vector_engine_choice: str = None
graph_engine: GraphDBType = None
llm_engine: LLMInterface = None
classification_model = None
@ -82,9 +77,6 @@ class InfrastructureConfig():
if self.intra_layer_score_treshold is None:
self.intra_layer_score_treshold = config.intra_layer_score_treshold
# if self.embedding_engine is None:
# self.embedding_engine = DefaultEmbeddingEngine()
if self.connect_documents is None:
self.connect_documents = config.connect_documents
@ -106,46 +98,13 @@ class InfrastructureConfig():
self.database_directory_path = self.system_root_directory + "/" + relational.db_path
if (config_entity is None or config_entity == "database_file_path") and self.database_file_path is None:
self.database_file_path = self.system_root_directory + "/" + relational.db_path + "/" + relational.db_name
if (config_entity is None or config_entity == "vector_engine") and self.vector_engine is None:
try:
from .databases.vector.weaviate_db import WeaviateAdapter
config.load()
if config.weaviate_url is None and config.weaviate_api_key is None:
raise EnvironmentError("Weaviate is not configured!")
self.vector_engine = WeaviateAdapter(
config.weaviate_url,
config.weaviate_api_key,
embedding_engine = self.embedding_engine
)
self.vector_engine_choice = "weaviate"
except (EnvironmentError, ModuleNotFoundError):
config.load()
if config.qdrant_url and config.qdrant_api_key:
from .databases.vector.qdrant.QDrantAdapter import QDrantAdapter
self.vector_engine = QDrantAdapter(
url = config.qdrant_url,
api_key = config.qdrant_api_key,
embedding_engine = self.embedding_engine
)
self.vector_engine_choice = "qdrant"
else:
self.vector_engine = vector_db_config.vector_engine
self.vector_engine_choice = vector_db_config.vector_engine_choice
if config_entity is not None:
return getattr(self, config_entity)
return {
"llm_engine": self.llm_engine,
"vector_engine": self.vector_engine,
"vector_engine_choice": self.vector_engine_choice,
"database_engine": self.database_engine,
"system_root_directory": self.system_root_directory,
"data_root_directory": self.data_root_directory,
@ -176,9 +135,6 @@ class InfrastructureConfig():
if "database_engine" in new_config:
self.database_engine = new_config["database_engine"]
if "vector_engine" in new_config:
self.vector_engine = new_config["vector_engine"]
if "llm_engine" in new_config:
self.llm_engine = new_config["llm_engine"]

View file

@ -6,7 +6,7 @@ from cognee.base_config import get_base_config
config = get_base_config()
class RelationalConfig(BaseSettings):
db_path: str = os.path.join(config.system_root_directory,"databases")
db_path: str = os.path.join(config.system_root_directory, "databases")
db_name: str = "cognee.db"
db_host: str = "localhost"
db_port: str = "5432"

View file

@ -66,6 +66,10 @@ class DuckDBAdapter():
result["data_id"]: result["status"] for result in results
}
def execute_query(self, query):
with self.get_connection() as connection:
return connection.sql(query).to_df().to_dict("records")
def load_cognify_data(self, data):
with self.get_connection() as connection:
# Ensure the "cognify" table exists

View file

@ -2,3 +2,4 @@ from .models.DataPoint import DataPoint
from .models.VectorConfig import VectorConfig
from .models.CollectionConfig import CollectionConfig
from .vector_db_interface import VectorDBInterface
from .config import get_vectordb_config

View file

@ -2,35 +2,43 @@ import os
from functools import lru_cache
from pydantic_settings import BaseSettings, SettingsConfigDict
from cognee.infrastructure.databases.relational.config import get_relationaldb_config
from cognee.infrastructure.databases.vector.lancedb.LanceDBAdapter import LanceDBAdapter
from cognee.infrastructure.databases.vector.embeddings.config import get_embedding_config
from cognee.infrastructure.files.storage import LocalStorage
from .create_vector_engine import create_vector_engine
embeddings_config = get_embedding_config()
relational_config = get_relationaldb_config()
class VectorConfig(BaseSettings):
vector_db_url: str = ""
vector_db_key: str = ""
vector_db_path: str = os.path.join(relational_config.database_directory_path + "cognee.lancedb")
vector_engine: object = LanceDBAdapter(
url = vector_db_path,
api_key = None,
embedding_engine = embeddings_config.embedding_engine,
)
vector_engine_choice:str = "lancedb"
lancedb_path = os.path.join(relational_config.database_directory_path, "cognee.lancedb")
LocalStorage.ensure_directory_exists(vector_db_path)
class VectorConfig(BaseSettings):
vector_db_url: str = lancedb_path
vector_db_key: str = ""
vector_engine_provider: str = "lancedb"
vector_engine: object = create_vector_engine(
{
"vector_db_key": None,
"vector_db_url": lancedb_path,
"vector_db_provider": "lancedb",
},
embeddings_config.embedding_engine,
)
model_config = SettingsConfigDict(env_file = ".env", extra = "allow")
def create_engine(self):
if self.vector_engine_provider == "lancedb":
self.vector_db_url = lancedb_path
self.vector_engine = create_vector_engine(
get_vectordb_config().to_dict(),
embeddings_config.embedding_engine,
)
def to_dict(self) -> dict:
return {
"vector_db_url": self.vector_db_url,
"vector_db_key": self.vector_db_key,
"vector_db_path": self.vector_db_path,
"vector_engine": self.vector_engine,
"vector_engine_choice": self.vector_engine_choice,
"vector_db_provider": self.vector_engine_provider,
}
@lru_cache

View file

@ -0,0 +1,39 @@
from typing import Dict
class VectorConfig(Dict):
vector_db_url: str
vector_db_key: str
vector_db_provider: str
def create_vector_engine(config: VectorConfig, embedding_engine):
if config["vector_db_provider"] == "weaviate":
from .weaviate_db import WeaviateAdapter
if config["vector_db_url"] is None and config["vector_db_key"] is None:
raise EnvironmentError("Weaviate is not configured!")
return WeaviateAdapter(
config["vector_db_url"],
config["vector_db_key"],
embedding_engine = embedding_engine
)
elif config["vector_db_provider"] == "qdrant":
if config["vector_db_url"] and config["vector_db_key"]:
from .qdrant.QDrantAdapter import QDrantAdapter
return QDrantAdapter(
url = config["vector_db_url"],
api_key = config["vector_db_key"],
embedding_engine = embedding_engine
)
else:
from .lancedb.LanceDBAdapter import LanceDBAdapter
from cognee.infrastructure.files.storage import LocalStorage
LocalStorage.ensure_directory_exists(config["vector_db_url"])
return LanceDBAdapter(
url = config["vector_db_url"],
api_key = config["vector_db_key"],
embedding_engine = embedding_engine,
)

View file

@ -1,13 +1,7 @@
"""Get the LLM client."""
from enum import Enum
import json
import logging
# from cognee.infrastructure.llm import llm_config
from cognee.config import Config
from cognee.infrastructure.llm import get_llm_config
# Define an Enum for LLM Providers
class LLMProvider(Enum):
OPENAI = "openai"
@ -18,7 +12,8 @@ class LLMProvider(Enum):
llm_config = get_llm_config()
def get_llm_client():
"""Get the LLM client based on the configuration using Enums."""
# logging.error(json.dumps(llm_config.to_dict()))
llm_config = get_llm_config()
provider = LLMProvider(llm_config.llm_provider)
if provider == LLMProvider.OPENAI:

View file

@ -3,7 +3,7 @@ from uuid import uuid4
from typing import List, Tuple, TypedDict
from pydantic import BaseModel
from cognee.infrastructure.databases.vector import DataPoint
from cognee.utils import extract_pos_tags, extract_named_entities, extract_sentiment_vader
# from cognee.utils import extract_pos_tags, extract_named_entities, extract_sentiment_vader
from cognee.infrastructure.databases.graph.config import get_graph_config
from cognee.infrastructure.databases.vector.config import get_vectordb_config
graph_config = get_graph_config()

View file

@ -1,7 +1,6 @@
from typing import TypedDict
from pydantic import BaseModel, Field
from cognee.infrastructure import infrastructure_config
from cognee.infrastructure.databases.vector.config import get_vectordb_config
from cognee.infrastructure.databases.vector import DataPoint

View file

@ -1,6 +1,4 @@
from typing import Dict, List
from cognee.infrastructure import infrastructure_config
from cognee.infrastructure.databases.graph.config import get_graph_config
from cognee.infrastructure.databases.vector.config import get_vectordb_config
graph_config = get_graph_config()

View file

@ -1,5 +1,3 @@
from dsp.utils import deduplicate
from cognee.infrastructure import infrastructure_config
from cognee.infrastructure.databases.graph.get_graph_client import get_graph_client
from cognee.infrastructure.databases.graph.config import get_graph_config
graph_config = get_graph_config()

View file

@ -1,6 +1,6 @@
from cognee.config import Config
from cognee.infrastructure import infrastructure_config
from cognee.infrastructure.llm.config import get_llm_config
from cognee.infrastructure.databases.vector import get_vectordb_config
from cognee.infrastructure.llm import get_llm_config
def get_settings():
config = Config()
@ -18,7 +18,7 @@ def get_settings():
"label": "LanceDB",
}]
vector_engine = infrastructure_config.get_config("vector_engine")
vector_config = get_vectordb_config()
llm_providers = [{
"value": "openai",
@ -31,6 +31,8 @@ def get_settings():
"label": "Anthropic",
}]
llm_config = get_llm_config()
return dict(
llm = {
"provider": {
@ -75,11 +77,11 @@ def get_settings():
},
vectorDB = {
"provider": {
"label": vector_engine.name,
"value": vector_engine.name.lower(),
"label": vector_config.vector_engine_provider,
"value": vector_config.vector_engine_provider.lower(),
},
"url": vector_engine.url,
"apiKey": vector_engine.api_key,
"url": vector_config.vector_db_url,
"apiKey": vector_config.vector_db_key,
"options": vector_dbs,
},
)

View file

@ -1,7 +1,7 @@
import json
import logging
from pydantic import BaseModel
from cognee.infrastructure.llm import llm_config
from cognee.infrastructure.llm import get_llm_config
from cognee.infrastructure import infrastructure_config
class LLMConfig(BaseModel):
@ -10,6 +10,8 @@ class LLMConfig(BaseModel):
provider: str
async def save_llm_config(new_llm_config: LLMConfig):
llm_config = get_llm_config()
llm_config.llm_provider = new_llm_config.provider
llm_config.llm_model = new_llm_config.model

View file

@ -1,10 +1,6 @@
import os
from typing import Union, Literal
from pydantic import BaseModel
from cognee.config import Config
from cognee.infrastructure import infrastructure_config
config = Config()
from cognee.infrastructure.databases.vector import get_vectordb_config
class VectorDBConfig(BaseModel):
url: str
@ -12,33 +8,9 @@ class VectorDBConfig(BaseModel):
provider: Union[Literal["lancedb"], Literal["qdrant"], Literal["weaviate"]]
async def save_vector_db_config(vector_db_config: VectorDBConfig):
if vector_db_config.provider == "weaviate":
os.environ["WEAVIATE_URL"] = vector_db_config.url
os.environ["WEAVIATE_API_KEY"] = vector_db_config.apiKey
vector_config = get_vectordb_config()
remove_qdrant_config()
if vector_db_config.provider == "qdrant":
os.environ["QDRANT_URL"] = vector_db_config.url
os.environ["QDRANT_API_KEY"] = vector_db_config.apiKey
remove_weaviate_config()
if vector_db_config.provider == "lancedb":
remove_qdrant_config()
remove_weaviate_config()
config.load()
infrastructure_config.vector_engine = None
def remove_weaviate_config():
if "WEAVIATE_URL" in os.environ:
del os.environ["WEAVIATE_URL"]
if "WEAVIATE_API_KEY" in os.environ:
del os.environ["WEAVIATE_API_KEY"]
def remove_qdrant_config():
if "QDRANT_URL" in os.environ:
del os.environ["QDRANT_URL"]
if "QDRANT_API_KEY" in os.environ:
del os.environ["QDRANT_API_KEY"]
vector_config.vector_db_url = vector_db_config.url
vector_config.vector_db_key = vector_db_config.apiKey
vector_config.vector_engine_provider = vector_db_config.provider
vector_config.create_engine()

View file

@ -1,2 +1,3 @@
from .get_task_status import get_task_status
from .update_task_status import update_task_status
from .create_task_status_table import create_task_status_table

View file

@ -0,0 +1,19 @@
from cognee.infrastructure.databases.relational.config import get_relationaldb_config
def get_task_status(data_ids: [str]):
relational_config = get_relationaldb_config()
db_engine = relational_config.db_engine
formatted_data_ids = ", ".join([f"'{data_id}'" for data_id in data_ids])
results = db_engine.execute_query(
f"""SELECT data_id, status
FROM (
SELECT data_id, status, ROW_NUMBER() OVER (PARTITION BY data_id ORDER BY created_at DESC) as rn
FROM cognee_task_status
WHERE data_id IN ({formatted_data_ids})
) t
WHERE rn = 1;"""
)
return results