From a036bd04c74bd75fb6167c23dc0539fb42293967 Mon Sep 17 00:00:00 2001
From: Boris
Date: Sat, 31 May 2025 02:35:18 +0200
Subject: [PATCH 01/15] version: v0.1.41 (#893)
## Description
## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
---------
Signed-off-by: Diego B Theuerkauf
Co-authored-by: vasilije
Co-authored-by: Igor Ilic <30923996+dexters1@users.noreply.github.com>
Co-authored-by: Vasilije <8619304+Vasilije1990@users.noreply.github.com>
Co-authored-by: Igor Ilic
Co-authored-by: Hande <159312713+hande-k@users.noreply.github.com>
Co-authored-by: Matea Pesic <80577904+matea16@users.noreply.github.com>
Co-authored-by: hajdul88 <52442977+hajdul88@users.noreply.github.com>
Co-authored-by: Daniel Molnar
Co-authored-by: Diego Baptista Theuerkauf <34717973+diegoabt@users.noreply.github.com>
Co-authored-by: Dmitrii Galkin <36552323+dm1tryG@users.noreply.github.com>
Co-authored-by: coderabbitai[bot] <136622811+coderabbitai[bot]@users.noreply.github.com>
Co-authored-by: lxobr <122801072+lxobr@users.noreply.github.com>
Co-authored-by: github-actions[bot]
From c37b5da422a00687597ab6dc48806a8c2bebd2a5 Mon Sep 17 00:00:00 2001
From: Igor Ilic <30923996+dexters1@users.noreply.github.com>
Date: Mon, 2 Jun 2025 11:19:43 +0200
Subject: [PATCH 02/15] refactor: Add better example information [main branch]
(#883)
## Description
Update migration example for upcoming blog on main branch
## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
---
.../relational_database_migration_example.py | 21 ++++++++++++-------
1 file changed, 14 insertions(+), 7 deletions(-)
diff --git a/examples/python/relational_database_migration_example.py b/examples/python/relational_database_migration_example.py
index bef95f3e9..c19e21435 100644
--- a/examples/python/relational_database_migration_example.py
+++ b/examples/python/relational_database_migration_example.py
@@ -1,7 +1,6 @@
import asyncio
import cognee
import os
-import logging
from cognee.infrastructure.databases.graph import get_graph_engine
from cognee.api.v1.visualize.visualize import visualize_graph
@@ -10,13 +9,12 @@ from cognee.infrastructure.databases.relational import (
)
from cognee.modules.search.types import SearchType
-from cognee.modules.users.methods import get_default_user
from cognee.infrastructure.databases.relational import (
create_db_and_tables as create_relational_db_and_tables,
)
from cognee.infrastructure.databases.vector.pgvector import (
- create_db_and_tables as create_pgvector_db_and_tables,
+ create_db_and_tables as create_vector_db_and_tables,
)
# Prerequisites:
@@ -25,17 +23,23 @@ from cognee.infrastructure.databases.vector.pgvector import (
# LLM_API_KEY = "your_key_here"
# 3. Fill all relevant MIGRATION_DB information for the database you want to migrate to graph / Cognee
+# NOTE: If you don't have a DB you want to migrate you can try it out with our
+# test database at the following location:
+# MIGRATION_DB_PATH="/{path_to_your_local_cognee}/cognee/tests/test_data"
+# MIGRATION_DB_NAME="migration_database.sqlite"
+# MIGRATION_DB_PROVIDER="sqlite"
+
async def main():
engine = get_migration_relational_engine()
+ # Clean all data stored in Cognee
await cognee.prune.prune_data()
await cognee.prune.prune_system(metadata=True)
- # Needed to create principals table
- # Create tables for databases
+ # Needed to create appropriate tables only on the Cognee side
await create_relational_db_and_tables()
- await create_pgvector_db_and_tables()
+ await create_vector_db_and_tables()
print("\nExtracting schema of database to migrate.")
schema = await engine.extract_schema()
@@ -57,8 +61,11 @@ async def main():
await visualize_graph(destination_file_path)
print(f"Visualization can be found at: {destination_file_path}")
+ # Make sure to set top_k at a high value for a broader search, the default value is only 10!
search_results = await cognee.search(
- query_type=SearchType.GRAPH_COMPLETION, query_text="What kind of data do you contain?"
+ query_type=SearchType.GRAPH_COMPLETION,
+ query_text="What kind of data do you contain?",
+ top_k=1000,
)
print(f"Search results: {search_results}")
From 0dc0c3ccdf5fefa8505f239dc23da410ad0dd1a0 Mon Sep 17 00:00:00 2001
From: Vasilije <8619304+Vasilije1990@users.noreply.github.com>
Date: Mon, 2 Jun 2025 20:20:35 +0200
Subject: [PATCH 03/15] Update README.md
---
README.md | 4 ++--
1 file changed, 2 insertions(+), 2 deletions(-)
diff --git a/README.md b/README.md
index 832ffaa3a..cbbad2a86 100644
--- a/README.md
+++ b/README.md
@@ -28,7 +28,7 @@
-Build dynamic Agent memory using scalable, modular ECL (Extract, Cognify, Load) pipelines.
+Build dynamic memory for Agents and replace RAG using scalable, modular ECL (Extract, Cognify, Load) pipelines.
More on [use-cases](https://docs.cognee.ai/use-cases) and [evals](https://github.com/topoteretes/cognee/tree/main/evals)
@@ -55,7 +55,7 @@ More on [use-cases](https://docs.cognee.ai/use-cases) and [evals](https://github
## Features
- Interconnect and retrieve your past conversations, documents, images and audio transcriptions
-- Reduce hallucinations, developer effort, and cost.
+- Replaces RAG systems and reduces developer effort, and cost.
- Load data to graph and vector databases using only Pydantic
- Manipulate your data while ingesting from 30+ data sources
From 1a9cf903fdf0dd476ba527b871a3e478d8f55f01 Mon Sep 17 00:00:00 2001
From: Igor Ilic <30923996+dexters1@users.noreply.github.com>
Date: Mon, 2 Jun 2025 22:12:35 +0200
Subject: [PATCH 04/15] fix: Resolve issue with UI auth (#900)
## Description
## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
---
cognee/modules/users/methods/get_authenticated_user.py | 6 ++----
1 file changed, 2 insertions(+), 4 deletions(-)
diff --git a/cognee/modules/users/methods/get_authenticated_user.py b/cognee/modules/users/methods/get_authenticated_user.py
index ae7825202..c181369b8 100644
--- a/cognee/modules/users/methods/get_authenticated_user.py
+++ b/cognee/modules/users/methods/get_authenticated_user.py
@@ -21,7 +21,7 @@ async def get_authenticated_user(authorization: str = Header(...)) -> SimpleName
token, os.getenv("FASTAPI_USERS_JWT_SECRET", "super_secret"), algorithms=["HS256"]
)
- if payload["tenant_id"]:
+ if payload.get("tenant_id"):
# SimpleNamespace lets us access dictionary elements like attributes
auth_data = SimpleNamespace(
id=UUID(payload["user_id"]),
@@ -29,9 +29,7 @@ async def get_authenticated_user(authorization: str = Header(...)) -> SimpleName
roles=payload["roles"],
)
else:
- auth_data = SimpleNamespace(
- id=UUID(payload["user_id"]), tenant_id=None, roles=payload["roles"]
- )
+ auth_data = SimpleNamespace(id=UUID(payload["user_id"]), tenant_id=None, roles=[])
return auth_data
From 128f72078ecd0c24bc41a83481145afb5b0c2d07 Mon Sep 17 00:00:00 2001
From: Vasilije <8619304+Vasilije1990@users.noreply.github.com>
Date: Wed, 4 Jun 2025 05:16:37 +0200
Subject: [PATCH 05/15] Update README.md
---
README.md | 2 +-
1 file changed, 1 insertion(+), 1 deletion(-)
diff --git a/README.md b/README.md
index cbbad2a86..18e19bb4a 100644
--- a/README.md
+++ b/README.md
@@ -61,7 +61,7 @@ More on [use-cases](https://docs.cognee.ai/use-cases) and [evals](https://github
## Get Started
-Get started quickly with a Google Colab notebook or starter repo
+Get started quickly with a Google Colab notebook , Deepnote notebook or starter repo
## Contributing
From aa3163561dbaf883aa7c519bdad81805ef99e7ec Mon Sep 17 00:00:00 2001
From: Igor Ilic <30923996+dexters1@users.noreply.github.com>
Date: Wed, 4 Jun 2025 20:06:36 +0200
Subject: [PATCH 06/15] refactor: remove notebooks (#905)
## Description
## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
---
.github/workflows/notebooks_tests.yml | 21 --
notebooks/cognee_graphiti_demo.ipynb | 225 --------------
notebooks/cognee_llama_index.ipynb | 239 ---------------
notebooks/graphrag_vs_rag.ipynb | 253 ---------------
.../llama_index_cognee_integration.ipynb | 288 ------------------
5 files changed, 1026 deletions(-)
delete mode 100644 notebooks/cognee_graphiti_demo.ipynb
delete mode 100644 notebooks/cognee_llama_index.ipynb
delete mode 100644 notebooks/graphrag_vs_rag.ipynb
delete mode 100644 notebooks/llama_index_cognee_integration.ipynb
diff --git a/.github/workflows/notebooks_tests.yml b/.github/workflows/notebooks_tests.yml
index a6fbae294..f33f90b4a 100644
--- a/.github/workflows/notebooks_tests.yml
+++ b/.github/workflows/notebooks_tests.yml
@@ -11,30 +11,9 @@ jobs:
# notebook-location: notebooks/cognee_demo.ipynb
# secrets: inherit
- run-llama-index-integration:
- name: LlamaIndex Integration Notebook
- uses: ./.github/workflows/reusable_notebook.yml
- with:
- notebook-location: notebooks/llama_index_cognee_integration.ipynb
- secrets: inherit
-
- run-cognee-llama-index:
- name: Cognee LlamaIndex Notebook
- uses: ./.github/workflows/reusable_notebook.yml
- with:
- notebook-location: notebooks/cognee_llama_index.ipynb
- secrets: inherit
-
run-cognee-multimedia:
name: Cognee Multimedia Notebook
uses: ./.github/workflows/reusable_notebook.yml
with:
notebook-location: notebooks/cognee_multimedia_demo.ipynb
secrets: inherit
-
-# run-graphrag-vs-rag:
-# name: Graphrag vs Rag notebook
-# uses: ./.github/workflows/reusable_notebook.yml
-# with:
-# notebook-location: notebooks/graphrag_vs_rag.ipynb
-# secrets: inherit
diff --git a/notebooks/cognee_graphiti_demo.ipynb b/notebooks/cognee_graphiti_demo.ipynb
deleted file mode 100644
index 79123a483..000000000
--- a/notebooks/cognee_graphiti_demo.ipynb
+++ /dev/null
@@ -1,225 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "# Cognee Graphiti integration demo"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {
- "vscode": {
- "languageId": "plaintext"
- }
- },
- "source": [
- "First we import the necessary libraries"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import asyncio\n",
- "\n",
- "import cognee\n",
- "from cognee.shared.logging_utils import get_logger, ERROR\n",
- "from cognee.modules.pipelines import Task, run_tasks\n",
- "from cognee.tasks.temporal_awareness import build_graph_with_temporal_awareness\n",
- "from cognee.infrastructure.databases.relational import (\n",
- " create_db_and_tables as create_relational_db_and_tables,\n",
- ")\n",
- "from cognee.tasks.temporal_awareness.index_graphiti_objects import (\n",
- " index_and_transform_graphiti_nodes_and_edges,\n",
- ")\n",
- "from cognee.modules.retrieval.utils.brute_force_triplet_search import brute_force_triplet_search\n",
- "from cognee.modules.retrieval.graph_completion_retriever import GraphCompletionRetriever\n",
- "from cognee.infrastructure.llm.prompts import read_query_prompt, render_prompt\n",
- "from cognee.infrastructure.llm.get_llm_client import get_llm_client\n",
- "from cognee.modules.users.methods import get_default_user"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Set environment variables"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "ExecuteTime": {
- "end_time": "2025-01-15T10:43:57.893763Z",
- "start_time": "2025-01-15T10:43:57.891332Z"
- }
- },
- "outputs": [],
- "source": [
- "import os\n",
- "\n",
- "# We ignore warnigns for now\n",
- "warnings.filterwarnings(\"ignore\")\n",
- "\n",
- "# API key for cognee\n",
- "if \"LLM_API_KEY\" not in os.environ:\n",
- " os.environ[\"LLM_API_KEY\"] = \"\"\n",
- "\n",
- "# API key for graphiti\n",
- "if \"OPENAI_API_KEY\" not in os.environ:\n",
- " os.environ[\"OPENAI_API_KEY\"] = \"\"\n",
- "\n",
- "GRAPH_DATABASE_PROVIDER = \"neo4j\"\n",
- "GRAPH_DATABASE_USERNAME = \"neo4j\"\n",
- "GRAPH_DATABASE_PASSWORD = \"pleaseletmein\"\n",
- "GRAPH_DATABASE_URL = \"bolt://localhost:7687\"\n",
- "\n",
- "os.environ[\"GRAPH_DATABASE_PROVIDER\"] = GRAPH_DATABASE_PROVIDER\n",
- "os.environ[\"GRAPH_DATABASE_USERNAME\"] = GRAPH_DATABASE_USERNAME\n",
- "os.environ[\"GRAPH_DATABASE_PASSWORD\"] = GRAPH_DATABASE_PASSWORD\n",
- "os.environ[\"GRAPH_DATABASE_URL\"] = GRAPH_DATABASE_URL\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Input texts with temporal information"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "ExecuteTime": {
- "end_time": "2025-01-15T10:43:57.928664Z",
- "start_time": "2025-01-15T10:43:57.927105Z"
- }
- },
- "outputs": [],
- "source": [
- "text_list = [\n",
- " \"Kamala Harris is the Attorney General of California. She was previously \"\n",
- " \"the district attorney for San Francisco.\",\n",
- " \"As AG, Harris was in office from January 3, 2011 – January 3, 2017\",\n",
- "]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Running graphiti + transforming its graph into cognee's core system (graph transformation + vector embeddings)"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "ExecuteTime": {
- "end_time": "2025-01-15T10:44:25.008501Z",
- "start_time": "2025-01-15T10:43:57.932240Z"
- }
- },
- "outputs": [],
- "source": [
- "await cognee.prune.prune_data()\n",
- "await cognee.prune.prune_system(metadata=True)\n",
- "await create_relational_db_and_tables()\n",
- "\n",
- "# Initialize default user\n",
- "user = await get_default_user()\n",
- "\n",
- "for text in text_list:\n",
- " await cognee.add(text)\n",
- "\n",
- "tasks = [\n",
- " Task(build_graph_with_temporal_awareness, text_list=text_list),\n",
- " ]\n",
- "\n",
- "pipeline = run_tasks(tasks, user=user)\n",
- "\n",
- "async for result in pipeline:\n",
- " print(result)\n",
- "\n",
- "await index_and_transform_graphiti_nodes_and_edges()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Retrieving and generating answer from graphiti graph with cognee retriever"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {
- "ExecuteTime": {
- "end_time": "2025-01-15T10:44:27.844438Z",
- "start_time": "2025-01-15T10:44:25.013325Z"
- }
- },
- "outputs": [],
- "source": [
- "# Step 1: Formulating the Query 🔍\n",
- "query = \"When was Kamala Harris in office?\"\n",
- "\n",
- "# Step 2: Searching for Relevant Triplets 📊\n",
- "triplets = await brute_force_triplet_search(\n",
- " query=query,\n",
- " top_k=3,\n",
- " collections=[\"graphitinode_content\", \"graphitinode_name\", \"graphitinode_summary\"],\n",
- ")\n",
- "\n",
- "# Step 3: Preparing the Context for the LLM\n",
- "retriever = GraphCompletionRetriever()\n",
- "context = await retriever.resolve_edges_to_text(triplets)\n",
- "\n",
- "args = {\"question\": query, \"context\": context}\n",
- "\n",
- "# Step 4: Generating Prompts ✍️\n",
- "user_prompt = render_prompt(\"graph_context_for_question.txt\", args)\n",
- "system_prompt = read_query_prompt(\"answer_simple_question_restricted.txt\")\n",
- "\n",
- "# Step 5: Interacting with the LLM 🤖\n",
- "llm_client = get_llm_client()\n",
- "computed_answer = await llm_client.acreate_structured_output(\n",
- " text_input=user_prompt, # Input prompt for the user context\n",
- " system_prompt=system_prompt, # System-level instructions for the model\n",
- " response_model=str,\n",
- ")\n",
- "\n",
- "# Step 6: Displaying the Computed Answer ✨\n",
- "print(f\"💡 Answer: {computed_answer}\")"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": ".venv",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.11.8"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/notebooks/cognee_llama_index.ipynb b/notebooks/cognee_llama_index.ipynb
deleted file mode 100644
index 65f1b2ef0..000000000
--- a/notebooks/cognee_llama_index.ipynb
+++ /dev/null
@@ -1,239 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Cognee GraphRAG with LlamaIndex Documents"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "%pip install llama-index-core\n"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Load Data\n",
- "\n",
- "We will use a sample news article dataset retrieved from Diffbot, which Tomaz has conveniently made available on GitHub for easy access.\n",
- "\n",
- "The dataset contains 2,500 samples; for ease of experimentation, we will use 5 of these samples, which include the `title` and `text` of news articles."
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import pandas as pd\n",
- "from llama_index.core import Document\n",
- "\n",
- "news = pd.read_csv(\n",
- " \"https://raw.githubusercontent.com/tomasonjo/blog-datasets/main/news_articles.csv\"\n",
- ")[:5]\n",
- "\n",
- "news.head()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Prepare documents as required by LlamaIndex"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 3,
- "metadata": {},
- "outputs": [],
- "source": [
- "documents = [Document(text=f\"{row['title']}: {row['text']}\") for i, row in news.iterrows()]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Set environment variables"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": 4,
- "metadata": {},
- "outputs": [],
- "source": [
- "import os\n",
- "\n",
- "# Setting environment variables\n",
- "if \"GRAPHISTRY_USERNAME\" not in os.environ:\n",
- " os.environ[\"GRAPHISTRY_USERNAME\"] = \"\"\n",
- "\n",
- "if \"GRAPHISTRY_PASSWORD\" not in os.environ:\n",
- " os.environ[\"GRAPHISTRY_PASSWORD\"] = \"\"\n",
- "\n",
- "if \"LLM_API_KEY\" not in os.environ:\n",
- " os.environ[\"LLM_API_KEY\"] = \"\"\n",
- "\n",
- "# \"neo4j\" or \"networkx\"\n",
- "os.environ[\"GRAPH_DATABASE_PROVIDER\"] = \"networkx\"\n",
- "# Not needed if using networkx\n",
- "# os.environ[\"GRAPH_DATABASE_URL\"]=\"\"\n",
- "# os.environ[\"GRAPH_DATABASE_USERNAME\"]=\"\"\n",
- "# os.environ[\"GRAPH_DATABASE_PASSWORD\"]=\"\"\n",
- "\n",
- "# \"pgvector\", \"qdrant\", \"weaviate\" or \"lancedb\"\n",
- "os.environ[\"VECTOR_DB_PROVIDER\"] = \"lancedb\"\n",
- "# Not needed if using \"lancedb\" or \"pgvector\"\n",
- "# os.environ[\"VECTOR_DB_URL\"]=\"\"\n",
- "# os.environ[\"VECTOR_DB_KEY\"]=\"\"\n",
- "\n",
- "# Relational Database provider \"sqlite\" or \"postgres\"\n",
- "os.environ[\"DB_PROVIDER\"] = \"sqlite\"\n",
- "\n",
- "# Database name\n",
- "os.environ[\"DB_NAME\"] = \"cognee_db\"\n",
- "\n",
- "# Postgres specific parameters (Only if Postgres or PGVector is used)\n",
- "# os.environ[\"DB_HOST\"]=\"127.0.0.1\"\n",
- "# os.environ[\"DB_PORT\"]=\"5432\"\n",
- "# os.environ[\"DB_USERNAME\"]=\"cognee\"\n",
- "# os.environ[\"DB_PASSWORD\"]=\"cognee\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Run Cognee with LlamaIndex Documents"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "from typing import Union, BinaryIO\n",
- "\n",
- "from cognee.infrastructure.databases.vector.pgvector import (\n",
- " create_db_and_tables as create_pgvector_db_and_tables,\n",
- ")\n",
- "from cognee.infrastructure.databases.relational import (\n",
- " create_db_and_tables as create_relational_db_and_tables,\n",
- ")\n",
- "from cognee.modules.users.models import User\n",
- "from cognee.modules.users.methods import get_default_user\n",
- "from cognee.tasks.ingestion.ingest_data import ingest_data\n",
- "import cognee\n",
- "\n",
- "# Create a clean slate for cognee -- reset data and system state\n",
- "await cognee.prune.prune_data()\n",
- "await cognee.prune.prune_system(metadata=True)\n",
- "\n",
- "\n",
- "# Add the LlamaIndex documents, and make it available for cognify\n",
- "async def add(\n",
- " data: Union[BinaryIO, list[BinaryIO], str, list[str]],\n",
- " dataset_name: str = \"main_dataset\",\n",
- " user: User = None,\n",
- "):\n",
- " await create_relational_db_and_tables()\n",
- " await create_pgvector_db_and_tables()\n",
- "\n",
- " if user is None:\n",
- " user = await get_default_user()\n",
- "\n",
- " await ingest_data(data, dataset_name, user)\n",
- "\n",
- "\n",
- "await add(documents)\n",
- "\n",
- "# Use LLMs and cognee to create knowledge graph\n",
- "await cognee.cognify()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Query Cognee for summaries related to data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "from cognee import SearchType\n",
- "\n",
- "# Query cognee for summaries\n",
- "search_results = await cognee.search(\n",
- " query_type=SearchType.SUMMARIES, query_text=\"What are the main news discussed in the document?\"\n",
- ")\n",
- "# Display search results\n",
- "print(\"\\n Summary of main news discussed:\\n\")\n",
- "print(search_results[0][\"text\"])"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Render Knowledge Graph generated from provided data"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import graphistry\n",
- "\n",
- "from cognee.infrastructure.databases.graph import get_graph_engine\n",
- "from cognee.shared.utils import render_graph\n",
- "\n",
- "# Get graph\n",
- "graphistry.login(\n",
- " username=os.getenv(\"GRAPHISTRY_USERNAME\"), password=os.getenv(\"GRAPHISTRY_PASSWORD\")\n",
- ")\n",
- "graph_engine = await get_graph_engine()\n",
- "\n",
- "graph_url = await render_graph(graph_engine.graph)\n",
- "print(graph_url)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": ".venv",
- "language": "python",
- "name": "python3"
- },
- "language_info": {
- "codemirror_mode": {
- "name": "ipython",
- "version": 3
- },
- "file_extension": ".py",
- "mimetype": "text/x-python",
- "name": "python",
- "nbconvert_exporter": "python",
- "pygments_lexer": "ipython3",
- "version": "3.9.6"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/notebooks/graphrag_vs_rag.ipynb b/notebooks/graphrag_vs_rag.ipynb
deleted file mode 100644
index 035015264..000000000
--- a/notebooks/graphrag_vs_rag.ipynb
+++ /dev/null
@@ -1,253 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Cognee GraphRAG\n",
- "\n",
- "Connecting external knowledge to the LLM efficiently and retrieving it is a key challenge faced by developers. For developers and data scientists, integrating structured and unstructured data into AI workflows often involves multiple tools, complex pipelines, and time-consuming processes.\n",
- "\n",
- "Enter **cognee,** a powerful framework for knowledge and memory management. Cognee streamlines the path from raw data to actionable insights.\n",
- "\n",
- "In this notebook, we’ll explore a demo that leverages cognee and creates a knowledge graph from a document, process it into a meaningful structure, and extract useful insights. By the end, you’ll see how cognee can give you new insights into your data by connecting various data sources in one big semantic layer you can analyze.\n",
- "\n",
- "## RAG: Retrieval Augmented Generation - Recap\n",
- "\n",
- "RAG enhances LLMs by integrating external knowledge sources during inference. It does so by turning the data into a vector representation and storing it in a vector store.\n",
- "\n",
- "### Key Benefits of RAG:\n",
- "\n",
- "1. Connecting domain specific data to LLMs\n",
- "2. Cost savings\n",
- "3. Higher accuracy than base LLM\n",
- "\n",
- "However, building a RAG system presents challenges: handling diverse data formats, data updates, creating a robust metadata layer, and mediocre accuracy\n",
- "\n",
- "## Introducing cognee\n",
- "\n",
- "cognee simplifies knowledge and memory management for LLMs\n",
- "\n",
- "cognee is inspired by human mind and higher cognitive functions. It mimics ways we construct our mental map of the world and build a semantic understanding of various objects, terms and issues in our everyday lives.\n",
- "\n",
- "cognee brings this approach to code by allowing developers to create semantic layers that would allow users to store their ontologies which are **a formalised depiction of knowledge** in graphs.\n",
- "\n",
- "This lets you use the knowledge you have about a system connect it to LLMs in a modular way, with the best data engineering practices, wide choice of vector and graph stores and various LLMs you can use.\n",
- "\n",
- "Together, they:\n",
- "\n",
- "- Turn unstructured and semi-structured data into a graph/vector representation.\n",
- "- Enable ontology generation for particular domains, making unique graphs for every vertical\n",
- "- Provide a deterministic layer for LLM outputs, ensuring consistency and reliability.\n",
- "\n",
- "## Step-by-Step Demo: Building a RAG System with Cognee\n",
- "\n",
- "### 1. Setting Up the Environment\n",
- "\n",
- "Start by importing the required libraries and defining the environment:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install cognee==0.1.39"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import os\n",
- "import cognee\n",
- "\n",
- "await cognee.prune.prune_data()\n",
- "await cognee.prune.prune_system(metadata=True)\n",
- "\n",
- "if \"OPENAI_API_KEY\" not in os.environ:\n",
- " os.environ[\"OPENAI_API_KEY\"] = \"\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Ensure you’ve set up your API keys and installed necessary dependencies.\n",
- "\n",
- "### 2. Preparing the Dataset\n",
- "\n",
- "We’ll use a brief profile of an individual as our sample dataset:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "documents = [\"Jessica Miller, Experienced Sales Manager with a strong track record in building high-performing teams.\",\n",
- " \"David Thompson, Creative Graphic Designer with over 8 years of experience in visual design and branding.\"\n",
- " ]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 3. Adding Data to Cognee\n",
- "\n",
- "Load the dataset into the cognee framework:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "await cognee.add(documents)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This step prepares the data for graph-based processing.\n",
- "\n",
- "### 5. Processing Data into a Knowledge Graph\n",
- "\n",
- "Transform the data into a structured knowledge graph:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "await cognee.cognify()"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The graph now contains nodes and relationships derived from the dataset, creating a powerful structure for exploration.\n",
- "\n",
- "### 6. Performing Searches\n",
- "\n",
- "### Answer prompt based on knowledge graph approach:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "from cognee.api.v1.search import SearchType\n",
- "search_results = await cognee.search(query_type=SearchType.GRAPH_COMPLETION, query_text=\"Tell me who are the people mentioned?\")\n",
- "\n",
- "print(\"\\n\\nAnswer based on knowledge graph:\\n\")\n",
- "for result in search_results:\n",
- " print(f\"{result}\\n\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Answer prompt based on RAG approach:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "search_results = await cognee.search(query_type=SearchType.RAG_COMPLETION, query_text=\"Tell me who are the people mentioned?\")\n",
- "\n",
- "print(\"\\n\\nAnswer based on RAG:\\n\")\n",
- "for result in search_results:\n",
- " print(f\"{result}\\n\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In conclusion, the results demonstrate a significant advantage of the knowledge graph-based approach (Graphrag) over the RAG approach. Graphrag successfully identified all the mentioned individuals across multiple documents, showcasing its ability to aggregate and infer information from a global context. In contrast, the RAG approach was limited to identifying individuals within a single document due to its chunking-based processing constraints. This highlights Graphrag's superior capability in comprehensively resolving queries that span across a broader corpus of interconnected data."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 7. Finding Related Nodes\n",
- "\n",
- "Explore relationships in the knowledge graph:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "related_nodes = await cognee.search(query_type=SearchType.INSIGHTS, query_text=\"person\")\n",
- "\n",
- "print(\"\\n\\nRelated nodes are:\\n\")\n",
- "for node in related_nodes:\n",
- " print(f\"{node}\\n\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Why Choose Cognee?\n",
- "\n",
- "### 1. Agentic Framework and Memory tied together\n",
- "\n",
- "Your agents can now get long-term, short-term memory and memory specific to their domains\n",
- "\n",
- "### 2. Enhanced Querying and Insights\n",
- "\n",
- "Your memory can now automatically optimize itself and allow to respond to questions better\n",
- "\n",
- "### 3. Simplified Deployment\n",
- "\n",
- "You can use the standard tools out of the box and get things done without much effort\n",
- "\n",
- "## Visualizing the Knowledge Graph\n",
- "\n",
- "Imagine a graph structure where each node represents a document or entity, and edges indicate relationships.\n",
- "\n",
- "Here’s the visualized knowledge graph from the simple example above:\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "## Conclusion\n",
- "\n",
- "Try running it yourself\n",
- "\n",
- "[join the cognee community](https://discord.gg/tV7pr5XSj7)"
- ]
- }
- ],
- "metadata": {
- "kernelspec": {
- "display_name": "Python 3 (ipykernel)",
- "language": "python",
- "name": "python3"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
diff --git a/notebooks/llama_index_cognee_integration.ipynb b/notebooks/llama_index_cognee_integration.ipynb
deleted file mode 100644
index be6c4ea13..000000000
--- a/notebooks/llama_index_cognee_integration.ipynb
+++ /dev/null
@@ -1,288 +0,0 @@
-{
- "cells": [
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "[](https://colab.research.google.com/drive/1EpokQ8Y_5jIJ7HdixZms81Oqgh2sp7-E?usp=sharing)"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## LlamaIndex Cognee GraphRAG Integration\n",
- "\n",
- "Connecting external knowledge to the LLM efficiently and retrieving it is a key challenge faced by developers. For developers and data scientists, integrating structured and unstructured data into AI workflows often involves multiple tools, complex pipelines, and time-consuming processes.\n",
- "\n",
- "Enter **cognee,** a powerful framework for knowledge and memory management, and LlamaIndex, a versatile data integration library. Together, they enable us to transform retrieval-augmented generation (RAG) pipelines, into GraphRAG pipelines, streamlining the path from raw data to actionable insights.\n",
- "\n",
- "In this post, we’ll explore a demo that leverages cognee and LlamaIndex to create a knowledge graph from a LlamaIndex document, process it into a meaningful structure, and extract useful insights. By the end, you’ll see how these tools can give you new insights into your data by connecting various data sources in one big semantic layer you can analyze.\n",
- "\n",
- "## RAG - Recap\n",
- "\n",
- "RAG enhances LLMs by integrating external knowledge sources during inference. It does so by turning the data into a vector representation and storing it in a vector store.\n",
- "\n",
- "### Key Benefits of RAG:\n",
- "\n",
- "1. Connecting domain specific data to LLMs\n",
- "2. Cost savings\n",
- "3. Higher accuracy than base LLM\n",
- "\n",
- "However, building a RAG system presents challenges: handling diverse data formats, data updates, creating a robust metadata layer, and mediocre accuracy\n",
- "\n",
- "## Introducing cognee and LlamaIndex more\n",
- "\n",
- "cognee simplifies knowledge and memory management for LLMs, while LlamaIndex facilitates connecting LLMs to structured data sources and enabling agentic use-cases\n",
- "\n",
- "cognee is inspired by human mind and higer cognitive functions. It mimics ways we construct our mental map of the world and build a semantic understanding of various objects, terms and issues in our everyday lives.\n",
- "\n",
- "cognee brings this approach to code by allowing developers to create semantic layers that would allow users to store their ontologies which are **a formalised depiction of knowledge** in graphs.\n",
- "\n",
- "This lets you use the knowledge you have about a system connect it to LLMs in a modular way, with best data engineering practices, wide choice of vector and graph stores and various LLMs you can use.\n",
- "\n",
- "Together, they:\n",
- "\n",
- "- Turn unstructured and semi-structured data into a graph/vector representation.\n",
- "- Enable ontology generation for particular domains, making unique graphs for every vertical\n",
- "- Provide a deterministic layer for LLM outputs, ensuring consistency and reliability.\n",
- "\n",
- "## Step-by-Step Demo: Building a RAG System with Cognee and LlamaIndex\n",
- "\n",
- "### 1. Setting Up the Environment\n",
- "\n",
- "Start by importing the required libraries and defining the environment:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "!pip install llama-index-graph-rag-cognee==0.1.3"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "import os\n",
- "import asyncio\n",
- "from llama_index.core import Document\n",
- "from llama_index.graph_rag.cognee import CogneeGraphRAG\n",
- "\n",
- "if \"OPENAI_API_KEY\" not in os.environ:\n",
- " os.environ[\"OPENAI_API_KEY\"] = \"\""
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "Ensure you’ve set up your API keys and installed necessary dependencies.\n",
- "\n",
- "### 2. Preparing the Dataset\n",
- "\n",
- "We’ll use a brief profile of an individual as our sample dataset:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "documents = [\n",
- " Document(\n",
- " text=\"Jessica Miller, Experienced Sales Manager with a strong track record in driving sales growth and building high-performing teams.\"\n",
- " ),\n",
- " Document(\n",
- " text=\"David Thompson, Creative Graphic Designer with over 8 years of experience in visual design and branding.\"\n",
- " ),\n",
- "]"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 3. Initializing CogneeGraphRAG\n",
- "\n",
- "Instantiate the Cognee framework with configurations for LLM, graph, and database providers:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "cogneeRAG = CogneeGraphRAG(\n",
- " llm_api_key=os.environ[\"OPENAI_API_KEY\"],\n",
- " llm_provider=\"openai\",\n",
- " llm_model=\"gpt-4o-mini\",\n",
- " graph_db_provider=\"networkx\",\n",
- " vector_db_provider=\"lancedb\",\n",
- " relational_db_provider=\"sqlite\",\n",
- " relational_db_name=\"cognee_db\",\n",
- ")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 4. Adding Data to Cognee\n",
- "\n",
- "Load the dataset into the cognee framework:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "await cogneeRAG.add(documents, \"test\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "This step prepares the data for graph-based processing.\n",
- "\n",
- "### 5. Processing Data into a Knowledge Graph\n",
- "\n",
- "Transform the data into a structured knowledge graph:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "await cogneeRAG.process_data(\"test\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "The graph now contains nodes and relationships derived from the dataset, creating a powerful structure for exploration.\n",
- "\n",
- "### 6. Performing Searches\n",
- "\n",
- "### Answer prompt based on knowledge graph approach:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "search_results = await cogneeRAG.search(\"Tell me who are the people mentioned?\")\n",
- "\n",
- "print(\"\\n\\nAnswer based on knowledge graph:\\n\")\n",
- "for result in search_results:\n",
- " print(f\"{result}\\n\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### Answer prompt based on RAG approach:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "search_results = await cogneeRAG.rag_search(\"Tell me who are the people mentioned?\")\n",
- "\n",
- "print(\"\\n\\nAnswer based on RAG:\\n\")\n",
- "for result in search_results:\n",
- " print(f\"{result}\\n\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "In conclusion, the results demonstrate a significant advantage of the knowledge graph-based approach (Graphrag) over the RAG approach. Graphrag successfully identified all the mentioned individuals across multiple documents, showcasing its ability to aggregate and infer information from a global context. In contrast, the RAG approach was limited to identifying individuals within a single document due to its chunking-based processing constraints. This highlights Graphrag's superior capability in comprehensively resolving queries that span across a broader corpus of interconnected data."
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "### 7. Finding Related Nodes\n",
- "\n",
- "Explore relationships in the knowledge graph:"
- ]
- },
- {
- "cell_type": "code",
- "execution_count": null,
- "metadata": {},
- "outputs": [],
- "source": [
- "related_nodes = await cogneeRAG.get_related_nodes(\"person\")\n",
- "\n",
- "print(\"\\n\\nRelated nodes are:\\n\")\n",
- "for node in related_nodes:\n",
- " print(f\"{node}\\n\")"
- ]
- },
- {
- "cell_type": "markdown",
- "metadata": {},
- "source": [
- "## Why Choose Cognee and LlamaIndex?\n",
- "\n",
- "### 1. Agentic Framework and Memory tied together\n",
- "\n",
- "Your agents can now get long-term, short-term memory and memory specific to their domains\n",
- "\n",
- "### 2. Enhanced Querying and Insights\n",
- "\n",
- "Your memory can now automatically optimize itself and allow to respond to questions better\n",
- "\n",
- "### 3. Simplified Deployment\n",
- "\n",
- "You can use the standard tools out of the box and get things done without much effort\n",
- "\n",
- "## Visualizing the Knowledge Graph\n",
- "\n",
- "Imagine a graph structure where each node represents a document or entity, and edges indicate relationships.\n",
- "\n",
- "Here’s the visualized knowledge graph from the simple example above:\n",
- "\n",
- "\n",
- "\n",
- "\n",
- "## Conclusion\n",
- "\n",
- "Try running it yourself\n",
- "\n",
- "[join the cognee community](https://discord.gg/tV7pr5XSj7)"
- ]
- }
- ],
- "metadata": {
- "language_info": {
- "name": "python",
- "version": "3.12.9"
- }
- },
- "nbformat": 4,
- "nbformat_minor": 2
-}
From c09750b7c6707f2023b59730e4847429ce067471 Mon Sep 17 00:00:00 2001
From: Igor Ilic <30923996+dexters1@users.noreply.github.com>
Date: Wed, 4 Jun 2025 20:48:03 +0200
Subject: [PATCH 07/15] feat: Ignore jupyter notebooks in language bar on
github (#906)
## Description
## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
---
.gitattributes | 2 ++
1 file changed, 2 insertions(+)
create mode 100644 .gitattributes
diff --git a/.gitattributes b/.gitattributes
new file mode 100644
index 000000000..8bdf6cf28
--- /dev/null
+++ b/.gitattributes
@@ -0,0 +1,2 @@
+# ignore jupyter notebooks in the language bar on github
+notebooks/** linguist-vendored
From fe0a4cb702ec7e088ab9c21bc00cf9fbfffeed0c Mon Sep 17 00:00:00 2001
From: Igor Ilic <30923996+dexters1@users.noreply.github.com>
Date: Thu, 5 Jun 2025 15:38:06 +0200
Subject: [PATCH 08/15] fix: default user (#908)
## Description
Resolve user already exist issue for UI
## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
---------
Co-authored-by: Boris Arzentar
---
.../versions/482cd6517ce4_add_default_user.py | 7 ++++-
.../ingestion/DatasetsView/DatasetsView.tsx | 4 +--
.../src/ui/Partials/SearchView/SearchView.tsx | 26 ++++++++++++++++---
cognee-frontend/tsconfig.json | 24 +++++++++++++----
.../v1/search/routers/get_search_router.py | 7 ++++-
entrypoint.sh | 4 +--
6 files changed, 57 insertions(+), 15 deletions(-)
diff --git a/alembic/versions/482cd6517ce4_add_default_user.py b/alembic/versions/482cd6517ce4_add_default_user.py
index 92429e1e4..d85f0f146 100644
--- a/alembic/versions/482cd6517ce4_add_default_user.py
+++ b/alembic/versions/482cd6517ce4_add_default_user.py
@@ -12,6 +12,8 @@ from sqlalchemy.util import await_only
from cognee.modules.users.methods import create_default_user, delete_user
+from fastapi_users.exceptions import UserAlreadyExists
+
# revision identifiers, used by Alembic.
revision: str = "482cd6517ce4"
@@ -21,7 +23,10 @@ depends_on: Union[str, Sequence[str], None] = "8057ae7329c2"
def upgrade() -> None:
- await_only(create_default_user())
+ try:
+ await_only(create_default_user())
+ except UserAlreadyExists:
+ pass # It's fine if the default user already exists
def downgrade() -> None:
diff --git a/cognee-frontend/src/modules/ingestion/DatasetsView/DatasetsView.tsx b/cognee-frontend/src/modules/ingestion/DatasetsView/DatasetsView.tsx
index 13965230b..577a27d63 100644
--- a/cognee-frontend/src/modules/ingestion/DatasetsView/DatasetsView.tsx
+++ b/cognee-frontend/src/modules/ingestion/DatasetsView/DatasetsView.tsx
@@ -95,10 +95,10 @@ export default function DatasetsView({
))}
-
+
{dataset?.name}
-
+
>
diff --git a/cognee-frontend/src/ui/Partials/SearchView/SearchView.tsx b/cognee-frontend/src/ui/Partials/SearchView/SearchView.tsx
index 664a46fad..bea5e1277 100644
--- a/cognee-frontend/src/ui/Partials/SearchView/SearchView.tsx
+++ b/cognee-frontend/src/ui/Partials/SearchView/SearchView.tsx
@@ -3,7 +3,7 @@
import { v4 } from 'uuid';
import classNames from 'classnames';
import { useCallback, useEffect, useState } from 'react';
-import { CTAButton, Stack, Text, DropdownSelect, TextArea, useBoolean } from 'ohmy-ui';
+import { CTAButton, Stack, Text, DropdownSelect, TextArea, useBoolean, Input } from 'ohmy-ui';
import { fetch } from '@/utils';
import styles from './SearchView.module.css';
import getHistory from '@/modules/chat/getHistory';
@@ -33,8 +33,15 @@ export default function SearchView() {
}, {
value: 'RAG_COMPLETION',
label: 'Completion using RAG',
+ }, {
+ value: 'GRAPH_COMPLETION_COT',
+ label: 'Cognee\'s Chain of Thought search',
+ }, {
+ value: 'GRAPH_COMPLETION_CONTEXT_EXTENSION',
+ label: 'Cognee\'s Multi-Hop search',
}];
const [searchType, setSearchType] = useState(searchOptions[0]);
+ const [rangeValue, setRangeValue] = useState(10);
const scrollToBottom = useCallback(() => {
setTimeout(() => {
@@ -90,6 +97,7 @@ export default function SearchView() {
body: JSON.stringify({
query: inputValue.trim(),
searchType: searchTypeValue,
+ topK: rangeValue,
}),
})
.then((response) => response.json())
@@ -108,7 +116,7 @@ export default function SearchView() {
.catch(() => {
setInputValue(inputValue);
});
- }, [inputValue, scrollToBottom, searchType.value]);
+ }, [inputValue, rangeValue, scrollToBottom, searchType.value]);
const {
value: isInputExpanded,
@@ -122,6 +130,10 @@ export default function SearchView() {
}
};
+ const handleRangeValueChange = (event: React.ChangeEvent) => {
+ setRangeValue(parseInt(event.target.value));
+ };
+
return (
@@ -146,9 +158,15 @@ export default function SearchView() {
diff --git a/cognee-frontend/tsconfig.json b/cognee-frontend/tsconfig.json
index 7b2858930..f48e7ee6f 100644
--- a/cognee-frontend/tsconfig.json
+++ b/cognee-frontend/tsconfig.json
@@ -1,6 +1,10 @@
{
"compilerOptions": {
- "lib": ["dom", "dom.iterable", "esnext"],
+ "lib": [
+ "dom",
+ "dom.iterable",
+ "esnext"
+ ],
"allowJs": true,
"skipLibCheck": true,
"strict": true,
@@ -18,9 +22,19 @@
}
],
"paths": {
- "@/*": ["./src/*"]
- }
+ "@/*": [
+ "./src/*"
+ ]
+ },
+ "target": "ES2017"
},
- "include": ["next-env.d.ts", "**/*.ts", "**/*.tsx", ".next/types/**/*.ts"],
- "exclude": ["node_modules"]
+ "include": [
+ "next-env.d.ts",
+ "**/*.ts",
+ "**/*.tsx",
+ ".next/types/**/*.ts"
+ ],
+ "exclude": [
+ "node_modules"
+ ]
}
diff --git a/cognee/api/v1/search/routers/get_search_router.py b/cognee/api/v1/search/routers/get_search_router.py
index cb3ef38a8..bd5abc859 100644
--- a/cognee/api/v1/search/routers/get_search_router.py
+++ b/cognee/api/v1/search/routers/get_search_router.py
@@ -1,3 +1,4 @@
+from typing import Optional
from uuid import UUID
from datetime import datetime
from fastapi import Depends, APIRouter
@@ -12,6 +13,7 @@ from cognee.modules.users.methods import get_authenticated_user
class SearchPayloadDTO(InDTO):
search_type: SearchType
query: str
+ top_k: Optional[int] = 10
def get_search_router() -> APIRouter:
@@ -39,7 +41,10 @@ def get_search_router() -> APIRouter:
try:
results = await cognee_search(
- query_text=payload.query, query_type=payload.search_type, user=user
+ query_text=payload.query,
+ query_type=payload.search_type,
+ user=user,
+ top_k=payload.top_k,
)
return results
diff --git a/entrypoint.sh b/entrypoint.sh
index 9cd81939c..31a2b328d 100755
--- a/entrypoint.sh
+++ b/entrypoint.sh
@@ -14,7 +14,7 @@ echo "Environment: $ENVIRONMENT"
# smooth redeployments and container restarts while maintaining data integrity.
echo "Running database migrations..."
-MIGRATION_OUTPUT=$(alembic upgrade head 2>&1)
+MIGRATION_OUTPUT=$(alembic upgrade head)
MIGRATION_EXIT_CODE=$?
if [[ $MIGRATION_EXIT_CODE -ne 0 ]]; then
@@ -42,5 +42,5 @@ if [ "$ENVIRONMENT" = "dev" ] || [ "$ENVIRONMENT" = "local" ]; then
gunicorn -w 3 -k uvicorn.workers.UvicornWorker -t 30000 --bind=0.0.0.0:8000 --log-level debug --reload cognee.api.client:app
fi
else
- gunicorn -w 3 -k uvicorn.workers.UvicornWorker -t 30000 --bind=0.0.0.0:8000 --log-level error cognee.api.client:app
+ gunicorn -w 3 -k uvicorn.workers.UvicornWorker -t 30000 --bind=0.0.0.0:8000 --log-level error cognee.api.client:app
fi
From 88f8aeed94dea0393dea8345b33c8d8f9abca0e4 Mon Sep 17 00:00:00 2001
From: Hande <159312713+hande-k@users.noreply.github.com>
Date: Thu, 5 Jun 2025 18:16:51 +0200
Subject: [PATCH 09/15] chore: add paper and reddit community (#911)
## Description
## DCO Affirmation
I affirm that all code in every commit of this pull request conforms to
the terms of the Topoteretes Developer Certificate of Origin.
---
README.md | 15 +++++++++++----
assets/cognee-paper.png | Bin 0 -> 90427 bytes
2 files changed, 11 insertions(+), 4 deletions(-)
create mode 100644 assets/cognee-paper.png
diff --git a/README.md b/README.md
index 18e19bb4a..eeee1145e 100644
--- a/README.md
+++ b/README.md
@@ -13,6 +13,8 @@
Learn more
·
Join Discord
+ ·
+ Join r/AIMemory
@@ -46,12 +48,9 @@ More on [use-cases](https://docs.cognee.ai/use-cases) and [evals](https://github
-
-
-
## Features
- Interconnect and retrieve your past conversations, documents, images and audio transcriptions
@@ -141,7 +140,15 @@ Example output:
```
-### cognee UI
+## Our paper is out! Read here
+
+
+

+
+
+
+
+## Cognee UI
You can also cognify your files and query using cognee UI.
diff --git a/assets/cognee-paper.png b/assets/cognee-paper.png
new file mode 100644
index 0000000000000000000000000000000000000000..df7113b0ec248b945447e7600735950db43fced7
GIT binary patch
literal 90427
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