Merge branch 'main' into feature/cog-2923-create-ci-test-for-fastembed

This commit is contained in:
Vasilije 2025-09-12 11:24:14 -07:00 committed by GitHub
commit 0b2c093144
No known key found for this signature in database
GPG key ID: B5690EEEBB952194
23 changed files with 97 additions and 76 deletions

View file

@ -1,6 +1,8 @@
import { FormEvent, useCallback, useState } from "react"; import { FormEvent, useCallback, useState } from "react";
import { CloseIcon, PlusIcon } from "@/ui/Icons";
import { LoadingIndicator } from "@/ui/App";
import { useModal } from "@/ui/elements/Modal"; import { useModal } from "@/ui/elements/Modal";
import { CloseIcon, PlusIcon } from "@/ui/Icons";
import { CTAButton, GhostButton, IconButton, Modal, NeutralButton, Select } from "@/ui/elements"; import { CTAButton, GhostButton, IconButton, Modal, NeutralButton, Select } from "@/ui/elements";
import addData from "@/modules/ingestion/addData"; import addData from "@/modules/ingestion/addData";
@ -44,14 +46,19 @@ export default function AddDataToCognee({ datasets, refreshDatasets, useCloud =
) )
.then(({ dataset_id, dataset_name }) => { .then(({ dataset_id, dataset_name }) => {
refreshDatasets(); refreshDatasets();
setFilesForUpload(null);
return cognifyDataset({ return cognifyDataset(
id: dataset_id, {
name: dataset_name, id: dataset_id,
data: [], // not important, just to mimick Dataset name: dataset_name,
status: "", // not important, just to mimick Dataset data: [], // not important, just to mimick Dataset
}, useCloud); status: "", // not important, just to mimick Dataset
},
useCloud,
)
.then(() => {
setFilesForUpload(null);
});
}); });
}, [filesForUpload, refreshDatasets, useCloud]); }, [filesForUpload, refreshDatasets, useCloud]);
@ -76,7 +83,7 @@ export default function AddDataToCognee({ datasets, refreshDatasets, useCloud =
<span className="text-2xl">Add new data to a dataset?</span> <span className="text-2xl">Add new data to a dataset?</span>
<IconButton disabled={isProcessingDataWithCognee} onClick={closeAddDataModal}><CloseIcon /></IconButton> <IconButton disabled={isProcessingDataWithCognee} onClick={closeAddDataModal}><CloseIcon /></IconButton>
</div> </div>
<div className="mt-8 mb-6">Please select a dataset to add data in.<br/> If you don&apos;t have any, don&apos;t worry, we will create one for you.</div> <div className="mt-8 mb-6">Please select a {useCloud ? "cloud" : "local"} dataset to add data in.<br/> If you don&apos;t have any, don&apos;t worry, we will create one for you.</div>
<form onSubmit={submitDataToCognee}> <form onSubmit={submitDataToCognee}>
<div className="max-w-md flex flex-col gap-4"> <div className="max-w-md flex flex-col gap-4">
<Select name="datasetName"> <Select name="datasetName">
@ -105,7 +112,8 @@ export default function AddDataToCognee({ datasets, refreshDatasets, useCloud =
<div className="flex flex-row gap-4 mt-4 justify-end"> <div className="flex flex-row gap-4 mt-4 justify-end">
<GhostButton disabled={isProcessingDataWithCognee} type="button" onClick={() => closeAddDataModal()}>cancel</GhostButton> <GhostButton disabled={isProcessingDataWithCognee} type="button" onClick={() => closeAddDataModal()}>cancel</GhostButton>
<CTAButton disabled={isProcessingDataWithCognee} type="submit"> <CTAButton disabled={isProcessingDataWithCognee} type="submit">
{isProcessingDataWithCognee ? "processing..." : "add"} {isProcessingDataWithCognee && <LoadingIndicator color="white" />}
add
</CTAButton> </CTAButton>
</div> </div>
</form> </form>

View file

@ -54,10 +54,12 @@ export default function Dashboard({ accessToken }: DashboardProps) {
const [selectedNotebookId, setSelectedNotebookId] = useState<string | null>(null); const [selectedNotebookId, setSelectedNotebookId] = useState<string | null>(null);
const handleNotebookRemove = useCallback((notebookId: string) => { const handleNotebookRemove = useCallback((notebookId: string) => {
setSelectedNotebookId((currentSelectedNotebookId) => ( return removeNotebook(notebookId)
currentSelectedNotebookId === notebookId ? null : currentSelectedNotebookId .then(() => {
)); setSelectedNotebookId((currentSelectedNotebookId) => (
return removeNotebook(notebookId); currentSelectedNotebookId === notebookId ? null : currentSelectedNotebookId
));
});
}, [removeNotebook]); }, [removeNotebook]);
const saveNotebookTimeoutRef = useRef<number | null>(null); const saveNotebookTimeoutRef = useRef<number | null>(null);
@ -158,7 +160,6 @@ export default function Dashboard({ accessToken }: DashboardProps) {
key={selectedNotebook.id} key={selectedNotebook.id}
notebook={selectedNotebook} notebook={selectedNotebook}
updateNotebook={handleNotebookUpdate} updateNotebook={handleNotebookUpdate}
saveNotebook={saveNotebook}
runCell={runCell} runCell={runCell}
/> />
)} )}

View file

@ -36,7 +36,7 @@ export default function InstanceDatasetsAccordion({ onDatasetsChange }: Instance
}; };
checkConnectionToLocalCognee(); checkConnectionToLocalCognee();
}, [checkConnectionToCloudCognee, setCloudCogneeConnected, setLocalCogneeConnected]); }, [setCloudCogneeConnected, setLocalCogneeConnected]);
const { const {
value: isCloudConnectedModalOpen, value: isCloudConnectedModalOpen,

View file

@ -56,8 +56,8 @@ export default function NotebooksAccordion({
notebookLoading(); notebookLoading();
removeNotebook(notebookToRemove!.id) removeNotebook(notebookToRemove!.id)
.finally(notebookLoaded) .finally(notebookLoaded)
.finally(closeRemoveNotebookModal); .finally(closeRemoveNotebookModal)
setNotebookToRemove(null); .finally(() => setNotebookToRemove(null));
}; };
const handleNotebookAdd = useCallback((_: object, formEvent?: FormEvent<HTMLFormElement>) => { const handleNotebookAdd = useCallback((_: object, formEvent?: FormEvent<HTMLFormElement>) => {

View file

@ -3,9 +3,9 @@
width: 1rem; width: 1rem;
height: 1rem; height: 1rem;
border-radius: 50%; border-radius: 50%;
border: 0.18rem solid var(--color-indigo-600);; border: 0.18rem solid var(--color-indigo-600);
border-top-color: transparent; border-top-color: transparent !important;
border-bottom-color: transparent; border-bottom-color: transparent !important;
animation: spin 2s linear infinite; animation: spin 2s linear infinite;
} }

View file

@ -1,5 +1,6 @@
import styles from './LoadingIndicator.module.css'; import classNames from "classnames";
import styles from "./LoadingIndicator.module.css";
export default function LoadingIndicator() { export default function LoadingIndicator({ color = "" }) {
return <div className={styles.loadingIndicator} /> return <div className={classNames(styles.loadingIndicator, `!border-${color}`)} />
} }

View file

@ -16,22 +16,9 @@ interface NotebookProps {
notebook: NotebookType; notebook: NotebookType;
runCell: (notebook: NotebookType, cell: Cell, cogneeInstance: string) => Promise<void>; runCell: (notebook: NotebookType, cell: Cell, cogneeInstance: string) => Promise<void>;
updateNotebook: (updatedNotebook: NotebookType) => void; updateNotebook: (updatedNotebook: NotebookType) => void;
saveNotebook: (notebook: NotebookType) => void;
} }
export default function Notebook({ notebook, updateNotebook, saveNotebook, runCell }: NotebookProps) { export default function Notebook({ notebook, updateNotebook, runCell }: NotebookProps) {
const saveCells = useCallback(() => {
saveNotebook(notebook);
}, [notebook, saveNotebook]);
useEffect(() => {
window.addEventListener("beforeunload", saveCells);
return () => {
window.removeEventListener("beforeunload", saveCells);
};
}, [saveCells]);
useEffect(() => { useEffect(() => {
if (notebook.cells.length === 0) { if (notebook.cells.length === 0) {
const newCell: Cell = { const newCell: Cell = {
@ -44,8 +31,9 @@ export default function Notebook({ notebook, updateNotebook, saveNotebook, runCe
...notebook, ...notebook,
cells: [newCell], cells: [newCell],
}); });
toggleCellOpen(newCell.id)
} }
}, [notebook, saveNotebook, updateNotebook]); }, [notebook, updateNotebook]);
const handleCellRun = useCallback((cell: Cell, cogneeInstance: string) => { const handleCellRun = useCallback((cell: Cell, cogneeInstance: string) => {
return runCell(notebook, cell, cogneeInstance); return runCell(notebook, cell, cogneeInstance);
@ -289,9 +277,14 @@ function CellResult({ content }: { content: [] }) {
} }
} }
if (typeof(line) === "object" && line["result"]) { if (typeof(line) === "object" && line["result"]) {
const datasets = Array.from(
// eslint-disable-next-line @typescript-eslint/no-explicit-any
new Set(Object.values(line["datasets"]).map((dataset: any) => dataset.name))
).join(", ");
parsedContent.push( parsedContent.push(
<div className="w-full h-full bg-white"> <div className="w-full h-full bg-white">
<span className="text-sm pl-2 mb-4">query response (dataset: {line["dataset_name"]})</span> <span className="text-sm pl-2 mb-4">query response (datasets: {datasets})</span>
<span className="block px-2 py-2">{line["result"]}</span> <span className="block px-2 py-2">{line["result"]}</span>
</div> </div>
); );
@ -303,7 +296,7 @@ function CellResult({ content }: { content: [] }) {
data={transformToVisualizationData(line["graphs"]["*"])} data={transformToVisualizationData(line["graphs"]["*"])}
ref={graphRef as MutableRefObject<GraphVisualizationAPI>} ref={graphRef as MutableRefObject<GraphVisualizationAPI>}
graphControls={graphControls} graphControls={graphControls}
className="min-h-48" className="min-h-80"
/> />
</div> </div>
); );

View file

@ -1,9 +1,9 @@
import { useState } from "react"; import { useState } from "react";
import classNames from "classnames"; import classNames from "classnames";
import { useBoolean } from "@/utils"; import { isCloudEnvironment, useBoolean } from "@/utils";
import { PlayIcon } from "@/ui/Icons"; import { PlayIcon } from "@/ui/Icons";
import { PopupMenu, IconButton, Select } from "@/ui/elements"; import { PopupMenu, IconButton } from "@/ui/elements";
import { LoadingIndicator } from "@/ui/App"; import { LoadingIndicator } from "@/ui/App";
import { Cell } from "./types"; import { Cell } from "./types";
@ -33,7 +33,7 @@ export default function NotebookCellHeader({
setFalse: setIsNotRunningCell, setFalse: setIsNotRunningCell,
} = useBoolean(false); } = useBoolean(false);
const [runInstance, setRunInstance] = useState<string>("local"); const [runInstance, setRunInstance] = useState<string>(isCloudEnvironment() ? "cloud" : "local");
const handleCellRun = () => { const handleCellRun = () => {
setIsRunningCell(); setIsRunningCell();
@ -50,14 +50,23 @@ export default function NotebookCellHeader({
<span className="ml-4">{cell.name}</span> <span className="ml-4">{cell.name}</span>
</div> </div>
<div className="pr-4 flex flex-row items-center gap-8"> <div className="pr-4 flex flex-row items-center gap-8">
<Select name="cogneeInstance" onChange={(event) => setRunInstance(event.currentTarget.value)} className="!bg-transparent outline-none cursor-pointer !hover:bg-gray-50"> {isCloudEnvironment() ? (
<div>
cloud cognee
</div>
) : (
<div>
local cognee
</div>
)}
{/* <Select name="cogneeInstance" onChange={(event) => setRunInstance(event.currentTarget.value)} className="!bg-transparent outline-none cursor-pointer !hover:bg-gray-50">
<option value="local" className="flex flex-row items-center gap-2"> <option value="local" className="flex flex-row items-center gap-2">
local cognee local cognee
</option> </option>
<option value="cloud" className="flex flex-row items-center gap-2"> <option value="cloud" className="flex flex-row items-center gap-2">
cloud cognee cloud cognee
</option> </option>
</Select> </Select> */}
<PopupMenu> <PopupMenu>
<div className="flex flex-col gap-0.5"> <div className="flex flex-col gap-0.5">
<button onClick={() => moveCellUp(cell)} className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer">move cell up</button> <button onClick={() => moveCellUp(cell)} className="hover:bg-gray-100 w-full text-left px-2 cursor-pointer">move cell up</button>

View file

@ -31,7 +31,8 @@ def get_notebooks_router():
@router.get("") @router.get("")
async def get_notebooks_endpoint(user: User = Depends(get_authenticated_user)): async def get_notebooks_endpoint(user: User = Depends(get_authenticated_user)):
return await get_notebooks(user.id) async with get_async_session() as session:
return await get_notebooks(user.id, session)
@router.post("") @router.post("")
async def create_notebook_endpoint( async def create_notebook_endpoint(

View file

@ -58,8 +58,8 @@ class SQLAlchemyAdapter:
else: else:
self.engine = create_async_engine( self.engine = create_async_engine(
connection_string, connection_string,
pool_size=5, pool_size=20,
max_overflow=10, max_overflow=20,
pool_recycle=280, pool_recycle=280,
pool_pre_ping=True, pool_pre_ping=True,
pool_timeout=280, pool_timeout=280,

View file

@ -39,7 +39,7 @@ class VectorConfig(BaseSettings):
values.vector_db_url = ensure_absolute_path( values.vector_db_url = ensure_absolute_path(
values.vector_db_url, values.vector_db_url,
) )
else: elif not values.vector_db_url:
# Default path # Default path
databases_directory_path = os.path.join(base_config.system_root_directory, "databases") databases_directory_path = os.path.join(base_config.system_root_directory, "databases")
values.vector_db_url = os.path.join(databases_directory_path, "cognee.lancedb") values.vector_db_url = os.path.join(databases_directory_path, "cognee.lancedb")

View file

@ -1,13 +1,18 @@
import asyncio import asyncio
from functools import partial from functools import partial
import inspect
async def run_async(func, *args, loop=None, executor=None, **kwargs): async def run_async(func, *args, loop=None, executor=None, **kwargs):
if loop is None: if loop is None:
try: try:
running_loop = asyncio.get_running_loop() loop = asyncio.get_running_loop()
except RuntimeError: except RuntimeError:
running_loop = asyncio.get_event_loop() loop = asyncio.get_event_loop()
pfunc = partial(func, *args, **kwargs) if "loop" in inspect.signature(func).parameters:
return await running_loop.run_in_executor(executor, pfunc) pfunc = partial(func, *args, loop=loop, **kwargs)
else:
pfunc = partial(func, *args, **kwargs)
return await loop.run_in_executor(executor, pfunc)

View file

@ -2,16 +2,17 @@ import asyncio
import threading import threading
def run_sync(coro, timeout=None): def run_sync(coro, running_loop=None, timeout=None):
result = None result = None
exception = None exception = None
def runner(): def runner():
nonlocal result, exception nonlocal result, exception, running_loop
try: try:
try: try:
running_loop = asyncio.get_running_loop() if not running_loop:
running_loop = asyncio.get_running_loop()
result = asyncio.run_coroutine_threadsafe(coro, running_loop).result(timeout) result = asyncio.run_coroutine_threadsafe(coro, running_loop).result(timeout)
except RuntimeError: except RuntimeError:

View file

@ -1,6 +1,6 @@
from uuid import UUID from uuid import UUID
from typing import Optional from typing import Optional
from sqlalchemy import select from sqlalchemy import and_, select
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from cognee.infrastructure.databases.relational import with_async_session from cognee.infrastructure.databases.relational import with_async_session
@ -15,7 +15,7 @@ async def get_notebook(
session: AsyncSession, session: AsyncSession,
) -> Optional[Notebook]: ) -> Optional[Notebook]:
result = await session.execute( result = await session.execute(
select(Notebook).where(Notebook.owner_id == user_id and Notebook.id == notebook_id) select(Notebook).where(and_(Notebook.owner_id == user_id, Notebook.id == notebook_id))
) )
return result.scalar() return result.scalar()

View file

@ -1,4 +1,3 @@
from typing import Callable, AsyncContextManager
from sqlalchemy.ext.asyncio import AsyncSession from sqlalchemy.ext.asyncio import AsyncSession
from cognee.infrastructure.databases.relational import with_async_session from cognee.infrastructure.databases.relational import with_async_session

View file

@ -5,16 +5,18 @@ import traceback
def wrap_in_async_handler(user_code: str) -> str: def wrap_in_async_handler(user_code: str) -> str:
return ( return (
"from cognee.infrastructure.utils.run_sync import run_sync\n\n" "import asyncio\n"
"async def __user_main__():\n" + "asyncio.set_event_loop(running_loop)\n\n"
+ "from cognee.infrastructure.utils.run_sync import run_sync\n\n"
+ "async def __user_main__():\n"
+ "\n".join(" " + line for line in user_code.strip().split("\n")) + "\n".join(" " + line for line in user_code.strip().split("\n"))
+ "\n" + "\n"
" globals().update(locals())\n\n" + " globals().update(locals())\n\n"
"run_sync(__user_main__())\n" + "run_sync(__user_main__(), running_loop)\n"
) )
def run_in_local_sandbox(code, environment=None): def run_in_local_sandbox(code, environment=None, loop=None):
environment = environment or {} environment = environment or {}
code = wrap_in_async_handler(code.replace("\xa0", "\n")) code = wrap_in_async_handler(code.replace("\xa0", "\n"))
@ -31,6 +33,7 @@ def run_in_local_sandbox(code, environment=None):
printOutput.append(output) printOutput.append(output)
environment["print"] = customPrintFunction environment["print"] = customPrintFunction
environment["running_loop"] = loop
try: try:
exec(code, environment) exec(code, environment)

View file

@ -48,7 +48,7 @@ class GraphCompletionContextExtensionRetriever(GraphCompletionRetriever):
query: str, query: str,
context: Optional[List[Edge]] = None, context: Optional[List[Edge]] = None,
context_extension_rounds=4, context_extension_rounds=4,
) -> str: ) -> List[str]:
""" """
Extends the context for a given query by retrieving related triplets and generating new Extends the context for a given query by retrieving related triplets and generating new
completions based on them. completions based on them.

View file

@ -58,7 +58,7 @@ class GraphCompletionCotRetriever(GraphCompletionRetriever):
query: str, query: str,
context: Optional[List[Edge]] = None, context: Optional[List[Edge]] = None,
max_iter=4, max_iter=4,
) -> str: ) -> List[str]:
""" """
Generate completion responses based on a user query and contextual information. Generate completion responses based on a user query and contextual information.

View file

@ -135,7 +135,7 @@ class GraphCompletionRetriever(BaseGraphRetriever):
self, self,
query: str, query: str,
context: Optional[List[Edge]] = None, context: Optional[List[Edge]] = None,
) -> Any: ) -> List[str]:
""" """
Generates a completion using graph connections context based on a query. Generates a completion using graph connections context based on a query.

View file

@ -136,7 +136,7 @@ class TemporalRetriever(GraphCompletionRetriever):
return self.descriptions_to_string(top_k_events) return self.descriptions_to_string(top_k_events)
async def get_completion(self, query: str, context: Optional[str] = None) -> str: async def get_completion(self, query: str, context: Optional[str] = None) -> List[str]:
"""Generates a response using the query and optional context.""" """Generates a response using the query and optional context."""
if not context: if not context:
context = await self.get_context(query=query) context = await self.get_context(query=query)

View file

@ -1,4 +1,4 @@
from typing import Any, Optional, List from typing import Optional, List
from uuid import NAMESPACE_OID, uuid5, UUID from uuid import NAMESPACE_OID, uuid5, UUID
from cognee.infrastructure.databases.graph import get_graph_engine from cognee.infrastructure.databases.graph import get_graph_engine

View file

@ -6,7 +6,7 @@ from cognee.modules.search.utils.transform_context_to_graph import transform_con
async def prepare_search_result(search_result): async def prepare_search_result(search_result):
result, context, datasets = search_result results, context, datasets = search_result
graphs = None graphs = None
result_graph = None result_graph = None
@ -30,11 +30,11 @@ async def prepare_search_result(search_result):
"*": "\n".join(cast(List[str], context)), "*": "\n".join(cast(List[str], context)),
} }
if isinstance(result, List) and len(result) > 0 and isinstance(result[0], Edge): if isinstance(results, List) and len(results) > 0 and isinstance(results[0], Edge):
result_graph = transform_context_to_graph(result) result_graph = transform_context_to_graph(results)
return { return {
"result": result_graph or result, "result": result_graph or results[0] if len(results) == 1 else results,
"graphs": graphs, "graphs": graphs,
"context": context_texts, "context": context_texts,
"datasets": datasets, "datasets": datasets,

View file

@ -1,7 +1,7 @@
[project] [project]
name = "cognee" name = "cognee"
version = "0.3.2" version = "0.3.3"
description = "Cognee - is a library for enriching LLM context with a semantic layer for better understanding and reasoning." description = "Cognee - is a library for enriching LLM context with a semantic layer for better understanding and reasoning."
authors = [ authors = [
{ name = "Vasilije Markovic" }, { name = "Vasilije Markovic" },
@ -147,7 +147,7 @@ Homepage = "https://www.cognee.ai"
Repository = "https://github.com/topoteretes/cognee" Repository = "https://github.com/topoteretes/cognee"
[project.scripts] [project.scripts]
cognee = "cognee.cli._cognee:main" cognee-cli = "cognee.cli._cognee:main"
[build-system] [build-system]
requires = ["hatchling"] requires = ["hatchling"]