graphiti-core 0.21.0rc1__tar.gz → 0.21.0rc3__tar.gz
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/PKG-INFO +1 -1
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/driver.py +97 -43
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/neo4j_driver.py +18 -7
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/neptune_driver.py +2 -2
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/edges.py +47 -5
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/graphiti.py +21 -5
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/nodes.py +99 -9
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_filters.py +8 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_utils.py +130 -106
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/bulk_utils.py +30 -11
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/edge_operations.py +39 -5
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/graph_data_operations.py +5 -3
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/pyproject.toml +1 -1
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/signatures/version1/cla.json +16 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/uv.lock +1 -1
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.env.example +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/dependabot.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/pull_request_template.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/secret_scanning.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/ai-moderator.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/cla.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/claude-code-review.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/claude.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/codeql.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/lint.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/mcp-server-docker.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/release-graphiti-core.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/typecheck.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/unit_tests.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.gitignore +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/CLAUDE.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/CODE_OF_CONDUCT.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/CONTRIBUTING.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/Dockerfile +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/LICENSE +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/Makefile +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/README.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/SECURITY.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/Zep-CLA.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/conftest.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/depot.json +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/docker-compose.test.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/docker-compose.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/ellipsis.yaml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/data/manybirds_products.json +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/ecommerce/runner.ipynb +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/ecommerce/runner.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/langgraph-agent/agent.ipynb +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/langgraph-agent/tinybirds-jess.png +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/podcast/podcast_runner.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/podcast/podcast_transcript.txt +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/podcast/transcript_parser.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/README.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/quickstart_falkordb.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/quickstart_neo4j.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/quickstart_neptune.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/requirements.txt +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/wizard_of_oz/parser.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/wizard_of_oz/runner.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/wizard_of_oz/woo.txt +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/bge_reranker_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/gemini_reranker_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/falkordb_driver.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/kuzu_driver.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/azure_openai.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/gemini.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/openai.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/voyage.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/errors.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/graph_queries.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/graphiti_types.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/helpers.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/anthropic_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/azure_openai_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/config.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/errors.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/gemini_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/groq_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/openai_base_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/openai_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/openai_generic_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/utils.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/migrations/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/edges/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/edges/edge_db_queries.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/nodes/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/nodes/node_db_queries.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/dedupe_edges.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/dedupe_nodes.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/eval.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/extract_edge_dates.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/extract_edges.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/extract_nodes.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/invalidate_edges.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/lib.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/models.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/prompt_helpers.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/summarize_nodes.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/py.typed +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_config.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_config_recipes.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_helpers.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/telemetry/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/telemetry/telemetry.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/datetime_utils.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/community_operations.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/node_operations.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/temporal_operations.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/utils.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/ontology_utils/entity_types_utils.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/arxiv-screenshot.png +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/graphiti-graph-intro.gif +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/graphiti-intro-slides-stock-2.gif +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/simple_graph.svg +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/.env.example +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/.python-version +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/Dockerfile +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/README.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/cursor_rules.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/docker-compose.yml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/graphiti_mcp_server.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/mcp_config_sse_example.json +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/mcp_config_stdio_example.json +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/pyproject.toml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/uv.lock +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/poetry.lock +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/py.typed +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/pytest.ini +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/.env.example +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/Makefile +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/README.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/config.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/common.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/ingest.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/retrieve.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/main.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/routers/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/routers/ingest.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/routers/retrieve.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/zep_graphiti.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/pyproject.toml +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/uv.lock +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/cross_encoder/test_bge_reranker_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/cross_encoder/test_gemini_reranker_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/driver/__init__.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/driver/test_falkordb_driver.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/embedder_fixtures.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/test_gemini.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/test_openai.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/test_voyage.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/data/longmemeval_data/README.md +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/data/longmemeval_data/longmemeval_oracle.json +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/eval_cli.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/eval_e2e_graph_building.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/pytest.ini +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/utils.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/helpers_test.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_anthropic_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_anthropic_client_int.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_errors.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_gemini_client.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_edge_int.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_entity_exclusion_int.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_graphiti_int.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_graphiti_mock.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_node_int.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/utils/maintenance/test_edge_operations.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/utils/maintenance/test_temporal_operations_int.py +0 -0
- {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/utils/search/search_utils_test.py +0 -0
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
Metadata-Version: 2.4
|
|
2
2
|
Name: graphiti-core
|
|
3
|
-
Version: 0.21.
|
|
3
|
+
Version: 0.21.0rc3
|
|
4
4
|
Summary: A temporal graph building library
|
|
5
5
|
Project-URL: Homepage, https://help.getzep.com/graphiti/graphiti/overview
|
|
6
6
|
Project-URL: Repository, https://github.com/getzep/graphiti
|
|
@@ -17,16 +17,19 @@ limitations under the License.
|
|
|
17
17
|
import asyncio
|
|
18
18
|
import copy
|
|
19
19
|
import logging
|
|
20
|
+
import os
|
|
20
21
|
from abc import ABC, abstractmethod
|
|
21
22
|
from collections.abc import Coroutine
|
|
22
23
|
from datetime import datetime
|
|
23
24
|
from enum import Enum
|
|
24
25
|
from typing import Any
|
|
25
26
|
|
|
27
|
+
from dotenv import load_dotenv
|
|
28
|
+
|
|
26
29
|
from graphiti_core.embedder.client import EMBEDDING_DIM
|
|
27
30
|
|
|
28
31
|
try:
|
|
29
|
-
from opensearchpy import
|
|
32
|
+
from opensearchpy import AsyncOpenSearch, helpers
|
|
30
33
|
|
|
31
34
|
_HAS_OPENSEARCH = True
|
|
32
35
|
except ImportError:
|
|
@@ -38,6 +41,13 @@ logger = logging.getLogger(__name__)
|
|
|
38
41
|
|
|
39
42
|
DEFAULT_SIZE = 10
|
|
40
43
|
|
|
44
|
+
load_dotenv()
|
|
45
|
+
|
|
46
|
+
ENTITY_INDEX_NAME = os.environ.get('ENTITY_INDEX_NAME', 'entities')
|
|
47
|
+
EPISODE_INDEX_NAME = os.environ.get('EPISODE_INDEX_NAME', 'episodes')
|
|
48
|
+
COMMUNITY_INDEX_NAME = os.environ.get('COMMUNITY_INDEX_NAME', 'communities')
|
|
49
|
+
ENTITY_EDGE_INDEX_NAME = os.environ.get('ENTITY_EDGE_INDEX_NAME', 'entity_edges')
|
|
50
|
+
|
|
41
51
|
|
|
42
52
|
class GraphProvider(Enum):
|
|
43
53
|
NEO4J = 'neo4j'
|
|
@@ -48,20 +58,19 @@ class GraphProvider(Enum):
|
|
|
48
58
|
|
|
49
59
|
aoss_indices = [
|
|
50
60
|
{
|
|
51
|
-
'index_name':
|
|
61
|
+
'index_name': ENTITY_INDEX_NAME,
|
|
52
62
|
'body': {
|
|
63
|
+
'settings': {'index': {'knn': True}},
|
|
53
64
|
'mappings': {
|
|
54
65
|
'properties': {
|
|
55
66
|
'uuid': {'type': 'keyword'},
|
|
56
67
|
'name': {'type': 'text'},
|
|
57
68
|
'summary': {'type': 'text'},
|
|
58
|
-
'group_id': {'type': '
|
|
59
|
-
'created_at': {'type': 'date', 'format':
|
|
69
|
+
'group_id': {'type': 'keyword'},
|
|
70
|
+
'created_at': {'type': 'date', 'format': 'strict_date_optional_time_nanos'},
|
|
60
71
|
'name_embedding': {
|
|
61
72
|
'type': 'knn_vector',
|
|
62
|
-
'
|
|
63
|
-
'index': True,
|
|
64
|
-
'similarity': 'cosine',
|
|
73
|
+
'dimension': EMBEDDING_DIM,
|
|
65
74
|
'method': {
|
|
66
75
|
'engine': 'faiss',
|
|
67
76
|
'space_type': 'cosinesimil',
|
|
@@ -70,23 +79,23 @@ aoss_indices = [
|
|
|
70
79
|
},
|
|
71
80
|
},
|
|
72
81
|
}
|
|
73
|
-
}
|
|
82
|
+
},
|
|
74
83
|
},
|
|
75
84
|
},
|
|
76
85
|
{
|
|
77
|
-
'index_name':
|
|
86
|
+
'index_name': COMMUNITY_INDEX_NAME,
|
|
78
87
|
'body': {
|
|
79
88
|
'mappings': {
|
|
80
89
|
'properties': {
|
|
81
90
|
'uuid': {'type': 'keyword'},
|
|
82
91
|
'name': {'type': 'text'},
|
|
83
|
-
'group_id': {'type': '
|
|
92
|
+
'group_id': {'type': 'keyword'},
|
|
84
93
|
}
|
|
85
94
|
}
|
|
86
95
|
},
|
|
87
96
|
},
|
|
88
97
|
{
|
|
89
|
-
'index_name':
|
|
98
|
+
'index_name': EPISODE_INDEX_NAME,
|
|
90
99
|
'body': {
|
|
91
100
|
'mappings': {
|
|
92
101
|
'properties': {
|
|
@@ -94,31 +103,30 @@ aoss_indices = [
|
|
|
94
103
|
'content': {'type': 'text'},
|
|
95
104
|
'source': {'type': 'text'},
|
|
96
105
|
'source_description': {'type': 'text'},
|
|
97
|
-
'group_id': {'type': '
|
|
98
|
-
'created_at': {'type': 'date', 'format':
|
|
99
|
-
'valid_at': {'type': 'date', 'format':
|
|
106
|
+
'group_id': {'type': 'keyword'},
|
|
107
|
+
'created_at': {'type': 'date', 'format': 'strict_date_optional_time_nanos'},
|
|
108
|
+
'valid_at': {'type': 'date', 'format': 'strict_date_optional_time_nanos'},
|
|
100
109
|
}
|
|
101
110
|
}
|
|
102
111
|
},
|
|
103
112
|
},
|
|
104
113
|
{
|
|
105
|
-
'index_name':
|
|
114
|
+
'index_name': ENTITY_EDGE_INDEX_NAME,
|
|
106
115
|
'body': {
|
|
116
|
+
'settings': {'index': {'knn': True}},
|
|
107
117
|
'mappings': {
|
|
108
118
|
'properties': {
|
|
109
119
|
'uuid': {'type': 'keyword'},
|
|
110
120
|
'name': {'type': 'text'},
|
|
111
121
|
'fact': {'type': 'text'},
|
|
112
|
-
'group_id': {'type': '
|
|
113
|
-
'created_at': {'type': 'date', 'format':
|
|
114
|
-
'valid_at': {'type': 'date', 'format':
|
|
115
|
-
'expired_at': {'type': 'date', 'format':
|
|
116
|
-
'invalid_at': {'type': 'date', 'format':
|
|
122
|
+
'group_id': {'type': 'keyword'},
|
|
123
|
+
'created_at': {'type': 'date', 'format': 'strict_date_optional_time_nanos'},
|
|
124
|
+
'valid_at': {'type': 'date', 'format': 'strict_date_optional_time_nanos'},
|
|
125
|
+
'expired_at': {'type': 'date', 'format': 'strict_date_optional_time_nanos'},
|
|
126
|
+
'invalid_at': {'type': 'date', 'format': 'strict_date_optional_time_nanos'},
|
|
117
127
|
'fact_embedding': {
|
|
118
128
|
'type': 'knn_vector',
|
|
119
|
-
'
|
|
120
|
-
'index': True,
|
|
121
|
-
'similarity': 'cosine',
|
|
129
|
+
'dimension': EMBEDDING_DIM,
|
|
122
130
|
'method': {
|
|
123
131
|
'engine': 'faiss',
|
|
124
132
|
'space_type': 'cosinesimil',
|
|
@@ -127,7 +135,7 @@ aoss_indices = [
|
|
|
127
135
|
},
|
|
128
136
|
},
|
|
129
137
|
}
|
|
130
|
-
}
|
|
138
|
+
},
|
|
131
139
|
},
|
|
132
140
|
},
|
|
133
141
|
]
|
|
@@ -163,7 +171,7 @@ class GraphDriver(ABC):
|
|
|
163
171
|
'' # Neo4j (default) syntax does not require a prefix for fulltext queries
|
|
164
172
|
)
|
|
165
173
|
_database: str
|
|
166
|
-
aoss_client:
|
|
174
|
+
aoss_client: AsyncOpenSearch | None # type: ignore
|
|
167
175
|
|
|
168
176
|
@abstractmethod
|
|
169
177
|
def execute_query(self, cypher_query_: str, **kwargs: Any) -> Coroutine:
|
|
@@ -205,7 +213,7 @@ class GraphDriver(ABC):
|
|
|
205
213
|
alias_name = index['index_name']
|
|
206
214
|
|
|
207
215
|
# If alias already exists, skip (idempotent behavior)
|
|
208
|
-
if client.indices.exists_alias(name=alias_name):
|
|
216
|
+
if await client.indices.exists_alias(name=alias_name):
|
|
209
217
|
continue
|
|
210
218
|
|
|
211
219
|
# Build a physical index name with timestamp
|
|
@@ -213,27 +221,67 @@ class GraphDriver(ABC):
|
|
|
213
221
|
physical_index_name = f'{alias_name}_{ts_suffix}'
|
|
214
222
|
|
|
215
223
|
# Create the index
|
|
216
|
-
client.indices.create(index=physical_index_name, body=index['body'])
|
|
224
|
+
await client.indices.create(index=physical_index_name, body=index['body'])
|
|
217
225
|
|
|
218
226
|
# Point alias to it
|
|
219
|
-
client.indices.put_alias(index=physical_index_name, name=alias_name)
|
|
227
|
+
await client.indices.put_alias(index=physical_index_name, name=alias_name)
|
|
220
228
|
|
|
221
229
|
# Allow some time for index creation
|
|
222
|
-
await asyncio.sleep(
|
|
230
|
+
await asyncio.sleep(1)
|
|
223
231
|
|
|
224
232
|
async def delete_aoss_indices(self):
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
233
|
+
client = self.aoss_client
|
|
234
|
+
|
|
235
|
+
if not client:
|
|
236
|
+
logger.warning('No OpenSearch client found')
|
|
237
|
+
return
|
|
238
|
+
|
|
239
|
+
for entry in aoss_indices:
|
|
240
|
+
alias_name = entry['index_name']
|
|
241
|
+
|
|
242
|
+
try:
|
|
243
|
+
# Resolve alias → indices
|
|
244
|
+
alias_info = await client.indices.get_alias(name=alias_name)
|
|
245
|
+
indices = list(alias_info.keys())
|
|
246
|
+
|
|
247
|
+
if not indices:
|
|
248
|
+
logger.info(f"No indices found for alias '{alias_name}'")
|
|
249
|
+
continue
|
|
228
250
|
|
|
229
|
-
|
|
230
|
-
|
|
231
|
-
|
|
251
|
+
for index in indices:
|
|
252
|
+
if await client.indices.exists(index=index):
|
|
253
|
+
await client.indices.delete(index=index)
|
|
254
|
+
logger.info(f"Deleted index '{index}' (alias: {alias_name})")
|
|
255
|
+
else:
|
|
256
|
+
logger.warning(f"Index '{index}' not found for alias '{alias_name}'")
|
|
232
257
|
|
|
233
|
-
|
|
234
|
-
|
|
258
|
+
except Exception as e:
|
|
259
|
+
logger.error(f"Error deleting indices for alias '{alias_name}': {e}")
|
|
235
260
|
|
|
236
|
-
def
|
|
261
|
+
async def clear_aoss_indices(self):
|
|
262
|
+
client = self.aoss_client
|
|
263
|
+
|
|
264
|
+
if not client:
|
|
265
|
+
logger.warning('No OpenSearch client found')
|
|
266
|
+
return
|
|
267
|
+
|
|
268
|
+
for index in aoss_indices:
|
|
269
|
+
index_name = index['index_name']
|
|
270
|
+
|
|
271
|
+
if await client.indices.exists(index=index_name):
|
|
272
|
+
try:
|
|
273
|
+
# Delete all documents but keep the index
|
|
274
|
+
response = await client.delete_by_query(
|
|
275
|
+
index=index_name,
|
|
276
|
+
body={'query': {'match_all': {}}},
|
|
277
|
+
)
|
|
278
|
+
logger.info(f"Cleared index '{index_name}': {response}")
|
|
279
|
+
except Exception as e:
|
|
280
|
+
logger.error(f"Error clearing index '{index_name}': {e}")
|
|
281
|
+
else:
|
|
282
|
+
logger.warning(f"Index '{index_name}' does not exist")
|
|
283
|
+
|
|
284
|
+
async def save_to_aoss(self, name: str, data: list[dict]) -> int:
|
|
237
285
|
client = self.aoss_client
|
|
238
286
|
if not client or not helpers:
|
|
239
287
|
logger.warning('No OpenSearch client found')
|
|
@@ -243,16 +291,22 @@ class GraphDriver(ABC):
|
|
|
243
291
|
if name.lower() == index['index_name']:
|
|
244
292
|
to_index = []
|
|
245
293
|
for d in data:
|
|
294
|
+
doc = {}
|
|
295
|
+
for p in index['body']['mappings']['properties']:
|
|
296
|
+
if p in d: # protect against missing fields
|
|
297
|
+
doc[p] = d[p]
|
|
298
|
+
|
|
246
299
|
item = {
|
|
247
300
|
'_index': name,
|
|
248
|
-
'
|
|
301
|
+
'_id': d['uuid'],
|
|
302
|
+
'_routing': d.get('group_id'),
|
|
303
|
+
'_source': doc,
|
|
249
304
|
}
|
|
250
|
-
for p in index['body']['mappings']['properties']:
|
|
251
|
-
if p in d: # protect against missing fields
|
|
252
|
-
item[p] = d[p]
|
|
253
305
|
to_index.append(item)
|
|
254
306
|
|
|
255
|
-
success, failed = helpers.
|
|
307
|
+
success, failed = await helpers.async_bulk(
|
|
308
|
+
client, to_index, stats_only=True, request_timeout=60
|
|
309
|
+
)
|
|
256
310
|
|
|
257
311
|
return success if failed == 0 else success
|
|
258
312
|
|
|
@@ -28,7 +28,13 @@ logger = logging.getLogger(__name__)
|
|
|
28
28
|
|
|
29
29
|
try:
|
|
30
30
|
import boto3
|
|
31
|
-
from opensearchpy import
|
|
31
|
+
from opensearchpy import (
|
|
32
|
+
AIOHttpConnection,
|
|
33
|
+
AsyncOpenSearch,
|
|
34
|
+
AWSV4SignerAuth,
|
|
35
|
+
Urllib3AWSV4SignerAuth,
|
|
36
|
+
Urllib3HttpConnection,
|
|
37
|
+
)
|
|
32
38
|
|
|
33
39
|
_HAS_OPENSEARCH = True
|
|
34
40
|
except ImportError:
|
|
@@ -50,6 +56,9 @@ class Neo4jDriver(GraphDriver):
|
|
|
50
56
|
database: str = 'neo4j',
|
|
51
57
|
aoss_host: str | None = None,
|
|
52
58
|
aoss_port: int | None = None,
|
|
59
|
+
aws_profile_name: str | None = None,
|
|
60
|
+
aws_region: str | None = None,
|
|
61
|
+
aws_service: str | None = None,
|
|
53
62
|
):
|
|
54
63
|
super().__init__()
|
|
55
64
|
self.client = AsyncGraphDatabase.driver(
|
|
@@ -61,15 +70,17 @@ class Neo4jDriver(GraphDriver):
|
|
|
61
70
|
self.aoss_client = None
|
|
62
71
|
if aoss_host and aoss_port and boto3 is not None:
|
|
63
72
|
try:
|
|
64
|
-
|
|
65
|
-
|
|
73
|
+
region = aws_region
|
|
74
|
+
service = aws_service
|
|
75
|
+
credentials = boto3.Session(profile_name=aws_profile_name).get_credentials()
|
|
76
|
+
auth = AWSV4SignerAuth(credentials, region or '', service or '')
|
|
77
|
+
|
|
78
|
+
self.aoss_client = AsyncOpenSearch(
|
|
66
79
|
hosts=[{'host': aoss_host, 'port': aoss_port}],
|
|
67
|
-
|
|
68
|
-
session.get_credentials(), session.region_name, 'aoss'
|
|
69
|
-
),
|
|
80
|
+
auth=auth,
|
|
70
81
|
use_ssl=True,
|
|
71
82
|
verify_certs=True,
|
|
72
|
-
connection_class=
|
|
83
|
+
connection_class=AIOHttpConnection,
|
|
73
84
|
pool_maxsize=20,
|
|
74
85
|
) # type: ignore
|
|
75
86
|
except Exception as e:
|
|
@@ -237,12 +237,12 @@ class NeptuneDriver(GraphDriver):
|
|
|
237
237
|
'You must provide an AOSS endpoint to create an OpenSearch driver.'
|
|
238
238
|
)
|
|
239
239
|
if not client.indices.exists(index=index_name):
|
|
240
|
-
client.indices.create(index=index_name, body=index['body'])
|
|
240
|
+
await client.indices.create(index=index_name, body=index['body'])
|
|
241
241
|
|
|
242
242
|
alias_name = index.get('alias_name', index_name)
|
|
243
243
|
|
|
244
244
|
if not client.indices.exists_alias(name=alias_name, index=index_name):
|
|
245
|
-
client.indices.put_alias(index=index_name, name=alias_name)
|
|
245
|
+
await client.indices.put_alias(index=index_name, name=alias_name)
|
|
246
246
|
|
|
247
247
|
# Sleep for 1 minute to let the index creation complete
|
|
248
248
|
await asyncio.sleep(60)
|
|
@@ -25,7 +25,7 @@ from uuid import uuid4
|
|
|
25
25
|
from pydantic import BaseModel, Field
|
|
26
26
|
from typing_extensions import LiteralString
|
|
27
27
|
|
|
28
|
-
from graphiti_core.driver.driver import GraphDriver, GraphProvider
|
|
28
|
+
from graphiti_core.driver.driver import ENTITY_EDGE_INDEX_NAME, GraphDriver, GraphProvider
|
|
29
29
|
from graphiti_core.embedder import EmbedderClient
|
|
30
30
|
from graphiti_core.errors import EdgeNotFoundError, GroupsEdgesNotFoundError
|
|
31
31
|
from graphiti_core.helpers import parse_db_date
|
|
@@ -77,6 +77,13 @@ class Edge(BaseModel, ABC):
|
|
|
77
77
|
uuid=self.uuid,
|
|
78
78
|
)
|
|
79
79
|
|
|
80
|
+
if driver.aoss_client:
|
|
81
|
+
await driver.aoss_client.delete(
|
|
82
|
+
index=ENTITY_EDGE_INDEX_NAME,
|
|
83
|
+
id=self.uuid,
|
|
84
|
+
params={'routing': self.group_id},
|
|
85
|
+
)
|
|
86
|
+
|
|
80
87
|
logger.debug(f'Deleted Edge: {self.uuid}')
|
|
81
88
|
|
|
82
89
|
@classmethod
|
|
@@ -108,6 +115,12 @@ class Edge(BaseModel, ABC):
|
|
|
108
115
|
uuids=uuids,
|
|
109
116
|
)
|
|
110
117
|
|
|
118
|
+
if driver.aoss_client:
|
|
119
|
+
await driver.aoss_client.delete_by_query(
|
|
120
|
+
index=ENTITY_EDGE_INDEX_NAME,
|
|
121
|
+
body={'query': {'terms': {'uuid': uuids}}},
|
|
122
|
+
)
|
|
123
|
+
|
|
111
124
|
logger.debug(f'Deleted Edges: {uuids}')
|
|
112
125
|
|
|
113
126
|
def __hash__(self):
|
|
@@ -256,13 +269,13 @@ class EntityEdge(Edge):
|
|
|
256
269
|
RETURN [x IN split(e.fact_embedding, ",") | toFloat(x)] as fact_embedding
|
|
257
270
|
"""
|
|
258
271
|
elif driver.aoss_client:
|
|
259
|
-
resp = driver.aoss_client.search(
|
|
272
|
+
resp = await driver.aoss_client.search(
|
|
260
273
|
body={
|
|
261
274
|
'query': {'multi_match': {'query': self.uuid, 'fields': ['uuid']}},
|
|
262
275
|
'size': 1,
|
|
263
276
|
},
|
|
264
|
-
index=
|
|
265
|
-
routing
|
|
277
|
+
index=ENTITY_EDGE_INDEX_NAME,
|
|
278
|
+
params={'routing': self.group_id},
|
|
266
279
|
)
|
|
267
280
|
|
|
268
281
|
if resp['hits']['hits']:
|
|
@@ -314,7 +327,7 @@ class EntityEdge(Edge):
|
|
|
314
327
|
edge_data.update(self.attributes or {})
|
|
315
328
|
|
|
316
329
|
if driver.aoss_client:
|
|
317
|
-
driver.save_to_aoss(
|
|
330
|
+
await driver.save_to_aoss(ENTITY_EDGE_INDEX_NAME, [edge_data]) # pyright: ignore reportAttributeAccessIssue
|
|
318
331
|
|
|
319
332
|
result = await driver.execute_query(
|
|
320
333
|
get_entity_edge_save_query(driver.provider),
|
|
@@ -351,6 +364,35 @@ class EntityEdge(Edge):
|
|
|
351
364
|
raise EdgeNotFoundError(uuid)
|
|
352
365
|
return edges[0]
|
|
353
366
|
|
|
367
|
+
@classmethod
|
|
368
|
+
async def get_between_nodes(
|
|
369
|
+
cls, driver: GraphDriver, source_node_uuid: str, target_node_uuid: str
|
|
370
|
+
):
|
|
371
|
+
match_query = """
|
|
372
|
+
MATCH (n:Entity {uuid: $source_node_uuid})-[e:RELATES_TO]->(m:Entity {uuid: $target_node_uuid})
|
|
373
|
+
"""
|
|
374
|
+
if driver.provider == GraphProvider.KUZU:
|
|
375
|
+
match_query = """
|
|
376
|
+
MATCH (n:Entity {uuid: $source_node_uuid})
|
|
377
|
+
-[:RELATES_TO]->(e:RelatesToNode_)
|
|
378
|
+
-[:RELATES_TO]->(m:Entity {uuid: $target_node_uuid})
|
|
379
|
+
"""
|
|
380
|
+
|
|
381
|
+
records, _, _ = await driver.execute_query(
|
|
382
|
+
match_query
|
|
383
|
+
+ """
|
|
384
|
+
RETURN
|
|
385
|
+
"""
|
|
386
|
+
+ get_entity_edge_return_query(driver.provider),
|
|
387
|
+
source_node_uuid=source_node_uuid,
|
|
388
|
+
target_node_uuid=target_node_uuid,
|
|
389
|
+
routing_='r',
|
|
390
|
+
)
|
|
391
|
+
|
|
392
|
+
edges = [get_entity_edge_from_record(record, driver.provider) for record in records]
|
|
393
|
+
|
|
394
|
+
return edges
|
|
395
|
+
|
|
354
396
|
@classmethod
|
|
355
397
|
async def get_by_uuids(cls, driver: GraphDriver, uuids: list[str]):
|
|
356
398
|
if len(uuids) == 0:
|
|
@@ -60,9 +60,7 @@ from graphiti_core.search.search_config_recipes import (
|
|
|
60
60
|
from graphiti_core.search.search_filters import SearchFilters
|
|
61
61
|
from graphiti_core.search.search_utils import (
|
|
62
62
|
RELEVANT_SCHEMA_LIMIT,
|
|
63
|
-
get_edge_invalidation_candidates,
|
|
64
63
|
get_mentioned_nodes,
|
|
65
|
-
get_relevant_edges,
|
|
66
64
|
)
|
|
67
65
|
from graphiti_core.telemetry import capture_event
|
|
68
66
|
from graphiti_core.utils.bulk_utils import (
|
|
@@ -1037,10 +1035,28 @@ class Graphiti:
|
|
|
1037
1035
|
|
|
1038
1036
|
updated_edge = resolve_edge_pointers([edge], uuid_map)[0]
|
|
1039
1037
|
|
|
1040
|
-
|
|
1038
|
+
valid_edges = await EntityEdge.get_between_nodes(
|
|
1039
|
+
self.driver, edge.source_node_uuid, edge.target_node_uuid
|
|
1040
|
+
)
|
|
1041
|
+
|
|
1042
|
+
related_edges = (
|
|
1043
|
+
await search(
|
|
1044
|
+
self.clients,
|
|
1045
|
+
updated_edge.fact,
|
|
1046
|
+
group_ids=[updated_edge.group_id],
|
|
1047
|
+
config=EDGE_HYBRID_SEARCH_RRF,
|
|
1048
|
+
search_filter=SearchFilters(edge_uuids=[edge.uuid for edge in valid_edges]),
|
|
1049
|
+
)
|
|
1050
|
+
).edges
|
|
1041
1051
|
existing_edges = (
|
|
1042
|
-
await
|
|
1043
|
-
|
|
1052
|
+
await search(
|
|
1053
|
+
self.clients,
|
|
1054
|
+
updated_edge.fact,
|
|
1055
|
+
group_ids=[updated_edge.group_id],
|
|
1056
|
+
config=EDGE_HYBRID_SEARCH_RRF,
|
|
1057
|
+
search_filter=SearchFilters(),
|
|
1058
|
+
)
|
|
1059
|
+
).edges
|
|
1044
1060
|
|
|
1045
1061
|
resolved_edge, invalidated_edges, _ = await resolve_extracted_edge(
|
|
1046
1062
|
self.llm_client,
|
|
@@ -26,7 +26,14 @@ from uuid import uuid4
|
|
|
26
26
|
from pydantic import BaseModel, Field
|
|
27
27
|
from typing_extensions import LiteralString
|
|
28
28
|
|
|
29
|
-
from graphiti_core.driver.driver import
|
|
29
|
+
from graphiti_core.driver.driver import (
|
|
30
|
+
COMMUNITY_INDEX_NAME,
|
|
31
|
+
ENTITY_EDGE_INDEX_NAME,
|
|
32
|
+
ENTITY_INDEX_NAME,
|
|
33
|
+
EPISODE_INDEX_NAME,
|
|
34
|
+
GraphDriver,
|
|
35
|
+
GraphProvider,
|
|
36
|
+
)
|
|
30
37
|
from graphiti_core.embedder import EmbedderClient
|
|
31
38
|
from graphiti_core.errors import NodeNotFoundError
|
|
32
39
|
from graphiti_core.helpers import parse_db_date
|
|
@@ -94,13 +101,39 @@ class Node(BaseModel, ABC):
|
|
|
94
101
|
async def delete(self, driver: GraphDriver):
|
|
95
102
|
match driver.provider:
|
|
96
103
|
case GraphProvider.NEO4J:
|
|
97
|
-
await driver.execute_query(
|
|
104
|
+
records, _, _ = await driver.execute_query(
|
|
98
105
|
"""
|
|
99
|
-
MATCH (n
|
|
106
|
+
MATCH (n {uuid: $uuid})
|
|
107
|
+
WHERE n:Entity OR n:Episodic OR n:Community
|
|
108
|
+
OPTIONAL MATCH (n)-[r]-()
|
|
109
|
+
WITH collect(r.uuid) AS edge_uuids, n
|
|
100
110
|
DETACH DELETE n
|
|
111
|
+
RETURN edge_uuids
|
|
101
112
|
""",
|
|
102
113
|
uuid=self.uuid,
|
|
103
114
|
)
|
|
115
|
+
|
|
116
|
+
edge_uuids: list[str] = records[0].get('edge_uuids', []) if records else []
|
|
117
|
+
|
|
118
|
+
if driver.aoss_client:
|
|
119
|
+
# Delete the node from OpenSearch indices
|
|
120
|
+
for index in (EPISODE_INDEX_NAME, ENTITY_INDEX_NAME, COMMUNITY_INDEX_NAME):
|
|
121
|
+
await driver.aoss_client.delete(
|
|
122
|
+
index=index,
|
|
123
|
+
id=self.uuid,
|
|
124
|
+
params={'routing': self.group_id},
|
|
125
|
+
)
|
|
126
|
+
|
|
127
|
+
# Bulk delete the detached edges
|
|
128
|
+
if edge_uuids:
|
|
129
|
+
actions = []
|
|
130
|
+
for eid in edge_uuids:
|
|
131
|
+
actions.append(
|
|
132
|
+
{'delete': {'_index': ENTITY_EDGE_INDEX_NAME, '_id': eid}}
|
|
133
|
+
)
|
|
134
|
+
|
|
135
|
+
await driver.aoss_client.bulk(body=actions)
|
|
136
|
+
|
|
104
137
|
case GraphProvider.KUZU:
|
|
105
138
|
for label in ['Episodic', 'Community']:
|
|
106
139
|
await driver.execute_query(
|
|
@@ -162,6 +195,32 @@ class Node(BaseModel, ABC):
|
|
|
162
195
|
group_id=group_id,
|
|
163
196
|
batch_size=batch_size,
|
|
164
197
|
)
|
|
198
|
+
|
|
199
|
+
if driver.aoss_client:
|
|
200
|
+
await driver.aoss_client.delete_by_query(
|
|
201
|
+
index=EPISODE_INDEX_NAME,
|
|
202
|
+
body={'query': {'term': {'group_id': group_id}}},
|
|
203
|
+
params={'routing': group_id},
|
|
204
|
+
)
|
|
205
|
+
|
|
206
|
+
await driver.aoss_client.delete_by_query(
|
|
207
|
+
index=ENTITY_INDEX_NAME,
|
|
208
|
+
body={'query': {'term': {'group_id': group_id}}},
|
|
209
|
+
params={'routing': group_id},
|
|
210
|
+
)
|
|
211
|
+
|
|
212
|
+
await driver.aoss_client.delete_by_query(
|
|
213
|
+
index=COMMUNITY_INDEX_NAME,
|
|
214
|
+
body={'query': {'term': {'group_id': group_id}}},
|
|
215
|
+
params={'routing': group_id},
|
|
216
|
+
)
|
|
217
|
+
|
|
218
|
+
await driver.aoss_client.delete_by_query(
|
|
219
|
+
index=ENTITY_EDGE_INDEX_NAME,
|
|
220
|
+
body={'query': {'term': {'group_id': group_id}}},
|
|
221
|
+
params={'routing': group_id},
|
|
222
|
+
)
|
|
223
|
+
|
|
165
224
|
case GraphProvider.KUZU:
|
|
166
225
|
for label in ['Episodic', 'Community']:
|
|
167
226
|
await driver.execute_query(
|
|
@@ -240,6 +299,23 @@ class Node(BaseModel, ABC):
|
|
|
240
299
|
)
|
|
241
300
|
case _: # Neo4J, Neptune
|
|
242
301
|
async with driver.session() as session:
|
|
302
|
+
# Collect all edge UUIDs before deleting nodes
|
|
303
|
+
result = await session.run(
|
|
304
|
+
"""
|
|
305
|
+
MATCH (n:Entity|Episodic|Community)
|
|
306
|
+
WHERE n.uuid IN $uuids
|
|
307
|
+
MATCH (n)-[r]-()
|
|
308
|
+
RETURN collect(r.uuid) AS edge_uuids
|
|
309
|
+
""",
|
|
310
|
+
uuids=uuids,
|
|
311
|
+
)
|
|
312
|
+
|
|
313
|
+
record = await result.single()
|
|
314
|
+
edge_uuids: list[str] = (
|
|
315
|
+
record['edge_uuids'] if record and record['edge_uuids'] else []
|
|
316
|
+
)
|
|
317
|
+
|
|
318
|
+
# Now delete the nodes in batches
|
|
243
319
|
await session.run(
|
|
244
320
|
"""
|
|
245
321
|
MATCH (n:Entity|Episodic|Community)
|
|
@@ -253,6 +329,20 @@ class Node(BaseModel, ABC):
|
|
|
253
329
|
batch_size=batch_size,
|
|
254
330
|
)
|
|
255
331
|
|
|
332
|
+
if driver.aoss_client:
|
|
333
|
+
for index in (EPISODE_INDEX_NAME, ENTITY_INDEX_NAME, COMMUNITY_INDEX_NAME):
|
|
334
|
+
await driver.aoss_client.delete_by_query(
|
|
335
|
+
index=index,
|
|
336
|
+
body={'query': {'terms': {'uuid': uuids}}},
|
|
337
|
+
)
|
|
338
|
+
|
|
339
|
+
if edge_uuids:
|
|
340
|
+
actions = [
|
|
341
|
+
{'delete': {'_index': ENTITY_EDGE_INDEX_NAME, '_id': eid}}
|
|
342
|
+
for eid in edge_uuids
|
|
343
|
+
]
|
|
344
|
+
await driver.aoss_client.bulk(body=actions)
|
|
345
|
+
|
|
256
346
|
@classmethod
|
|
257
347
|
async def get_by_uuid(cls, driver: GraphDriver, uuid: str): ...
|
|
258
348
|
|
|
@@ -286,7 +376,7 @@ class EpisodicNode(Node):
|
|
|
286
376
|
}
|
|
287
377
|
|
|
288
378
|
if driver.aoss_client:
|
|
289
|
-
driver.save_to_aoss( # pyright: ignore reportAttributeAccessIssue
|
|
379
|
+
await driver.save_to_aoss( # pyright: ignore reportAttributeAccessIssue
|
|
290
380
|
'episodes',
|
|
291
381
|
[episode_args],
|
|
292
382
|
)
|
|
@@ -426,13 +516,13 @@ class EntityNode(Node):
|
|
|
426
516
|
RETURN [x IN split(n.name_embedding, ",") | toFloat(x)] as name_embedding
|
|
427
517
|
"""
|
|
428
518
|
elif driver.aoss_client:
|
|
429
|
-
resp = driver.aoss_client.search(
|
|
519
|
+
resp = await driver.aoss_client.search(
|
|
430
520
|
body={
|
|
431
521
|
'query': {'multi_match': {'query': self.uuid, 'fields': ['uuid']}},
|
|
432
522
|
'size': 1,
|
|
433
523
|
},
|
|
434
|
-
index=
|
|
435
|
-
routing
|
|
524
|
+
index=ENTITY_INDEX_NAME,
|
|
525
|
+
params={'routing': self.group_id},
|
|
436
526
|
)
|
|
437
527
|
|
|
438
528
|
if resp['hits']['hits']:
|
|
@@ -479,7 +569,7 @@ class EntityNode(Node):
|
|
|
479
569
|
labels = ':'.join(self.labels + ['Entity'])
|
|
480
570
|
|
|
481
571
|
if driver.aoss_client:
|
|
482
|
-
driver.save_to_aoss(
|
|
572
|
+
await driver.save_to_aoss(ENTITY_INDEX_NAME, [entity_data]) # pyright: ignore reportAttributeAccessIssue
|
|
483
573
|
|
|
484
574
|
result = await driver.execute_query(
|
|
485
575
|
get_entity_node_save_query(driver.provider, labels, bool(driver.aoss_client)),
|
|
@@ -577,7 +667,7 @@ class CommunityNode(Node):
|
|
|
577
667
|
|
|
578
668
|
async def save(self, driver: GraphDriver):
|
|
579
669
|
if driver.provider == GraphProvider.NEPTUNE:
|
|
580
|
-
driver.save_to_aoss( # pyright: ignore reportAttributeAccessIssue
|
|
670
|
+
await driver.save_to_aoss( # pyright: ignore reportAttributeAccessIssue
|
|
581
671
|
'communities',
|
|
582
672
|
[{'name': self.name, 'uuid': self.uuid, 'group_id': self.group_id}],
|
|
583
673
|
)
|