graphiti-core 0.11.6rc6__tar.gz → 0.11.6rc9__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.

Potentially problematic release.


This version of graphiti-core might be problematic. Click here for more details.

Files changed (66) hide show
  1. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/PKG-INFO +1 -1
  2. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/helpers.py +8 -26
  3. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/search/search.py +22 -52
  4. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/search/search_utils.py +117 -13
  5. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/maintenance/node_operations.py +3 -3
  6. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/pyproject.toml +1 -1
  7. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/LICENSE +0 -0
  8. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/README.md +0 -0
  9. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/__init__.py +0 -0
  10. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/cross_encoder/__init__.py +0 -0
  11. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/cross_encoder/bge_reranker_client.py +0 -0
  12. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/cross_encoder/client.py +0 -0
  13. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/cross_encoder/openai_reranker_client.py +0 -0
  14. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/edges.py +0 -0
  15. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/embedder/__init__.py +0 -0
  16. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/embedder/client.py +0 -0
  17. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/embedder/gemini.py +0 -0
  18. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/embedder/openai.py +0 -0
  19. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/embedder/voyage.py +0 -0
  20. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/errors.py +0 -0
  21. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/graphiti.py +0 -0
  22. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/graphiti_types.py +0 -0
  23. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/__init__.py +0 -0
  24. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/anthropic_client.py +0 -0
  25. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/client.py +0 -0
  26. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/config.py +0 -0
  27. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/errors.py +0 -0
  28. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/gemini_client.py +0 -0
  29. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/groq_client.py +0 -0
  30. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/openai_client.py +0 -0
  31. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/openai_generic_client.py +0 -0
  32. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/llm_client/utils.py +0 -0
  33. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/models/__init__.py +0 -0
  34. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/models/edges/__init__.py +0 -0
  35. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/models/edges/edge_db_queries.py +0 -0
  36. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/models/nodes/__init__.py +0 -0
  37. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/models/nodes/node_db_queries.py +0 -0
  38. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/nodes.py +0 -0
  39. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/__init__.py +0 -0
  40. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/dedupe_edges.py +0 -0
  41. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/dedupe_nodes.py +0 -0
  42. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/eval.py +0 -0
  43. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/extract_edge_dates.py +0 -0
  44. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/extract_edges.py +0 -0
  45. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/extract_nodes.py +0 -0
  46. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/invalidate_edges.py +0 -0
  47. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/lib.py +0 -0
  48. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/models.py +0 -0
  49. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/prompt_helpers.py +0 -0
  50. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/prompts/summarize_nodes.py +0 -0
  51. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/py.typed +0 -0
  52. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/search/__init__.py +0 -0
  53. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/search/search_config.py +0 -0
  54. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/search/search_config_recipes.py +0 -0
  55. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/search/search_filters.py +0 -0
  56. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/search/search_helpers.py +0 -0
  57. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/__init__.py +0 -0
  58. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/bulk_utils.py +0 -0
  59. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/datetime_utils.py +0 -0
  60. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/maintenance/__init__.py +0 -0
  61. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/maintenance/community_operations.py +0 -0
  62. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/maintenance/edge_operations.py +0 -0
  63. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/maintenance/graph_data_operations.py +0 -0
  64. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/maintenance/temporal_operations.py +0 -0
  65. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/maintenance/utils.py +0 -0
  66. {graphiti_core-0.11.6rc6 → graphiti_core-0.11.6rc9}/graphiti_core/utils/ontology_utils/entity_types_utils.py +0 -0
@@ -1,6 +1,6 @@
1
1
  Metadata-Version: 2.3
2
2
  Name: graphiti-core
3
- Version: 0.11.6rc6
3
+ Version: 0.11.6rc9
4
4
  Summary: A temporal graph building library
5
5
  License: Apache-2.0
6
6
  Author: Paul Paliychuk
@@ -23,6 +23,7 @@ from typing import Any
23
23
  import numpy as np
24
24
  from dotenv import load_dotenv
25
25
  from neo4j import time as neo4j_time
26
+ from numpy._typing import NDArray
26
27
  from typing_extensions import LiteralString
27
28
 
28
29
  load_dotenv()
@@ -79,16 +80,10 @@ def lucene_sanitize(query: str) -> str:
79
80
  return sanitized
80
81
 
81
82
 
