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.
Files changed (188) hide show
  1. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/PKG-INFO +1 -1
  2. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/driver.py +97 -43
  3. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/neo4j_driver.py +18 -7
  4. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/neptune_driver.py +2 -2
  5. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/edges.py +47 -5
  6. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/graphiti.py +21 -5
  7. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/nodes.py +99 -9
  8. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_filters.py +8 -0
  9. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_utils.py +130 -106
  10. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/bulk_utils.py +30 -11
  11. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/edge_operations.py +39 -5
  12. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/graph_data_operations.py +5 -3
  13. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/pyproject.toml +1 -1
  14. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/signatures/version1/cla.json +16 -0
  15. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/uv.lock +1 -1
  16. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.env.example +0 -0
  17. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/ISSUE_TEMPLATE/bug_report.md +0 -0
  18. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/dependabot.yml +0 -0
  19. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/pull_request_template.md +0 -0
  20. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/secret_scanning.yml +0 -0
  21. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/ai-moderator.yml +0 -0
  22. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/cla.yml +0 -0
  23. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/claude-code-review.yml +0 -0
  24. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/claude.yml +0 -0
  25. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/codeql.yml +0 -0
  26. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/lint.yml +0 -0
  27. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/mcp-server-docker.yml +0 -0
  28. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/release-graphiti-core.yml +0 -0
  29. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/typecheck.yml +0 -0
  30. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.github/workflows/unit_tests.yml +0 -0
  31. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/.gitignore +0 -0
  32. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/CLAUDE.md +0 -0
  33. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/CODE_OF_CONDUCT.md +0 -0
  34. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/CONTRIBUTING.md +0 -0
  35. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/Dockerfile +0 -0
  36. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/LICENSE +0 -0
  37. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/Makefile +0 -0
  38. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/README.md +0 -0
  39. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/SECURITY.md +0 -0
  40. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/Zep-CLA.md +0 -0
  41. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/conftest.py +0 -0
  42. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/depot.json +0 -0
  43. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/docker-compose.test.yml +0 -0
  44. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/docker-compose.yml +0 -0
  45. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/ellipsis.yaml +0 -0
  46. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/data/manybirds_products.json +0 -0
  47. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/ecommerce/runner.ipynb +0 -0
  48. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/ecommerce/runner.py +0 -0
  49. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/langgraph-agent/agent.ipynb +0 -0
  50. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/langgraph-agent/tinybirds-jess.png +0 -0
  51. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/podcast/podcast_runner.py +0 -0
  52. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/podcast/podcast_transcript.txt +0 -0
  53. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/podcast/transcript_parser.py +0 -0
  54. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/README.md +0 -0
  55. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/quickstart_falkordb.py +0 -0
  56. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/quickstart_neo4j.py +0 -0
  57. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/quickstart_neptune.py +0 -0
  58. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/quickstart/requirements.txt +0 -0
  59. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/wizard_of_oz/parser.py +0 -0
  60. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/wizard_of_oz/runner.py +0 -0
  61. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/examples/wizard_of_oz/woo.txt +0 -0
  62. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/__init__.py +0 -0
  63. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/__init__.py +0 -0
  64. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/bge_reranker_client.py +0 -0
  65. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/client.py +0 -0
  66. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/gemini_reranker_client.py +0 -0
  67. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
  68. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/__init__.py +0 -0
  69. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/falkordb_driver.py +0 -0
  70. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/driver/kuzu_driver.py +0 -0
  71. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/__init__.py +0 -0
  72. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/azure_openai.py +0 -0
  73. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/client.py +0 -0
  74. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/gemini.py +0 -0
  75. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/openai.py +0 -0
  76. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/embedder/voyage.py +0 -0
  77. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/errors.py +0 -0
  78. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/graph_queries.py +0 -0
  79. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/graphiti_types.py +0 -0
  80. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/helpers.py +0 -0
  81. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/__init__.py +0 -0
  82. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/anthropic_client.py +0 -0
  83. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/azure_openai_client.py +0 -0
  84. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/client.py +0 -0
  85. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/config.py +0 -0
  86. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/errors.py +0 -0
  87. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/gemini_client.py +0 -0
  88. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/groq_client.py +0 -0
  89. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/openai_base_client.py +0 -0
  90. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/openai_client.py +0 -0
  91. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/openai_generic_client.py +0 -0
  92. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/llm_client/utils.py +0 -0
  93. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/migrations/__init__.py +0 -0
  94. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/__init__.py +0 -0
  95. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/edges/__init__.py +0 -0
  96. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/edges/edge_db_queries.py +0 -0
  97. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/nodes/__init__.py +0 -0
  98. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/models/nodes/node_db_queries.py +0 -0
  99. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/__init__.py +0 -0
  100. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/dedupe_edges.py +0 -0
  101. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/dedupe_nodes.py +0 -0
  102. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/eval.py +0 -0
  103. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/extract_edge_dates.py +0 -0
  104. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/extract_edges.py +0 -0
  105. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/extract_nodes.py +0 -0
  106. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/invalidate_edges.py +0 -0
  107. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/lib.py +0 -0
  108. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/models.py +0 -0
  109. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/prompt_helpers.py +0 -0
  110. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/prompts/summarize_nodes.py +0 -0
  111. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/py.typed +0 -0
  112. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/__init__.py +0 -0
  113. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search.py +0 -0
  114. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_config.py +0 -0
  115. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_config_recipes.py +0 -0
  116. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/search/search_helpers.py +0 -0
  117. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/telemetry/__init__.py +0 -0
  118. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/telemetry/telemetry.py +0 -0
  119. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/__init__.py +0 -0
  120. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/datetime_utils.py +0 -0
  121. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/__init__.py +0 -0
  122. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/community_operations.py +0 -0
  123. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/node_operations.py +0 -0
  124. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/temporal_operations.py +0 -0
  125. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/maintenance/utils.py +0 -0
  126. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/graphiti_core/utils/ontology_utils/entity_types_utils.py +0 -0
  127. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/arxiv-screenshot.png +0 -0
  128. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/graphiti-graph-intro.gif +0 -0
  129. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/graphiti-intro-slides-stock-2.gif +0 -0
  130. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/images/simple_graph.svg +0 -0
  131. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/.env.example +0 -0
  132. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/.python-version +0 -0
  133. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/Dockerfile +0 -0
  134. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/README.md +0 -0
  135. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/cursor_rules.md +0 -0
  136. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/docker-compose.yml +0 -0
  137. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/graphiti_mcp_server.py +0 -0
  138. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/mcp_config_sse_example.json +0 -0
  139. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/mcp_config_stdio_example.json +0 -0
  140. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/pyproject.toml +0 -0
  141. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/mcp_server/uv.lock +0 -0
  142. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/poetry.lock +0 -0
  143. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/py.typed +0 -0
  144. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/pytest.ini +0 -0
  145. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/.env.example +0 -0
  146. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/Makefile +0 -0
  147. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/README.md +0 -0
  148. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/__init__.py +0 -0
  149. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/config.py +0 -0
  150. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/__init__.py +0 -0
  151. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/common.py +0 -0
  152. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/ingest.py +0 -0
  153. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/dto/retrieve.py +0 -0
  154. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/main.py +0 -0
  155. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/routers/__init__.py +0 -0
  156. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/routers/ingest.py +0 -0
  157. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/routers/retrieve.py +0 -0
  158. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/graph_service/zep_graphiti.py +0 -0
  159. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/pyproject.toml +0 -0
  160. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/server/uv.lock +0 -0
  161. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/cross_encoder/test_bge_reranker_client.py +0 -0
  162. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/cross_encoder/test_gemini_reranker_client.py +0 -0
  163. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/driver/__init__.py +0 -0
  164. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/driver/test_falkordb_driver.py +0 -0
  165. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/embedder_fixtures.py +0 -0
  166. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/test_gemini.py +0 -0
  167. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/test_openai.py +0 -0
  168. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/embedder/test_voyage.py +0 -0
  169. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/data/longmemeval_data/README.md +0 -0
  170. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/data/longmemeval_data/longmemeval_oracle.json +0 -0
  171. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/eval_cli.py +0 -0
  172. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/eval_e2e_graph_building.py +0 -0
  173. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/pytest.ini +0 -0
  174. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/evals/utils.py +0 -0
  175. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/helpers_test.py +0 -0
  176. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_anthropic_client.py +0 -0
  177. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_anthropic_client_int.py +0 -0
  178. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_client.py +0 -0
  179. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_errors.py +0 -0
  180. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/llm_client/test_gemini_client.py +0 -0
  181. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_edge_int.py +0 -0
  182. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_entity_exclusion_int.py +0 -0
  183. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_graphiti_int.py +0 -0
  184. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_graphiti_mock.py +0 -0
  185. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/test_node_int.py +0 -0
  186. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/utils/maintenance/test_edge_operations.py +0 -0
  187. {graphiti_core-0.21.0rc1 → graphiti_core-0.21.0rc3}/tests/utils/maintenance/test_temporal_operations_int.py +0 -0
  188. {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.0rc1
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 OpenSearch, helpers
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': 'entities',
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': 'text'},
59
- 'created_at': {'type': 'date', 'format': "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
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
- 'dims': EMBEDDING_DIM,
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': 'communities',
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': 'text'},
92
+ 'group_id': {'type': 'keyword'},
84
93
  }
85
94
  }
86
95
  },
87
96
  },
88
97
  {
89
- 'index_name': 'episodes',
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': 'text'},
98
- 'created_at': {'type': 'date', 'format': "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
99
- 'valid_at': {'type': 'date', 'format': "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
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': 'entity_edges',
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': 'text'},
113
- 'created_at': {'type': 'date', 'format': "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
114
- 'valid_at': {'type': 'date', 'format': "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
115
- 'expired_at': {'type': 'date', 'format': "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
116
- 'invalid_at': {'type': 'date', 'format': "yyyy-MM-dd'T'HH:mm:ss.SSSZ"},
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
- 'dims': EMBEDDING_DIM,
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: OpenSearch | None # type: ignore
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(60)
230
+ await asyncio.sleep(1)
223
231
 
224
232
  async def delete_aoss_indices(self):
225
- for index in aoss_indices:
226
- index_name = index['index_name']
227
- client = self.aoss_client
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
- if not client:
230
- logger.warning('No OpenSearch client found')
231
- return
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
- if client.indices.exists(index=index_name):
234
- client.indices.delete(index=index_name)
258
+ except Exception as e:
259
+ logger.error(f"Error deleting indices for alias '{alias_name}': {e}")
235
260
 
236
- def save_to_aoss(self, name: str, data: list[dict]) -> int:
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
- '_routing': d.get('group_id'), # shard routing
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.bulk(client, to_index, stats_only=True)
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 OpenSearch, Urllib3AWSV4SignerAuth, Urllib3HttpConnection
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
- session = boto3.Session()
65
- self.aoss_client = OpenSearch( # type: ignore
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
- http_auth=Urllib3AWSV4SignerAuth( # type: ignore
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=Urllib3HttpConnection,
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='entity_edges',
265
- routing=self.group_id,
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('entity_edges', [edge_data]) # pyright: ignore reportAttributeAccessIssue
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
- related_edges = (await get_relevant_edges(self.driver, [updated_edge], SearchFilters()))[0]
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 get_edge_invalidation_candidates(self.driver, [updated_edge], SearchFilters())
1043
- )[0]
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 GraphDriver, GraphProvider
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:Entity|Episodic|Community {uuid: $uuid})
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='entities',
435
- routing=self.group_id,
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('entities', [entity_data]) # pyright: ignore reportAttributeAccessIssue
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
  )