82
- def normalize_l2(embedding: list[float]):
83
+ def normalize_l2(embedding: list[float]) -> NDArray:
83
84
  embedding_array = np.array(embedding)
84
- if embedding_array.ndim == 1:
85
- norm = np.linalg.norm(embedding_array)
86
- if norm == 0:
87
- return [0.0] * len(embedding)
88
- return (embedding_array / norm).tolist()
89
- else:
90
- norm = np.linalg.norm(embedding_array, 2, axis=1, keepdims=True)
91
- return (np.where(norm == 0, embedding_array, embedding_array / norm)).tolist()
85
+ norm = np.linalg.norm(embedding_array, 2, axis=0, keepdims=True)
86
+ return np.where(norm == 0, embedding_array, embedding_array / norm)
92
87
 
93
88
 
94
89
  # Use this instead of asyncio.gather() to bound coroutines
@@ -98,21 +93,8 @@ async def semaphore_gather(
98
93
  ):
99
94
  semaphore = asyncio.Semaphore(max_coroutines)
100
95
 
101
- async def _wrap(coro: Coroutine) -> Any:
96
+ async def _wrap_coroutine(coroutine):
102
97
  async with semaphore:
103
- return await coro
104
-
105
- results = []
106
- batch = []
107
- for coroutine in coroutines:
108
- batch.append(_wrap(coroutine))
109
- # once we hit max_coroutines, gather and clear the batch
110
- if len(batch) >= max_coroutines:
111
- results.extend(await asyncio.gather(*batch))
112
- batch.clear()
113
-
114
- # gather any remaining coroutines in the final batch
115
- if batch:
116
- results.extend(await asyncio.gather(*batch))
117
-
118
- return results
98
+ return await coroutine
99
+
100
+ return await asyncio.gather(*(_wrap_coroutine(coroutine) for coroutine in coroutines))
@@ -50,6 +50,9 @@ from graphiti_core.search.search_utils import (
50
50
  edge_similarity_search,
51
51
  episode_fulltext_search,
52
52
  episode_mentions_reranker,
53
+ get_embeddings_for_communities,
54
+ get_embeddings_for_edges,
55
+ get_embeddings_for_nodes,
53
56
  maximal_marginal_relevance,
54
57
  node_bfs_search,
55
58
  node_distance_reranker,
@@ -209,26 +212,17 @@ async def edge_search(
209
212
 
210
213
  reranked_uuids = rrf(search_result_uuids, min_score=reranker_min_score)
211
214
  elif config.reranker == EdgeReranker.mmr:
212
- await semaphore_gather(
213
- *[edge.load_fact_embedding(driver) for result in search_results for edge in result]
215
+ search_result_uuids_and_vectors = await get_embeddings_for_edges(
216
+ driver, list(edge_uuid_map.values())
214
217
  )
215
- search_result_uuids_and_vectors = [
216
- (edge.uuid, edge.fact_embedding if edge.fact_embedding is not None else [0.0] * 1024)
217
- for result in search_results
218
- for edge in result
219
- ]
220
218
  reranked_uuids = maximal_marginal_relevance(
221
219
  query_vector,
222
220
  search_result_uuids_and_vectors,
223
221
  config.mmr_lambda,
222
+ reranker_min_score,
224
223
  )
225
224
  elif config.reranker == EdgeReranker.cross_encoder:
226
- search_result_uuids = [[edge.uuid for edge in result] for result in search_results]
227
-
228
- rrf_result_uuids = rrf(search_result_uuids, min_score=reranker_min_score)
229
- rrf_edges = [edge_uuid_map[uuid] for uuid in rrf_result_uuids][:limit]
230
-
231
- fact_to_uuid_map = {edge.fact: edge.uuid for edge in rrf_edges}
225
+ fact_to_uuid_map = {edge.fact: edge.uuid for edge in list(edge_uuid_map.values())[:limit]}
232
226
  reranked_facts = await cross_encoder.rank(query, list(fact_to_uuid_map.keys()))
233
227
  reranked_uuids = [
234
228
  fact_to_uuid_map[fact] for fact, score in reranked_facts if score >= reranker_min_score
@@ -311,30 +305,23 @@ async def node_search(
311
305
  if config.reranker == NodeReranker.rrf:
312
306
  reranked_uuids = rrf(search_result_uuids, min_score=reranker_min_score)
313
307
  elif config.reranker == NodeReranker.mmr:
314
- await semaphore_gather(
315
- *[node.load_name_embedding(driver) for result in search_results for node in result]
308
+ search_result_uuids_and_vectors = await get_embeddings_for_nodes(
309
+ driver, list(node_uuid_map.values())
316
310
  )
317
- search_result_uuids_and_vectors = [
318
- (node.uuid, node.name_embedding if node.name_embedding is not None else [0.0] * 1024)
319
- for result in search_results
320
- for node in result
321
- ]
311
+
322
312
  reranked_uuids = maximal_marginal_relevance(
323
313
  query_vector,
324
314
  search_result_uuids_and_vectors,
325
315
  config.mmr_lambda,
316
+ reranker_min_score,
326
317
  )
327
318
  elif config.reranker == NodeReranker.cross_encoder:
328
- # use rrf as a preliminary reranker
329
- rrf_result_uuids = rrf(search_result_uuids, min_score=reranker_min_score)
330
- rrf_results = [node_uuid_map[uuid] for uuid in rrf_result_uuids][:limit]
319
+ name_to_uuid_map = {node.name: node.uuid for node in list(node_uuid_map.values())}
331
320
 
332
- summary_to_uuid_map = {node.summary: node.uuid for node in rrf_results}
333
-
334
- reranked_summaries = await cross_encoder.rank(query, list(summary_to_uuid_map.keys()))
321
+ reranked_node_names = await cross_encoder.rank(query, list(name_to_uuid_map.keys()))
335
322
  reranked_uuids = [
336
- summary_to_uuid_map[fact]
337
- for fact, score in reranked_summaries
323
+ name_to_uuid_map[name]
324
+ for name, score in reranked_node_names
338
325
  if score >= reranker_min_score
339
326
  ]
340
327
  elif config.reranker == NodeReranker.episode_mentions:
@@ -437,35 +424,18 @@ async def community_search(
437
424
  if config.reranker == CommunityReranker.rrf:
438
425
  reranked_uuids = rrf(search_result_uuids, min_score=reranker_min_score)
439
426
  elif config.reranker == CommunityReranker.mmr:
440
- await semaphore_gather(
441
- *[
442
- community.load_name_embedding(driver)
443
- for result in search_results
444
- for community in result
445
- ]
427
+ search_result_uuids_and_vectors = await get_embeddings_for_communities(
428
+ driver, list(community_uuid_map.values())
446
429
  )
447
- search_result_uuids_and_vectors = [
448
- (
449
- community.uuid,
450
- community.name_embedding if community.name_embedding is not None else [0.0] * 1024,
451
- )
452
- for result in search_results
453
- for community in result
454
- ]
430
+
455
431
  reranked_uuids = maximal_marginal_relevance(
456
- query_vector,
457
- search_result_uuids_and_vectors,
458
- config.mmr_lambda,
432
+ query_vector, search_result_uuids_and_vectors, config.mmr_lambda, reranker_min_score
459
433
  )
460
434
  elif config.reranker == CommunityReranker.cross_encoder:
461
- summary_to_uuid_map = {
462
- node.summary: node.uuid for result in search_results for node in result
463
- }
464
- reranked_summaries = await cross_encoder.rank(query, list(summary_to_uuid_map.keys()))
435
+ name_to_uuid_map = {node.name: node.uuid for result in search_results for node in result}
436
+ reranked_nodes = await cross_encoder.rank(query, list(name_to_uuid_map.keys()))
465
437
  reranked_uuids = [
466
- summary_to_uuid_map[fact]
467
- for fact, score in reranked_summaries
468
- if score >= reranker_min_score
438
+ name_to_uuid_map[name] for name, score in reranked_nodes if score >= reranker_min_score
469
439
  ]
470
440
 
471
441
  reranked_communities = [community_uuid_map[uuid] for uuid in reranked_uuids]
@@ -21,6 +21,7 @@ from typing import Any
21
21
 
22
22
  import numpy as np
23
23
  from neo4j import AsyncDriver, Query
24
+ from numpy._typing import NDArray
24
25
  from typing_extensions import LiteralString
25
26
 
26
27
  from graphiti_core.edges import EntityEdge, get_entity_edge_from_record
@@ -336,10 +337,10 @@ async def node_fulltext_search(
336
337
 
337
338
  query = (
338
339
  """
339
- CALL db.index.fulltext.queryNodes("node_name_and_summary", $query, {limit: $limit})
340
- YIELD node AS n, score
341
- WHERE n:Entity
342
- """
340
+ CALL db.index.fulltext.queryNodes("node_name_and_summary", $query, {limit: $limit})
341
+ YIELD node AS n, score
342
+ WHERE n:Entity
343
+ """
343
344
  + filter_query
344
345
  + ENTITY_NODE_RETURN
345
346
  + """
@@ -899,6 +900,7 @@ async def node_distance_reranker(
899
900
  node_uuids=filtered_uuids,
900
901
  center_uuid=center_node_uuid,
901
902
  database_=DEFAULT_DATABASE,
903
+ routing_='r',
902
904
  )
903
905
 
904
906
  for result in path_results:
@@ -939,6 +941,7 @@ async def episode_mentions_reranker(
939
941
  query,
940
942
  node_uuids=sorted_uuids,
941
943
  database_=DEFAULT_DATABASE,
944
+ routing_='r',
942
945
  )
943
946
 
944
947
  for result in results:
@@ -952,15 +955,116 @@ async def episode_mentions_reranker(
952
955
 
953
956
  def maximal_marginal_relevance(
954
957
  query_vector: list[float],
955
- candidates: list[tuple[str, list[float]]],
958
+ candidates: dict[str, list[float]],
956
959
  mmr_lambda: float = DEFAULT_MMR_LAMBDA,
957
- ):
958
- candidates_with_mmr: list[tuple[str, float]] = []
959
- for candidate in candidates:
960
- max_sim = max([np.dot(normalize_l2(candidate[1]), normalize_l2(c[1])) for c in candidates])
961
- mmr = mmr_lambda * np.dot(candidate[1], query_vector) - (1 - mmr_lambda) * max_sim
962
- candidates_with_mmr.append((candidate[0], mmr))
960
+ min_score: float = -2.0,
961
+ ) -> list[str]:
962
+ start = time()
963
+ query_array = np.array(query_vector)
964
+ candidate_arrays: dict[str, NDArray] = {}
965
+ for uuid, embedding in candidates.items():
966
+ candidate_arrays[uuid] = normalize_l2(embedding)
967
+
968
+ uuids: list[str] = list(candidate_arrays.keys())
969
+
970
+ similarity_matrix = np.zeros((len(uuids), len(uuids)))
971
+
972
+ for i, uuid_1 in enumerate(uuids):
973
+ for j, uuid_2 in enumerate(uuids[:i]):
974
+ u = candidate_arrays[uuid_1]
975
+ v = candidate_arrays[uuid_2]
976
+ similarity = np.dot(u, v)
977
+
978
+ similarity_matrix[i, j] = similarity
979
+ similarity_matrix[j, i] = similarity
980
+
981
+ mmr_scores: dict[str, float] = {}
982
+ for i, uuid in enumerate(uuids):
983
+ max_sim = np.max(similarity_matrix[i, :])
984
+ mmr = mmr_lambda * np.dot(query_array, candidate_arrays[uuid]) + (mmr_lambda - 1) * max_sim
985
+ mmr_scores[uuid] = mmr
986
+
987
+ uuids.sort(reverse=True, key=lambda c: mmr_scores[c])
988
+
989
+ end = time()
990
+ logger.debug(f'Completed MMR reranking in {(end - start) * 1000} ms')
991
+
992
+ return [uuid for uuid in uuids if mmr_scores[uuid] >= min_score]
993
+
994
+
995
+ async def get_embeddings_for_nodes(
996
+ driver: AsyncDriver, nodes: list[EntityNode]
997
+ ) -> dict[str, list[float]]:
998
+ query: LiteralString = """MATCH (n:Entity)
999
+ WHERE n.uuid IN $node_uuids
1000
+ RETURN DISTINCT
1001
+ n.uuid AS uuid,
1002
+ n.name_embedding AS name_embedding
1003
+ """
1004
+
1005
+ results, _, _ = await driver.execute_query(
1006
+ query, node_uuids=[node.uuid for node in nodes], database_=DEFAULT_DATABASE, routing_='r'
1007
+ )
1008
+
1009
+ embeddings_dict: dict[str, list[float]] = {}
1010
+ for result in results:
1011
+ uuid: str = result.get('uuid')
1012
+ embedding: list[float] = result.get('name_embedding')
1013
+ if uuid is not None and embedding is not None:
1014
+ embeddings_dict[uuid] = embedding
963
1015
 
964
- candidates_with_mmr.sort(reverse=True, key=lambda c: c[1])
1016
+ return embeddings_dict
1017
+
1018
+
1019
+ async def get_embeddings_for_communities(
1020
+ driver: AsyncDriver, communities: list[CommunityNode]
1021
+ ) -> dict[str, list[float]]:
1022
+ query: LiteralString = """MATCH (c:Community)
1023
+ WHERE c.uuid IN $community_uuids
1024
+ RETURN DISTINCT
1025
+ c.uuid AS uuid,
1026
+ c.name_embedding AS name_embedding
1027
+ """
1028
+
1029
+ results, _, _ = await driver.execute_query(
1030
+ query,
1031
+ community_uuids=[community.uuid for community in communities],
1032
+ database_=DEFAULT_DATABASE,
1033
+ routing_='r',
1034
+ )
1035
+
1036
+ embeddings_dict: dict[str, list[float]] = {}
1037
+ for result in results:
1038
+ uuid: str = result.get('uuid')
1039
+ embedding: list[float] = result.get('name_embedding')
1040
+ if uuid is not None and embedding is not None:
1041
+ embeddings_dict[uuid] = embedding
1042
+
1043
+ return embeddings_dict
1044
+
1045
+
1046
+ async def get_embeddings_for_edges(
1047
+ driver: AsyncDriver, edges: list[EntityEdge]
1048
+ ) -> dict[str, list[float]]:
1049
+ query: LiteralString = """MATCH (n:Entity)-[e:RELATES_TO]-(m:Entity)
1050
+ WHERE e.uuid IN $edge_uuids
1051
+ RETURN DISTINCT
1052
+ e.uuid AS uuid,
1053
+ e.fact_embedding AS fact_embedding
1054
+ """
1055
+
1056
+ results, _, _ = await driver.execute_query(
1057
+ query,
1058
+ edge_uuids=[edge.uuid for edge in edges],
1059
+ database_=DEFAULT_DATABASE,
1060
+ routing_='r',
1061
+ )
1062
+
1063
+ embeddings_dict: dict[str, list[float]] = {}
1064
+ for result in results:
1065
+ uuid: str = result.get('uuid')
1066
+ embedding: list[float] = result.get('fact_embedding')
1067
+ if uuid is not None and embedding is not None:
1068
+ embeddings_dict[uuid] = embedding
965
1069
 
966
- return list(set([candidate[0] for candidate in candidates_with_mmr]))
1070
+ return embeddings_dict
@@ -18,6 +18,7 @@ import logging
18
18
  from contextlib import suppress
19
19
  from time import time
20
20
  from typing import Any
21
+ from uuid import uuid4
21
22
 
22
23
  import pydantic
23
24
  from pydantic import BaseModel, Field
@@ -395,7 +396,8 @@ async def extract_attributes_from_node(
395
396
  Field(description=field_info.description),
396
397
  )
397
398
 
398
- entity_attributes_model = pydantic.create_model('EntityAttributes', **attributes_definitions)
399
+ unique_model_name = f'EntityAttributes_{uuid4().hex}'
400
+ entity_attributes_model = pydantic.create_model(unique_model_name, **attributes_definitions)
399
401
 
400
402
  summary_context: dict[str, Any] = {
401
403
  'node': node_context,
@@ -411,12 +413,10 @@ async def extract_attributes_from_node(
411
413
  )
412
414
 
413
415
  node.summary = llm_response.get('summary', node.summary)
414
- node.name = llm_response.get('name', node.name)
415
416
  node_attributes = {key: value for key, value in llm_response.items()}
416
417
 
417
418
  with suppress(KeyError):
418
419
  del node_attributes['summary']
419
- del node_attributes['name']
420
420
 
421
421
  node.attributes.update(node_attributes)
422
422
 
@@ -1,7 +1,7 @@
1
1
  [project]
2
2
  name = "graphiti-core"
3
3
  description = "A temporal graph building library"
4
- version = "0.11.6pre6"
4
+ version = "0.11.6pre9"
5
5
  authors = [
6
6
  { "name" = "Paul Paliychuk", "email" = "paul@getzep.com" },
7
7
  { "name" = "Preston Rasmussen", "email" = "preston@getzep.com" },